commit 21a5a4aef01bed3583c1b136e17613934351568d Author: NOTPIES Date: Sun Mar 1 20:29:13 2026 -0300 Initial commit - LCEMP source code (assets excluded) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23e59a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,179 @@ +# =========================================== +# Build outputs +# =========================================== +x64/ +Minecraft.Client/x64/ +Minecraft.World/x64_Debug/ +Minecraft.World/x64_Release/ +ipch/ + +# =========================================== +# Visual Studio files +# =========================================== +*.sdf +*.opensdf +*.suo +*.user +*.vspscc +*.pdb +*.obj +*.pch +*.lib +*.exe +*.dll +*.tlog +*.lastbuildstate +*.log +*.idb +*.ilk +*.exp + +# =========================================== +# Archives & packaged binaries +# =========================================== +*.zip + +# =========================================== +# Copyrighted game assets (Mojang/Microsoft) +# Users must supply these from a legal copy. +# =========================================== + +# Music (Binka audio - all platforms) +Minecraft.Client/music/ +*.binka + +# Common Media - UI (SWF), Graphics (PNG), Sound (WAV), Fonts +Minecraft.Client/Common/Media/ +# Game resource textures, art, audio, etc. +Minecraft.Client/Common/res/ +Minecraft.Client/Common/DummyTexturePack/ + +# Platform-specific media directories +Minecraft.Client/DurangoMedia/ +Minecraft.Client/OrbisMedia/ +Minecraft.Client/PS3Media/ +Minecraft.Client/PSVitaMedia/ +Minecraft.Client/Windows64Media/ + +# Console package / system data +Minecraft.Client/PS3_GAME/ +Minecraft.Client/PS4_GAME/ +Minecraft.Client/sce_sys/ +Minecraft.Client/TROPDIR/ + +# Save data directories +Minecraft.Client/Windows64/GameHDD/ + +# Thumbnail / test images +*.png +# But allow source code PNG references to be noted +!Minecraft.Client/Common/Media/Graphics/.gitkeep + +# SWF UI files (Flash-based UI assets) +*.swf + +# Arc archives (packed texture/media bundles) +*.arc + +# Miles Sound System redistributables +Minecraft.Client/redist64/ +*.asi +*.flt + +# =========================================== +# Third-party / proprietary middleware +# =========================================== + +# 4J Studios proprietary libraries +**/4JLibs/ + +# Miles Sound System (RAD Game Tools) +**/Miles/ + +# Iggy / Scaleform UI middleware (RAD Game Tools) +**/Iggy/ + +# Sentient middleware +**/Sentient/ + +# =========================================== +# Console-specific SDK / platform files +# =========================================== + +# Xbox SPU tasks +**/SPU_Tasks/ + +# PS3 Edge libraries +Minecraft.Client/PS3/Edge/ +Minecraft.Client/PS3/DATA/ +Minecraft.Client/PS3/Media/ +Minecraft.Client/PS3/Passphrase/ + +# Durango (Xbox One) extras +Minecraft.Client/Durango/DLCImages/ +Minecraft.Client/Durango/Layout/ +Minecraft.Client/Durango/Sound/ +Minecraft.Client/Durango/CU/ + +# Orbis (PS4) extras +Minecraft.Client/Orbis/DLCImages/ +Minecraft.Client/Orbis/GameConfig/ + +# PSVita extras +Minecraft.Client/PSVita/app/ +Minecraft.Client/PSVita/Builds/ + +# Xbox (360) extras +Minecraft.Client/Xbox/Audio/ +Minecraft.Client/Xbox/Font/ +Minecraft.Client/Xbox/kinect/ +Minecraft.Client/Xbox/loc/ +Minecraft.Client/Xbox/ContentPackageBuild/ +Minecraft.Client/Xbox/ReleaseBuild/ +Minecraft.Client/Xbox/SubmissionBuild/ +Minecraft.Client/Xbox/TMSFiles/ +Minecraft.Client/Xbox/Cheats/ +Minecraft.Client/Xbox/Docs/ +Minecraft.Client/Xbox/Title Update/ + +# =========================================== +# Platform DLC content directories +# =========================================== +**/DLC/ + +# =========================================== +# Misc binary/compiled assets +# =========================================== +*.msscmp +*.cd +*.xap +*.bin +*.sfo +*.at9 +*.sig +*.dat +*.ico +*.rc +*.jpg +*.docx +*.xlsx +*.rtf +*.spa +*.winmd +*.alignmentchunk +*.trp +*.gameconfig + +# =========================================== +# Third-party libraries (boost, DirectX, etc.) +# =========================================== +Minecraft.Client/PS3/PS3Extras/boost_*/ +Minecraft.Client/PS3/PS3Extras/DirectX/ +Minecraft.Client/PS3/PS3Extras/HeapInspector/ + +# Static libraries and compiled packages +*.a +*.cmp + +# Sony remote storage libs +Minecraft.Client/Common/Network/Sony/ diff --git a/Minecraft.Client/AbstractContainerScreen.cpp b/Minecraft.Client/AbstractContainerScreen.cpp new file mode 100644 index 0000000..2b3e083 --- /dev/null +++ b/Minecraft.Client/AbstractContainerScreen.cpp @@ -0,0 +1,235 @@ +#include "stdafx.h" +#include "AbstractContainerScreen.h" +#include "ItemRenderer.h" +#include "MultiplayerLocalPlayer.h" +#include "Lighting.h" +#include "GameMode.h" +#include "KeyMapping.h" +#include "Options.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" + +ItemRenderer *AbstractContainerScreen::itemRenderer = new ItemRenderer(); + +AbstractContainerScreen::AbstractContainerScreen(AbstractContainerMenu *menu) +{ + // 4J - added initialisers + imageWidth = 176; + imageHeight = 166; + + this->menu = menu; +} + +void AbstractContainerScreen::init() +{ + Screen::init(); + minecraft->player->containerMenu = menu; +// leftPos = (width - imageWidth) / 2; +// topPos = (height - imageHeight) / 2; + +} + +void AbstractContainerScreen::render(int xm, int ym, float a) +{ + // 4J Stu - Not used +#if 0 + renderBackground(); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + renderBg(a); + + glPushMatrix(); + glRotatef(120, 1, 0, 0); + Lighting::turnOn(); + glPopMatrix(); + + glPushMatrix(); + glTranslatef((float)xo, (float)yo, 0); + + glColor4f(1, 1, 1, 1); + glEnable(GL_RESCALE_NORMAL); + + Slot *hoveredSlot = NULL; + + AUTO_VAR(itEnd, menu->slots->end()); + for (AUTO_VAR(it, menu->slots->begin()); it != itEnd; it++) + { + Slot *slot = *it; //menu->slots->at(i); + + renderSlot(slot); + + if (isHovering(slot, xm, ym)) + { + hoveredSlot = slot; + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + int x = slot->x; + int y = slot->y; + fillGradient(x, y, x + 16, y + 16, 0x80ffffff, 0x80ffffff); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + } + } + + shared_ptr inventory = minecraft->player->inventory; + if (inventory->getCarried() != NULL) + { + glTranslatef(0, 0, 32); + // Slot old = carriedSlot; + // carriedSlot = null; + itemRenderer->renderGuiItem(font, minecraft->textures, inventory->getCarried(), xm - xo - 8, ym - yo - 8); + itemRenderer->renderGuiItemDecorations(font, minecraft->textures, inventory->getCarried(), xm - xo - 8, ym - yo - 8); + // carriedSlot = old; + } + glDisable(GL_RESCALE_NORMAL); + Lighting::turnOff(); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + renderLabels(); + + if (inventory->getCarried() == NULL && hoveredSlot != NULL && hoveredSlot->hasItem()) + { + + wstring elementName = trimString(Language::getInstance()->getElementName(hoveredSlot->getItem()->getDescriptionId())); + + if (elementName.length() > 0) + { + int x = xm - xo + 12; + int y = ym - yo - 12; + int width = font->width(elementName); + fillGradient(x - 3, y - 3, x + width + 3, y + 8 + 3, 0xc0000000, 0xc0000000); + + font->drawShadow(elementName, x, y, 0xffffffff); + } + + } + + glPopMatrix(); + + Screen::render(xm, ym, a); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); +#endif +} + +void AbstractContainerScreen::renderLabels() +{ +} + +void AbstractContainerScreen::renderSlot(Slot *slot) +{ + // 4J Unused +#if 0 + int x = slot->x; + int y = slot->y; + shared_ptr item = slot->getItem(); + + if (item == NULL) + { + int icon = slot->getNoItemIcon(); + if (icon >= 0) + { + glDisable(GL_LIGHTING); + minecraft->textures->bind(minecraft->textures->loadTexture(TN_GUI_ITEMS));//L"/gui/items.png")); + blit(x, y, icon % 16 * 16, icon / 16 * 16, 16, 16); + glEnable(GL_LIGHTING); + return; + } + } + + itemRenderer->renderGuiItem(font, minecraft->textures, item, x, y); + itemRenderer->renderGuiItemDecorations(font, minecraft->textures, item, x, y); +#endif +} + +Slot *AbstractContainerScreen::findSlot(int x, int y) +{ + AUTO_VAR(itEnd, menu->slots->end()); + for (AUTO_VAR(it, menu->slots->begin()); it != itEnd; it++) + { + Slot *slot = *it; //menu->slots->at(i); + if (isHovering(slot, x, y)) return slot; + } + return NULL; +} + +bool AbstractContainerScreen::isHovering(Slot *slot, int xm, int ym) +{ + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + xm -= xo; + ym -= yo; + + return xm >= slot->x - 1 && xm < slot->x + 16 + 1 && ym >= slot->y - 1 && ym < slot->y + 16 + 1; + +} + +void AbstractContainerScreen::mouseClicked(int x, int y, int buttonNum) +{ + Screen::mouseClicked(x, y, buttonNum); + if (buttonNum == 0 || buttonNum == 1) + { + 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) + { + bool quickKey = slotId != AbstractContainerMenu::CLICKED_OUTSIDE && (Keyboard::isKeyDown(Keyboard::KEY_LSHIFT) || Keyboard::isKeyDown(Keyboard::KEY_RSHIFT)); + minecraft->gameMode->handleInventoryMouseClick(menu->containerId, slotId, buttonNum, quickKey, minecraft->player); + } + } + +} + +void AbstractContainerScreen::mouseReleased(int x, int y, int buttonNum) +{ + if (buttonNum == 0) + { + } +} + +void AbstractContainerScreen::keyPressed(wchar_t eventCharacter, int eventKey) +{ + if (eventKey == Keyboard::KEY_ESCAPE || eventKey == minecraft->options->keyBuild->key) + { + minecraft->player->closeContainer(); + } +} + +void AbstractContainerScreen::removed() +{ + if (minecraft->player == NULL) return; +} + +void AbstractContainerScreen::slotsChanged(shared_ptr container) +{ +} + +bool AbstractContainerScreen::isPauseScreen() +{ + return false; +} + +void AbstractContainerScreen::tick() +{ + Screen::tick(); + if (!minecraft->player->isAlive() || minecraft->player->removed) minecraft->player->closeContainer(); + +} \ No newline at end of file diff --git a/Minecraft.Client/AbstractContainerScreen.h b/Minecraft.Client/AbstractContainerScreen.h new file mode 100644 index 0000000..2ba9cdc --- /dev/null +++ b/Minecraft.Client/AbstractContainerScreen.h @@ -0,0 +1,38 @@ +#pragma once +#include "Screen.h" +class ItemRenderer; +class AbstractContainerMenu; +class Slot; +class Container; + +class AbstractContainerScreen : public Screen +{ +private: + static ItemRenderer *itemRenderer; +protected: + int imageWidth; + int imageHeight; + //int leftPos, topPos; +public: + AbstractContainerMenu *menu; + + AbstractContainerScreen(AbstractContainerMenu *menu); + virtual void init(); + virtual void render(int xm, int ym, float a); +protected: + virtual void renderLabels(); + virtual void renderBg(float a) = 0; +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); + virtual void keyPressed(wchar_t eventCharacter, int eventKey); +public: + virtual void removed(); + virtual void slotsChanged(shared_ptr container); + virtual bool isPauseScreen(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/AbstractTexturePack.cpp b/Minecraft.Client/AbstractTexturePack.cpp new file mode 100644 index 0000000..a4eb7f0 --- /dev/null +++ b/Minecraft.Client/AbstractTexturePack.cpp @@ -0,0 +1,399 @@ +#include "stdafx.h" +#include "Textures.h" +#include "AbstractTexturePack.h" +#include "..\Minecraft.World\InputOutputStream.h" +#include "..\Minecraft.World\StringHelpers.h" + +AbstractTexturePack::AbstractTexturePack(DWORD id, File *file, const wstring &name, TexturePack *fallback) : id(id), name(name) +{ + // 4J init + textureId = -1; + m_colourTable = NULL; + + + this->file = file; + this->fallback = fallback; + + m_iconData = NULL; + m_iconSize = 0; + + m_comparisonData = NULL; + m_comparisonSize = 0; + + // 4J Stu - These calls need to be in the most derived version of the class + //loadIcon(); + //loadDescription(); +} + +wstring AbstractTexturePack::trim(wstring line) +{ + if (!line.empty() && line.length() > 34) + { + line = line.substr(0, 34); + } + return line; +} + +void AbstractTexturePack::loadIcon() +{ +#ifdef _XBOX + // 4J Stu - Temporary only + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/TexturePackIcon.png"); + + UINT size = 0; + HRESULT hr = XuiResourceLoadAllNoLoc(szResourceLocator, &m_iconData, &size); + m_iconSize = size; +#endif +} + +void AbstractTexturePack::loadComparison() +{ +#ifdef _XBOX + // 4J Stu - Temporary only + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/DefaultPack_Comparison.png"); + + UINT size = 0; + HRESULT hr = XuiResourceLoadAllNoLoc(szResourceLocator, &m_comparisonData, &size); + m_comparisonSize = size; +#endif +} + +void AbstractTexturePack::loadDescription() +{ + // 4J Unused currently +#if 0 + InputStream *inputStream = NULL; + BufferedReader *br = NULL; + //try { + inputStream = getResourceImplementation(L"/pack.txt"); + br = new BufferedReader(new InputStreamReader(inputStream)); + desc1 = trim(br->readLine()); + desc2 = trim(br->readLine()); + //} catch (IOException ignored) { + //} finally { + // TODO [EB]: use IOUtils.closeSilently() + // try { + if (br != NULL) + { + br->close(); + delete br; + } + if (inputStream != NULL) + { + inputStream->close(); + delete inputStream; + } + // } catch (IOException ignored) { + // } + //} +#endif +} + +void AbstractTexturePack::loadName() +{ +} + +InputStream *AbstractTexturePack::getResource(const wstring &name, bool allowFallback) //throws IOException +{ + app.DebugPrintf("texture - %ls\n",name.c_str()); + InputStream *is = getResourceImplementation(name); + if (is == NULL && fallback != NULL && allowFallback) + { + is = fallback->getResource(name, true); + } + + return is; +} + +// 4J Currently removed due to override in TexturePack class +//InputStream *AbstractTexturePack::getResource(const wstring &name) //throws IOException +//{ +// return getResource(name, true); +//} + +void AbstractTexturePack::unload(Textures *textures) +{ + if (iconImage != NULL && textureId != -1) + { + textures->releaseTexture(textureId); + } +} + +void AbstractTexturePack::load(Textures *textures) +{ + if (iconImage != NULL) + { + if (textureId == -1) + { + textureId = textures->getTexture(iconImage); + } + glBindTexture(GL_TEXTURE_2D, textureId); + textures->clearLastBoundId(); + } + else + { + // 4J Stu - Don't do this + //textures->bindTexture(L"/gui/unknown_pack.png"); + } +} + +bool AbstractTexturePack::hasFile(const wstring &name, bool allowFallback) +{ + bool hasFile = this->hasFile(name); + + return !hasFile && (allowFallback && fallback != NULL) ? fallback->hasFile(name, allowFallback) : hasFile; +} + +DWORD AbstractTexturePack::getId() +{ + return id; +} + +wstring AbstractTexturePack::getName() +{ + return texname; +} + +wstring AbstractTexturePack::getWorldName() +{ + return m_wsWorldName; +} + +wstring AbstractTexturePack::getDesc1() +{ + return desc1; +} + +wstring AbstractTexturePack::getDesc2() +{ + return desc2; +} + +wstring AbstractTexturePack::getAnimationString(const wstring &textureName, const wstring &path, bool allowFallback) +{ + return getAnimationString(textureName, path); +} + +wstring AbstractTexturePack::getAnimationString(const wstring &textureName, const wstring &path) +{ + wstring animationDefinitionFile = textureName + L".txt"; + + bool requiresFallback = !hasFile(L"\\" + textureName + L".png", false); + + InputStream *fileStream = getResource(L"\\" + path + animationDefinitionFile, requiresFallback); + + //Minecraft::getInstance()->getLogger().info("Found animation info for: " + animationDefinitionFile); +#ifndef _CONTENT_PACKAGE + wprintf(L"Found animation info for: %ls\n", animationDefinitionFile.c_str() ); +#endif + InputStreamReader isr(fileStream); + BufferedReader br(&isr); + + wstring result = L""; + + wstring line = br.readLine(); + while (!line.empty()) + { + line = trimString(line); + if (line.length() > 0) + { + result.append(L","); + result.append(line); + } + line = br.readLine(); + } + delete fileStream; + + return result; +} + +BufferedImage *AbstractTexturePack::getImageResource(const wstring& File, bool filenameHasExtension /*= false*/, bool bTitleUpdateTexture /*=false*/, const wstring &drive /*=L""*/) +{ + const char *pchTexture=wstringtofilename(File); + app.DebugPrintf("AbstractTexturePack::getImageResource - %s, drive is %s\n",pchTexture, wstringtofilename(drive)); + + return new BufferedImage(TexturePack::getResource(L"/" + File),filenameHasExtension,bTitleUpdateTexture,drive); +} + +void AbstractTexturePack::loadDefaultUI() +{ +#ifdef _XBOX + // load from the .xzp file + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + // Load new skin + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + swprintf(szResourceLocator, LOCATOR_SIZE,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/skin_Minecraft.xur"); + + XuiFreeVisuals(L""); + app.LoadSkin(szResourceLocator,NULL);//L"TexturePack"); + //CXuiSceneBase::GetInstance()->SetVisualPrefix(L"TexturePack"); + CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); +#else + ui.ReloadSkin(); +#endif +} + +void AbstractTexturePack::loadColourTable() +{ + loadDefaultColourTable(); + loadDefaultHTMLColourTable(); +} + +void AbstractTexturePack::loadDefaultColourTable() +{ + // Load the file + File coloursFile(AbstractTexturePack::getPath(true).append(L"res/colours.col")); + + if(coloursFile.exists()) + { + DWORD dwLength = coloursFile.length(); + byteArray data(dwLength); + + FileInputStream fis(coloursFile); + fis.read(data,0,dwLength); + fis.close(); + if(m_colourTable != NULL) delete m_colourTable; + m_colourTable = new ColourTable(data.data, dwLength); + + delete [] data.data; + } + else + { + app.DebugPrintf("Failed to load the default colours table\n"); + app.FatalLoadError(); + } +} + +void AbstractTexturePack::loadDefaultHTMLColourTable() +{ +#ifdef _XBOX + // load from the .xzp file + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + // Try and load the HTMLColours.col based off the common XML first, before the deprecated xuiscene_colourtable + wsprintfW(szResourceLocator,L"section://%X,%s#%s",c_ModuleHandle,L"media", L"media/HTMLColours.col"); + BYTE *data; + UINT dataLength; + if(XuiResourceLoadAll(szResourceLocator, &data, &dataLength) == S_OK) + { + m_colourTable->loadColoursFromData(data,dataLength); + + XuiFree(data); + } + else + { + wsprintfW(szResourceLocator,L"section://%X,%s#%s",c_ModuleHandle,L"media", L"media/"); + HXUIOBJ hScene; + HRESULT hr = XuiSceneCreate(szResourceLocator,L"xuiscene_colourtable.xur", NULL, &hScene); + + if(HRESULT_SUCCEEDED(hr)) + { + loadHTMLColourTableFromXuiScene(hScene); + } + } +#else + if(app.hasArchiveFile(L"HTMLColours.col")) + { + byteArray textColours = app.getArchiveFile(L"HTMLColours.col"); + m_colourTable->loadColoursFromData(textColours.data,textColours.length); + + delete [] textColours.data; + } +#endif +} + +#ifdef _XBOX +void AbstractTexturePack::loadHTMLColourTableFromXuiScene(HXUIOBJ hObj) +{ + HXUIOBJ child; + HRESULT hr = XuiElementGetFirstChild(hObj, &child); + + while(HRESULT_SUCCEEDED(hr) && child != NULL) + { + LPCWSTR childName; + XuiElementGetId(child,&childName); + m_colourTable->setColour(childName,XuiTextElementGetText(child)); + + //eMinecraftTextColours colourIndex = eTextColor_NONE; + //for(int i = 0; i < (int)eTextColor_MAX; i++) + //{ + // if(wcscmp(HTMLColourTableElements[i],childName)==0) + // { + // colourIndex = (eMinecraftTextColours)i; + // break; + // } + //} + + //LPCWSTR stringValue = XuiTextElementGetText(child); + + //m_htmlColourTable[colourIndex] = XuiTextElementGetText(child); + + hr = XuiElementGetNext(child, &child); + } +} +#endif + +void AbstractTexturePack::loadUI() +{ + loadColourTable(); + +#ifdef _XBOX + CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); +#endif +} + +void AbstractTexturePack::unloadUI() +{ + // Do nothing +} + +wstring AbstractTexturePack::getXuiRootPath() +{ + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + // Load new skin + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + swprintf(szResourceLocator, LOCATOR_SIZE,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/"); + return szResourceLocator; +} + +PBYTE AbstractTexturePack::getPackIcon(DWORD &dwImageBytes) +{ + if(m_iconSize == 0 || m_iconData == NULL) loadIcon(); + dwImageBytes = m_iconSize; + return m_iconData; +} + +PBYTE AbstractTexturePack::getPackComparison(DWORD &dwImageBytes) +{ + if(m_comparisonSize == 0 || m_comparisonData == NULL) loadComparison(); + + dwImageBytes = m_comparisonSize; + return m_comparisonData; +} + +unsigned int AbstractTexturePack::getDLCParentPackId() +{ + return 0; +} + +unsigned char AbstractTexturePack::getDLCSubPackId() +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/AbstractTexturePack.h b/Minecraft.Client/AbstractTexturePack.h new file mode 100644 index 0000000..e6410c1 --- /dev/null +++ b/Minecraft.Client/AbstractTexturePack.h @@ -0,0 +1,93 @@ +#pragma once +using namespace std; + +#include "TexturePack.h" + +class BufferedImage; + +class AbstractTexturePack : public TexturePack +{ +private: + const DWORD id; + const wstring name; + +protected: + File *file; + wstring texname; + wstring m_wsWorldName; + + wstring desc1; + wstring desc2; + + PBYTE m_iconData; + DWORD m_iconSize; + + PBYTE m_comparisonData; + DWORD m_comparisonSize; + + TexturePack *fallback; + + ColourTable *m_colourTable; + +protected: + BufferedImage *iconImage; + +private: + int textureId; + +protected: + AbstractTexturePack(DWORD id, File *file, const wstring &name, TexturePack *fallback); + +private: + static wstring trim(wstring line); + +protected: + virtual void loadIcon(); + virtual void loadComparison(); + virtual void loadDescription(); + virtual void loadName(); + +public: + virtual InputStream *getResource(const wstring &name, bool allowFallback); //throws IOException + // 4J Removed do to current override in TexturePack class + //virtual InputStream *getResource(const wstring &name); //throws IOException + virtual DLCPack * getDLCPack() =0; + + +protected: + virtual InputStream *getResourceImplementation(const wstring &name) = 0; // throws IOException; +public: + virtual void unload(Textures *textures); + virtual void load(Textures *textures); + virtual bool hasFile(const wstring &name, bool allowFallback); + virtual bool hasFile(const wstring &name) = 0; + virtual DWORD getId(); + virtual wstring getName(); + virtual wstring getDesc1(); + virtual wstring getDesc2(); + virtual wstring getWorldName(); + + virtual wstring getAnimationString(const wstring &textureName, const wstring &path, bool allowFallback); + +protected: + virtual wstring getAnimationString(const wstring &textureName, const wstring &path); + void loadDefaultUI(); + void loadDefaultColourTable(); + void loadDefaultHTMLColourTable(); +#ifdef _XBOX + void loadHTMLColourTableFromXuiScene(HXUIOBJ hObj); +#endif + +public: + virtual BufferedImage *getImageResource(const wstring& File, bool filenameHasExtension = false, bool bTitleUpdateTexture=false, const wstring &drive =L""); + virtual void loadColourTable(); + virtual void loadUI(); + virtual void unloadUI(); + virtual wstring getXuiRootPath(); + virtual PBYTE getPackIcon(DWORD &dwImageBytes); + virtual PBYTE getPackComparison(DWORD &dwImageBytes); + virtual unsigned int getDLCParentPackId(); + virtual unsigned char getDLCSubPackId(); + virtual ColourTable *getColourTable() { return m_colourTable; } + virtual ArchiveFile *getArchiveFile() { return NULL; } +}; diff --git a/Minecraft.Client/AchievementPopup.cpp b/Minecraft.Client/AchievementPopup.cpp new file mode 100644 index 0000000..04f822a --- /dev/null +++ b/Minecraft.Client/AchievementPopup.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" +#include "AchievementPopup.h" +#include "ItemRenderer.h" +#include "Font.h" +#include "Textures.h" +#include "Lighting.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +#include "..\Minecraft.World\SharedConstants.h" + +AchievementPopup::AchievementPopup(Minecraft *mc) +{ + // 4J - added initialisers + width = 0; + height = 0; + ach = NULL; + startTime = 0; + isHelper = false; + + this->mc = mc; + ir = new ItemRenderer(); +} + +void AchievementPopup::popup(Achievement *ach) +{ + title = I18n::get(L"achievement.get"); + desc = ach->name; + startTime = System::currentTimeMillis(); + this->ach = ach; + isHelper = false; +} + +void AchievementPopup::permanent(Achievement *ach) +{ + title = ach->name; + desc = ach->getDescription(); + + startTime = System::currentTimeMillis() - 2500; + this->ach = ach; + isHelper = true; +} + +void AchievementPopup::prepareWindow() +{ + glViewport(0, 0, mc->width, mc->height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + this->width = mc->width; + this->height = mc->height; + + ScreenSizeCalculator ssc(mc->options, mc->width, mc->height); + width = ssc.getWidth(); + height = ssc.getHeight(); + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, (float)width, (float)height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + +} + +void AchievementPopup::render() +{ +// 4J Unused +#if 0 + if (Minecraft::warezTime > 0) + { + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + Lighting::turnOff(); + prepareWindow(); + + wstring title = L"Minecraft " + SharedConstants::VERSION_STRING + L" Unlicensed Copy :("; + wstring msg1 = L"(Or logged in from another location)"; + wstring msg2 = L"Purchase at minecraft.net"; + + mc->font->drawShadow(title, 2, 2 + 9 * 0, 0xffffff); + mc->font->drawShadow(msg1, 2, 2 + 9 * 1, 0xffffff); + mc->font->drawShadow(msg2, 2, 2 + 9 * 2, 0xffffff); + + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + } + if (ach == NULL || startTime == 0) return; + + double time = (System::currentTimeMillis() - startTime) / 3000.0; + if (isHelper) + { + } + else if (!isHelper && (time < 0 || time > 1)) + { + startTime = 0; + return; + } + + + prepareWindow(); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + + double yo = time * 2; + if (yo > 1) yo = 2 - yo; + yo = yo * 4; + yo = 1 - yo; + if (yo < 0) yo = 0; + yo = yo * yo; + yo = yo * yo; + + int xx = width - 160; + int yy = 0 - (int) (yo * 36); + int tex = mc->textures->loadTexture(L"/achievement/bg.png"); + glColor4f(1, 1, 1, 1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, tex); + glDisable(GL_LIGHTING); + + blit(xx, yy, 96, 202, 160, 32); + + if (isHelper) + { + mc->font->drawWordWrap(desc, xx + 30, yy + 7, 120, 0xffffffff); + } + else + { + mc->font->draw(title, xx + 30, yy + 7, 0xffffff00); + mc->font->draw(desc, xx + 30, yy + 18, 0xffffffff); + } + + glPushMatrix(); + glRotatef(180, 1, 0, 0); + Lighting::turnOn(); + glPopMatrix(); + glDisable(GL_LIGHTING); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glEnable(GL_LIGHTING); + ir->renderGuiItem(mc->font, mc->textures, ach->icon, xx + 8, yy + 8); + glDisable(GL_LIGHTING); + + glDepthMask(true); + glEnable(GL_DEPTH_TEST); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/AchievementPopup.h b/Minecraft.Client/AchievementPopup.h new file mode 100644 index 0000000..3085dc6 --- /dev/null +++ b/Minecraft.Client/AchievementPopup.h @@ -0,0 +1,28 @@ +#pragma once +#include "GuiComponent.h" +class Achievement; +class ItemRenderer; +using namespace std; + +class AchievementPopup : public GuiComponent +{ +private: + Minecraft *mc; + int width, height; + + wstring title; + wstring desc; + Achievement *ach; + __int64 startTime; + ItemRenderer *ir; + bool isHelper; + +public: + AchievementPopup(Minecraft *mc); + void popup(Achievement *ach); + void permanent(Achievement *ach); +private: + void prepareWindow(); +public: + void render(); +}; \ No newline at end of file diff --git a/Minecraft.Client/AchievementScreen.cpp b/Minecraft.Client/AchievementScreen.cpp new file mode 100644 index 0000000..26f2032 --- /dev/null +++ b/Minecraft.Client/AchievementScreen.cpp @@ -0,0 +1,426 @@ +#include "stdafx.h" +#include "AchievementScreen.h" +#include "SmallButton.h" +#include "Options.h" +#include "KeyMapping.h" +#include "Font.h" +#include "Lighting.h" +#include "Textures.h" +#include "StatsCounter.h" +#include "ItemRenderer.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\JavaMath.h" + + + +AchievementScreen::AchievementScreen(StatsCounter *statsCounter) +{ + // 4J - added initialisers + imageWidth = 256; + imageHeight = 202; + xLastScroll = 0; + yLastScroll = 0; + scrolling = 0; + + // 4J - TODO - investigate - these were static final ints before, but based on members of Achievements which + // aren't final Or actually initialised + xMin = Achievements::xMin * ACHIEVEMENT_COORD_SCALE - BIGMAP_WIDTH / 2; + yMin = Achievements::yMin * ACHIEVEMENT_COORD_SCALE - BIGMAP_WIDTH / 2; + xMax = Achievements::xMax * ACHIEVEMENT_COORD_SCALE - BIGMAP_HEIGHT / 2; + yMax = Achievements::yMax * ACHIEVEMENT_COORD_SCALE - BIGMAP_HEIGHT / 2; + + this->statsCounter = statsCounter; + int wBigMap = 141; + int hBigMap = 141; + + xScrollO = xScrollP = xScrollTarget = Achievements::openInventory->x * ACHIEVEMENT_COORD_SCALE - wBigMap / 2 - 12; + yScrollO = yScrollP = yScrollTarget = Achievements::openInventory->y * ACHIEVEMENT_COORD_SCALE - hBigMap / 2; + +} + +void AchievementScreen::init() +{ + buttons.clear(); +// buttons.add(new SmallButton(0, width / 2 - 80 - 24, height / 2 + 74, 110, 20, I18n.get("gui.achievements"))); + buttons.push_back(new SmallButton(1, width / 2 + 24, height / 2 + 74, 80, 20, I18n::get(L"gui.done"))); + +} + +void AchievementScreen::buttonClicked(Button *button) +{ + if (button->id == 1) + { + minecraft->setScreen(NULL); +// minecraft->grabMouse(); // 4J removed + } + Screen::buttonClicked(button); +} + +void AchievementScreen::keyPressed(char eventCharacter, int eventKey) +{ + if (eventKey == minecraft->options->keyBuild->key) + { + minecraft->setScreen(NULL); +// minecraft->grabMouse(); // 4J removed + } + else + { + Screen::keyPressed(eventCharacter, eventKey); + } +} + +void AchievementScreen::render(int mouseX, int mouseY, float a) +{ + if (Mouse::isButtonDown(0)) + { + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + int xBigMap = xo + 8; + int yBigMap = yo + 17; + + if (scrolling == 0 || scrolling == 1) + { + if (mouseX >= xBigMap && mouseX < xBigMap + BIGMAP_WIDTH && mouseY >= yBigMap && mouseY < yBigMap + BIGMAP_HEIGHT) + { + if (scrolling == 0) + { + scrolling = 1; + } + else + { + xScrollP -= mouseX - xLastScroll; + yScrollP -= mouseY - yLastScroll; + xScrollTarget = xScrollO = xScrollP; + yScrollTarget = yScrollO = yScrollP; + } + xLastScroll = mouseX; + yLastScroll = mouseY; + } + } + + if (xScrollTarget < xMin) xScrollTarget = xMin; + if (yScrollTarget < yMin) yScrollTarget = yMin; + if (xScrollTarget >= xMax) xScrollTarget = xMax - 1; + if (yScrollTarget >= yMax) yScrollTarget = yMax - 1; + } + else + { + scrolling = 0; + } + + renderBackground(); + + renderBg(mouseX, mouseY, a); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + renderLabels(); + + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + +} + +void AchievementScreen::tick() +{ + xScrollO = xScrollP; + yScrollO = yScrollP; + + double xd = (xScrollTarget - xScrollP); + double yd = (yScrollTarget - yScrollP); + if (xd * xd + yd * yd < 4) + { + xScrollP += xd; + yScrollP += yd; + } + else + { + xScrollP += xd * 0.85; + yScrollP += yd * 0.85; + } +} + +void AchievementScreen::renderLabels() +{ + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + font->draw(L"Achievements", xo + 15, yo + 5, 0x404040); + +// font.draw(xScrollP + ", " + yScrollP, xo + 5, yo + 5 + BIGMAP_HEIGHT + 18, 0x404040); +// font.drawWordWrap("Ride a pig off a cliff.", xo + 5, yo + 5 + BIGMAP_HEIGHT + 16, BIGMAP_WIDTH, 0x404040); + +} + +void AchievementScreen::renderBg(int xm, int ym, float a) +{ + // 4J Unused +#if 0 + int xScroll = Mth::floor(xScrollO + (xScrollP - xScrollO) * a); + int yScroll = Mth::floor(yScrollO + (yScrollP - yScrollO) * a); + + if (xScroll < xMin) xScroll = xMin; + if (yScroll < yMin) yScroll = yMin; + if (xScroll >= xMax) xScroll = xMax - 1; + if (yScroll >= yMax) yScroll = yMax - 1; + + + int terrainTex = minecraft->textures->loadTexture(L"/terrain.png"); + int tex = minecraft->textures->loadTexture(L"/achievement/bg.png"); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + int xBigMap = xo + BIGMAP_X; + int yBigMap = yo + BIGMAP_Y; + + blitOffset = 0; +// glDisable(GL_DEPTH_TEST); + glDepthFunc(GL_GEQUAL); + glPushMatrix(); + glTranslatef(0, 0, -200); + + { + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + minecraft->textures->bind(terrainTex); + + int leftTile = (xScroll + EDGE_VALUE_X) >> 4; + int topTile = (yScroll + EDGE_VALUE_Y) >> 4; + int xMod = (xScroll + EDGE_VALUE_X) % 16; + int yMod = (yScroll + EDGE_VALUE_Y) % 16; + + const int rockLevel = (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 4) / 10; + const int coalLevel = (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 7) / 10; + const int ironLevel = (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 9) / 10; + const int diamondLevel = (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 19) / 10; + const int bedrockLevel = (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 31) / 10; + + Random *random = new Random(); + + for (int tileY = 0; (tileY * 16) - yMod < BIGMAP_HEIGHT; tileY++) + { + + float amount = .6f - (float) (topTile + tileY) / (float) (Achievements::ACHIEVEMENT_HEIGHT_POSITION * 2 + 1) * .3f; + glColor4f(amount, amount, amount, 1); + + for (int tileX = 0; (tileX * 16) - xMod < BIGMAP_WIDTH; tileX++) + { + + random->setSeed(1234 + leftTile + tileX); + random->nextInt(); + int heightValue = random->nextInt(1 + topTile + tileY) + (topTile + tileY) / 2; + int tileType = Tile::sand->tex; + + if (heightValue > bedrockLevel || (topTile + tileY) == MAX_BG_TILE_Y) + { + tileType = Tile::unbreakable->tex; + } + else if (heightValue == diamondLevel) + { + if (random->nextInt(2) == 0) + { + tileType = Tile::diamondOre->tex; + } + else + { + tileType = Tile::redStoneOre->tex; + } + } + else if (heightValue == ironLevel) + { + tileType = Tile::ironOre->tex; + } + else if (heightValue == coalLevel) + { + tileType = Tile::coalOre->tex; + } + else if (heightValue > rockLevel) + { + tileType = Tile::rock->tex; + } + else if (heightValue > 0) + { + tileType = Tile::dirt->tex; + } + + this->blit(xBigMap + tileX * 16 - xMod, yBigMap + tileY * 16 - yMod, (tileType % 16) << 4, (tileType >> 4) << 4, 16, 16); + } + } + + } + glEnable(GL_DEPTH_TEST); + + + glDepthFunc(GL_LEQUAL); + + glDisable(GL_TEXTURE_2D); + + AUTO_VAR(itEnd, Achievements::achievements->end()); + for (AUTO_VAR(it, Achievements::achievements->begin()); it != itEnd; it++) + { + Achievement *ach = *it; //Achievements::achievements->at(i); + if (ach->requires == NULL) continue; + + int x1 = ach->x * ACHIEVEMENT_COORD_SCALE - (int) xScroll + 11 + xBigMap; + int y1 = ach->y * ACHIEVEMENT_COORD_SCALE - (int) yScroll + 11 + yBigMap; + + int x2 = ach->requires->x * ACHIEVEMENT_COORD_SCALE - (int) xScroll + 11 + xBigMap; + int y2 = ach->requires->y * ACHIEVEMENT_COORD_SCALE - (int) yScroll + 11 + yBigMap; + + int color = 0; + + bool taken = statsCounter->hasTaken(ach); + bool canTake = statsCounter->canTake(ach); + + int alph = (int) (sin(System::currentTimeMillis() % 600 / 600.0 * PI * 2) > 0.6 ? 255 : 130); + if (taken) color = 0xff707070; + else if (canTake) color = 0x00ff00 + (alph << 24); + else color = 0xff000000; + + hLine(x1, x2, y1, color); + vLine(x2, y1, y2, color); + } + + Achievement *hoveredAchievement = NULL; + ItemRenderer *ir = new ItemRenderer(); + + glPushMatrix(); + glRotatef(180, 1, 0, 0); + Lighting::turnOn(); + glPopMatrix(); + glDisable(GL_LIGHTING); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + itEnd = Achievements::achievements->end(); + for (AUTO_VAR(it, Achievements::achievements->begin()); it != itEnd; it++) + { + Achievement *ach = *it; //Achievements::achievements->at(i); + + int x = ach->x * ACHIEVEMENT_COORD_SCALE - (int) xScroll; + int y = ach->y * ACHIEVEMENT_COORD_SCALE - (int) yScroll; + + if (x >= -24 && y >= -24 && x <= BIGMAP_WIDTH && y <= BIGMAP_HEIGHT) + { + + if (statsCounter->hasTaken(ach)) + { + float br = 1.0f; + glColor4f(br, br, br, 1); + } + else if (statsCounter->canTake(ach)) + { + float br = (sin(System::currentTimeMillis() % 600 / 600.0 * PI * 2) < 0.6 ? 0.6f : 0.8f); + glColor4f(br, br, br, 1); + } + else + { + float br = 0.3f; + glColor4f(br, br, br, 1); + } + + minecraft->textures->bind(tex); + int xx = xBigMap + x; + int yy = yBigMap + y; + if (ach->isGolden()) + { + this->blit(xx - 2, yy - 2, 26, 202, 26, 26); + } + else + { + this->blit(xx - 2, yy - 2, 0, 202, 26, 26); + } + + if (!statsCounter->canTake(ach)) + { + float br = 0.1f; + glColor4f(br, br, br, 1); + ir->setColor = false; + } + glEnable(GL_LIGHTING); + glEnable(GL_CULL_FACE); + ir->renderGuiItem(minecraft->font, minecraft->textures, ach->icon, xx + 3, yy + 3); + glDisable(GL_LIGHTING); + if (!statsCounter->canTake(ach)) + { + ir->setColor = true; + } + glColor4f(1, 1, 1, 1); + + + if (xm >= xBigMap && ym >= yBigMap && xm < xBigMap + BIGMAP_WIDTH && ym < yBigMap + BIGMAP_HEIGHT && xm >= xx && xm <= xx + 22 && ym >= yy && ym <= yy + 22) { + hoveredAchievement = ach; + } + } + } + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + + glPopMatrix(); + + blitOffset = 0; + glDepthFunc(GL_LEQUAL); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + Screen::render(xm, ym, a); + + if (hoveredAchievement != NULL) + { + Achievement *ach = hoveredAchievement; + wstring name = ach->name; + wstring descr = ach->getDescription(); + + int x = xm + 12; + int y = ym - 4; + + if (statsCounter->canTake(ach)) + { + int width = Math::_max(font->width(name), 120); + int height = font->wordWrapHeight(descr, width); + if (statsCounter->hasTaken(ach)) + { + height += 12; + } + fillGradient(x - 3, y - 3, x + width + 3, y + height + 3 + 12, 0xc0000000, 0xc0000000); + + font->drawWordWrap(descr, x, y + 12, width, 0xffa0a0a0); + if (statsCounter->hasTaken(ach)) + { + font->drawShadow(I18n::get(L"achievement.taken"), x, y + height + 4, 0xff9090ff); + } + } + else + { + int width = Math::_max(font->width(name), 120); + wstring msg = I18n::get(L"achievement.requires", ach->requires->name); + int height = font->wordWrapHeight(msg, width); + fillGradient(x - 3, y - 3, x + width + 3, y + height + 12 + 3, 0xc0000000, 0xc0000000); + font->drawWordWrap(msg, x, y + 12, width, 0xff705050); + } + font->drawShadow(name, x, y, statsCounter->canTake(ach) ? ach->isGolden() ? 0xffffff80 : 0xffffffff : ach->isGolden() ? 0xff808040 : 0xff808080); + + + } + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + Lighting::turnOff(); +#endif +} + +bool AchievementScreen::isPauseScreen() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.Client/AchievementScreen.h b/Minecraft.Client/AchievementScreen.h new file mode 100644 index 0000000..3c4412e --- /dev/null +++ b/Minecraft.Client/AchievementScreen.h @@ -0,0 +1,57 @@ +#pragma once +#include "Screen.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +class StatsCounter; + +class AchievementScreen : public Screen +{ +private: + static const int BIGMAP_X = 16; + static const int BIGMAP_Y = 17; + static const int BIGMAP_WIDTH = 224; + static const int BIGMAP_HEIGHT = 155; + + // number of pixels per achievement + static const int ACHIEVEMENT_COORD_SCALE = 24; + static const int EDGE_VALUE_X = Achievements::ACHIEVEMENT_WIDTH_POSITION * ACHIEVEMENT_COORD_SCALE; + static const int EDGE_VALUE_Y = Achievements::ACHIEVEMENT_HEIGHT_POSITION * ACHIEVEMENT_COORD_SCALE; + + int xMin; + int yMin; + int xMax; + int yMax; + + static const int MAX_BG_TILE_Y = (EDGE_VALUE_Y * 2 - 1) / 16; + +protected: + int imageWidth; + int imageHeight; + int xLastScroll; + int yLastScroll; + +protected: + double xScrollO, yScrollO; + double xScrollP, yScrollP; + double xScrollTarget, yScrollTarget; + +private: + int scrolling; + StatsCounter *statsCounter; + +public: + using Screen::keyPressed; + + AchievementScreen(StatsCounter *statsCounter); + virtual void init(); +protected: + virtual void buttonClicked(Button *button); + virtual void keyPressed(char eventCharacter, int eventKey); +public: + virtual void render(int mouseX, int mouseY, float a); + virtual void tick(); +protected: + virtual void renderLabels(); + virtual void renderBg(int xm, int ym, float a); +public: + virtual bool isPauseScreen(); +}; diff --git a/Minecraft.Client/AllowAllCuller.cpp b/Minecraft.Client/AllowAllCuller.cpp new file mode 100644 index 0000000..7627af3 --- /dev/null +++ b/Minecraft.Client/AllowAllCuller.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "AllowAllCuller.h" + +bool AllowAllCuller::isVisible(AABB *bb) +{ + return true; +} + +bool AllowAllCuller::cubeInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return true; +} + +bool AllowAllCuller::cubeFullyInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return true; +} + +void AllowAllCuller::prepare(double xOff, double yOff, double zOff) +{ +} \ No newline at end of file diff --git a/Minecraft.Client/AllowAllCuller.h b/Minecraft.Client/AllowAllCuller.h new file mode 100644 index 0000000..5b86604 --- /dev/null +++ b/Minecraft.Client/AllowAllCuller.h @@ -0,0 +1,11 @@ +#pragma once +#include "Culler.h" + +class AllowAllCuller +{ +public: + virtual bool isVisible(AABB *bb); + virtual bool cubeInFrustum(double x0, double y0, double z0, double x1, double y1, double z1); + virtual bool cubeFullyInFrustum(double x0, double y0, double z0, double x1, double y1, double z1); + virtual void prepare(double xOff, double yOff, double zOff); +}; \ No newline at end of file diff --git a/Minecraft.Client/ArchiveFile.cpp b/Minecraft.Client/ArchiveFile.cpp new file mode 100644 index 0000000..642471a --- /dev/null +++ b/Minecraft.Client/ArchiveFile.cpp @@ -0,0 +1,215 @@ +#include "stdafx.h" + +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\compression.h" + +#include "ArchiveFile.h" + +void ArchiveFile::_readHeader(DataInputStream *dis) +{ + int numberOfFiles = dis->readInt(); + + for (int i = 0; i < numberOfFiles; i++) + { + MetaData *meta = new MetaData(); + meta->filename = dis->readUTF(); + meta->ptr = dis->readInt(); + meta->filesize = dis->readInt(); + + // Filenames preceeded by an asterisk have been compressed. + if (meta->filename[0] == '*') + { + meta->filename = meta->filename.substr(1); + meta->isCompressed = true; + } + else meta->isCompressed = false; + + m_index.insert( pair(meta->filename,meta) ); + } +} + +ArchiveFile::ArchiveFile(File file) +{ + m_cachedData = NULL; + m_sourcefile = file; + app.DebugPrintf("Loading archive file...\n"); +#ifndef _CONTENT_PACKAGE + char buf[256]; + wcstombs(buf, file.getPath().c_str(), 256); + app.DebugPrintf("archive file - %s\n",buf); +#endif + + if(!file.exists()) + { + app.DebugPrintf("Failed to load archive file!\n");//,file.getPath()); + app.FatalLoadError(); + } + + FileInputStream fis(file); + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + byteArray readArray(file.length()); + fis.read(readArray,0,file.length()); + + ByteArrayInputStream bais(readArray); + DataInputStream dis(&bais); + + m_cachedData = readArray.data; +#else + DataInputStream dis(&fis); +#endif + + _readHeader(&dis); + + dis.close(); + fis.close(); +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + bais.reset(); +#endif + app.DebugPrintf("Finished loading archive file\n"); +} + +ArchiveFile::~ArchiveFile() +{ + delete m_cachedData; +} + +vector *ArchiveFile::getFileList() +{ + vector *out = new vector(); + + for ( AUTO_VAR(it, m_index.begin()); + it != m_index.end(); + it++ ) + + out->push_back( it->first ); + + return out; +} + +bool ArchiveFile::hasFile(const wstring &filename) +{ + return m_index.find(filename) != m_index.end(); +} + +int ArchiveFile::getFileSize(const wstring &filename) +{ + return hasFile(filename) ? m_index.at(filename)->filesize : -1; +} + +byteArray ArchiveFile::getFile(const wstring &filename) +{ + byteArray out; + AUTO_VAR(it,m_index.find(filename)); + + if(it == m_index.end()) + { + app.DebugPrintf("Couldn't find file in archive\n"); + app.DebugPrintf("Failed to find file '%ls' in archive\n", filename.c_str()); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + } + else + { + PMetaData data = it->second; + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + out = byteArray(data->filesize ); + + memcpy( out.data, m_cachedData + data->ptr, data->filesize ); +#else + +#ifdef _UNICODE + HANDLE hfile = CreateFile( m_sourcefile.getPath().c_str(), + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); +#else + app.DebugPrintf("Createfile archive\n"); + HANDLE hfile = CreateFile( wstringtofilename(m_sourcefile.getPath()), + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); +#endif + + if (hfile != INVALID_HANDLE_VALUE) + { + app.DebugPrintf("hfile ok\n"); + DWORD ok = SetFilePointer( hfile, + data->ptr, + NULL, + FILE_BEGIN + ); + + if (ok != INVALID_SET_FILE_POINTER) + { + PBYTE pbData = new BYTE[ data->filesize ]; + + DWORD bytesRead = -1; + BOOL bSuccess = ReadFile( hfile, + (LPVOID) pbData, + data->filesize, + &bytesRead, + NULL + ); + + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + assert(bytesRead == data->filesize); + out = byteArray(pbData, data->filesize); + } + else + { + app.FatalLoadError(); + } + + CloseHandle(hfile); + } + else + { + app.DebugPrintf("bad hfile\n"); + app.FatalLoadError(); + } +#endif + + // Compressed filenames are preceeded with an asterisk. + if ( data->isCompressed && out.data != NULL ) + { + /* 4J-JEV: + * If a compressed file is accessed before compression object is + * initialized it will crash here (Compression::getCompression). + */ + ///4 279 553 556 + + ByteArrayInputStream bais(out); + DataInputStream dis(&bais); + unsigned int decompressedSize = dis.readInt(); + dis.close(); + + PBYTE uncompressedBuffer = new BYTE[decompressedSize]; + Compression::getCompression()->Decompress(uncompressedBuffer, &decompressedSize, out.data+4, out.length-4); + + delete [] out.data; + + out.data = uncompressedBuffer; + out.length = decompressedSize; + } + + assert(out.data != NULL); // THERE IS NO FILE WITH THIS NAME! + + } + + return out; +} diff --git a/Minecraft.Client/ArchiveFile.h b/Minecraft.Client/ArchiveFile.h new file mode 100644 index 0000000..722d570 --- /dev/null +++ b/Minecraft.Client/ArchiveFile.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include "..\Minecraft.World\File.h" +#include "..\Minecraft.World\ArrayWithLength.h" + +using namespace std; + +class ArchiveFile +{ +protected: + File m_sourcefile; + BYTE *m_cachedData; + + typedef struct _MetaData + { + wstring filename; + int ptr; + int filesize; + bool isCompressed; + + } MetaData, *PMetaData; + + unordered_map m_index; + +public: + void _readHeader(DataInputStream *dis); + + ArchiveFile(File file); + ~ArchiveFile(); + + vector *getFileList(); + bool hasFile(const wstring &filename); + int getFileSize(const wstring &filename); + byteArray getFile(const wstring &filename); +}; \ No newline at end of file diff --git a/Minecraft.Client/ArrowRenderer.cpp b/Minecraft.Client/ArrowRenderer.cpp new file mode 100644 index 0000000..65c4ee7 --- /dev/null +++ b/Minecraft.Client/ArrowRenderer.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "ArrowRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\Mth.h" + +void ArrowRenderer::render(shared_ptr _arrow, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Arrow rather than shared_ptr we have here - + // do some casting around instead + shared_ptr arrow = dynamic_pointer_cast(_arrow); + bindTexture(TN_ITEM_ARROWS); // 4J - was L"/item/arrows.png" + + glPushMatrix(); + + float yRot = arrow->yRot; + float xRot = arrow->xRot; + float yRotO = arrow->yRotO; + float xRotO = arrow->xRotO; + if( ( yRot - yRotO ) > 180.0f ) yRot -= 360.0f; + else if( ( yRot - yRotO ) < -180.0f ) yRot += 360.0f; + if( ( xRot - xRotO ) > 180.0f ) xRot -= 360.0f; + else if( ( xRot - xRotO ) < -180.0f ) xRot += 360.0f; + + glTranslatef((float)x, (float)y, (float)z); + glRotatef(yRotO + (yRot - yRotO) * a - 90, 0, 1, 0); + glRotatef(xRotO + (xRot - xRotO) * a, 0, 0, 1); + + Tesselator *t = Tesselator::getInstance(); + int type = 0; + + float u0 = 0 / 32.0f; + float u1 = 16 / 32.0f; + float v0 = (0 + type * 10) / 32.0f; + float v1 = (5 + type * 10) / 32.0f; + + float u02 = 0 / 32.0f; + float u12 = 5 / 32.0f; + float v02 = (5 + type * 10) / 32.0f; + float v12 = (10 + type * 10) / 32.0f; + float ss = 0.9f / 16.0f; + glEnable(GL_RESCALE_NORMAL); + float shake = arrow->shakeTime-a; + if (shake>0) + { + float pow = -Mth::sin(shake*3)*shake; + glRotatef(pow, 0, 0, 1); + } + glRotatef(45, 1, 0, 0); + glScalef(ss, ss, ss); + + glTranslatef(-4, 0, 0); + +// glNormal3f(ss, 0, 0); // 4J - changed to use tesselator + t->begin(); + t->normal(1,0,0); + t->vertexUV((float)(-7), (float)( -2), (float)( -2), (float)( u02), (float)( v02)); + t->vertexUV((float)(-7), (float)( -2), (float)( +2), (float)( u12), (float)( v02)); + t->vertexUV((float)(-7), (float)( +2), (float)( +2), (float)( u12), (float)( v12)); + t->vertexUV((float)(-7), (float)( +2), (float)( -2), (float)( u02), (float)( v12)); + t->end(); + +// glNormal3f(-ss, 0, 0); // 4J - changed to use tesselator + t->begin(); + t->normal(-1,0,0); + t->vertexUV((float)(-7), (float)( +2), (float)( -2), (float)( u02), (float)( v02)); + t->vertexUV((float)(-7), (float)( +2), (float)( +2), (float)( u12), (float)( v02)); + t->vertexUV((float)(-7), (float)( -2), (float)( +2), (float)( u12), (float)( v12)); + t->vertexUV((float)(-7), (float)( -2), (float)( -2), (float)( u02), (float)( v12)); + t->end(); + + for (int i = 0; i < 4; i++) + { + + glRotatef(90, 1, 0, 0); +// glNormal3f(0, 0, ss); // 4J - changed to use tesselator + t->begin(); + t->normal(0,0,1); + t->vertexUV((float)(-8), (float)( -2), (float)( 0), (float)( u0), (float)( v0)); + t->vertexUV((float)(+8), (float)( -2), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(+8), (float)( +2), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(-8), (float)( +2), (float)( 0), (float)( u0), (float)( v1)); + t->end(); + } + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/ArrowRenderer.h b/Minecraft.Client/ArrowRenderer.h new file mode 100644 index 0000000..2731f7c --- /dev/null +++ b/Minecraft.Client/ArrowRenderer.h @@ -0,0 +1,8 @@ +#pragma once +#include "EntityRenderer.h" + +class ArrowRenderer : public EntityRenderer +{ +public: + virtual void render(shared_ptr _arrow, double x, double y, double z, float rot, float a); +}; diff --git a/Minecraft.Client/BlazeModel.cpp b/Minecraft.Client/BlazeModel.cpp new file mode 100644 index 0000000..5af894a --- /dev/null +++ b/Minecraft.Client/BlazeModel.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "BlazeModel.h" +#include "ModelPart.h" + +BlazeModel::BlazeModel() : Model() +{ + upperBodyParts = ModelPartArray(12); + + for (unsigned int i = 0; i < upperBodyParts.length; i++) + { + upperBodyParts[i] = new ModelPart(this, 0, 16); + upperBodyParts[i]->addBox(0, 0, 0, 2, 8, 2); + } + + head = new ModelPart(this, 0, 0); + head->addBox(-4, -4, -4, 8, 8, 8); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + // 4J Stu - Not just performance, but alpha+depth tests don't work right unless we compile here + for (unsigned int i = 0; i < upperBodyParts.length; i++) + { + upperBodyParts[i]->compile(1.0f/16.0f); + } + head->compile(1.0f/16.0f); +} + +int BlazeModel::modelVersion() +{ + return 8; +} + +void BlazeModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + head->render(scale,usecompiled); + for (unsigned int i = 0; i < upperBodyParts.length; i++) + { + upperBodyParts[i]->render(scale, usecompiled); + } +} + +void BlazeModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + + float angle = bob * PI * -.1f; + for (int i = 0; i < 4; i++) + { + upperBodyParts[i]->y = -2 + Mth::cos((i * 2 + bob) * .25f); + upperBodyParts[i]->x = Mth::cos(angle) * 9.0f; + upperBodyParts[i]->z = Mth::sin(angle) * 9.0f; + angle += PI * 0.5f; + } + angle = .25f * PI + bob * PI * .03f; + for (int i = 4; i < 8; i++) + { + upperBodyParts[i]->y = 2 + Mth::cos((i * 2 + bob) * .25f); + upperBodyParts[i]->x = Mth::cos(angle) * 7.0f; + upperBodyParts[i]->z = Mth::sin(angle) * 7.0f; + angle += PI * 0.5f; + } + + angle = .15f * PI + bob * PI * -.05f; + for (int i = 8; i < 12; i++) + { + upperBodyParts[i]->y = 11 + Mth::cos((i * 1.5f + bob) * .5f); + upperBodyParts[i]->x = Mth::cos(angle) * 5.0f; + upperBodyParts[i]->z = Mth::sin(angle) * 5.0f; + angle += PI * 0.5f; + } + + head->yRot = yRot / (float) (180 / PI); + head->xRot = xRot / (float) (180 / PI); +} + diff --git a/Minecraft.Client/BlazeModel.h b/Minecraft.Client/BlazeModel.h new file mode 100644 index 0000000..27ee206 --- /dev/null +++ b/Minecraft.Client/BlazeModel.h @@ -0,0 +1,16 @@ +#pragma once +#include "Model.h" + +class BlazeModel : public Model +{ + +private: + ModelPartArray upperBodyParts; + ModelPart *head; + +public: + BlazeModel(); + int modelVersion(); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); +}; diff --git a/Minecraft.Client/BlazeRenderer.cpp b/Minecraft.Client/BlazeRenderer.cpp new file mode 100644 index 0000000..7d5210d --- /dev/null +++ b/Minecraft.Client/BlazeRenderer.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "BlazeModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "BlazeRenderer.h" + +BlazeRenderer::BlazeRenderer() : MobRenderer(new BlazeModel(), 0.5f) +{ + this->modelVersion = ((BlazeModel *) model)->modelVersion(); +} + +void BlazeRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Blaze rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + + int modelVersion = ((BlazeModel *) model)->modelVersion(); + if (modelVersion != this->modelVersion) + { + this->modelVersion = modelVersion; + model = new BlazeModel(); + } + MobRenderer::render(mob, x, y, z, rot, a); +} \ No newline at end of file diff --git a/Minecraft.Client/BlazeRenderer.h b/Minecraft.Client/BlazeRenderer.h new file mode 100644 index 0000000..fc575e9 --- /dev/null +++ b/Minecraft.Client/BlazeRenderer.h @@ -0,0 +1,14 @@ +#pragma once + +#include "MobRenderer.h" + +class BlazeRenderer : public MobRenderer +{ +private: + int modelVersion; + +public: + BlazeRenderer(); + + virtual void render(shared_ptr mob, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/BoatModel.cpp b/Minecraft.Client/BoatModel.cpp new file mode 100644 index 0000000..d0d465e --- /dev/null +++ b/Minecraft.Client/BoatModel.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "BoatModel.h" + +BoatModel::BoatModel() : Model() +{ + cubes[0] = new ModelPart(this, 0, 8); + cubes[1] = new ModelPart(this, 0, 0); + cubes[2] = new ModelPart(this, 0, 0); + cubes[3] = new ModelPart(this, 0, 0); + cubes[4] = new ModelPart(this, 0, 0); + + int w = 24; + int d = 6; + int h = 20; + int yOff = 4; + + cubes[0]->addBox((float)(-w / 2), (float)(-h / 2 + 2), -3, w, h - 4, 4, 0); + cubes[0]->setPos(0, (float)(0 + yOff), 0); + + cubes[1]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[1]->setPos((float)(-w / 2 + 1), (float)(0 + yOff), 0); + + cubes[2]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[2]->setPos((float)(+w / 2 - 1), (float)(0 + yOff), 0); + + cubes[3]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[3]->setPos(0, (float)(0 + yOff), (float)(-h / 2 + 1)); + + cubes[4]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[4]->setPos(0, (float)(0 + yOff), (float)(+h / 2 - 1)); + + cubes[0]->xRot = PI / 2; + cubes[1]->yRot = PI / 2 * 3; + cubes[2]->yRot = PI / 2 * 1; + cubes[3]->yRot = PI / 2 * 2; + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + cubes[0]->compile(1.0f/16.0f); + cubes[1]->compile(1.0f/16.0f); + cubes[2]->compile(1.0f/16.0f); + cubes[3]->compile(1.0f/16.0f); + cubes[4]->compile(1.0f/16.0f); +} + +void BoatModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + for (int i = 0; i < 5; i++) + { + cubes[i]->render(scale, usecompiled); + } +} \ No newline at end of file diff --git a/Minecraft.Client/BoatModel.h b/Minecraft.Client/BoatModel.h new file mode 100644 index 0000000..4298b64 --- /dev/null +++ b/Minecraft.Client/BoatModel.h @@ -0,0 +1,11 @@ +#pragma once +#include "Model.h" +#include "ModelPart.h" + +class BoatModel : public Model +{ +public: + ModelPart *cubes[5]; + BoatModel(); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +}; \ No newline at end of file diff --git a/Minecraft.Client/BoatRenderer.cpp b/Minecraft.Client/BoatRenderer.cpp new file mode 100644 index 0000000..f1367ae --- /dev/null +++ b/Minecraft.Client/BoatRenderer.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "BoatRenderer.h" +#include "BoatModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\Mth.h" + +BoatRenderer::BoatRenderer() : EntityRenderer() +{ + this->shadowRadius = 0.5f; + model = new BoatModel(); +} + +void BoatRenderer::render(shared_ptr _boat, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Boat rather than shared_ptr we have here - + // do some casting around instead + shared_ptr boat = dynamic_pointer_cast(_boat); + + glPushMatrix(); + + glTranslatef((float) x, (float) y, (float) z); + + glRotatef(180-rot, 0, 1, 0); + float hurt = boat->getHurtTime() - a; + float dmg = boat->getDamage() - a; + if (dmg<0) dmg = 0; + if (hurt>0) + { + glRotatef(Mth::sin(hurt)*hurt*dmg/10*boat->getHurtDir(), 1, 0, 0); + } + + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + float ss = 12/16.0f; + glScalef(ss, ss, ss); + glScalef(1/ss, 1/ss, 1/ss); + + bindTexture(TN_ITEM_BOAT); // 4J was L"/item/boat.png" + glScalef(-1, -1, 1); + model->render(boat, 0, 0, -0.1f, 0, 0, 1 / 16.0f, true); + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/BoatRenderer.h b/Minecraft.Client/BoatRenderer.h new file mode 100644 index 0000000..1cc9c1d --- /dev/null +++ b/Minecraft.Client/BoatRenderer.h @@ -0,0 +1,13 @@ +#pragma once +#include "EntityRenderer.h" + +class BoatRenderer : public EntityRenderer +{ +protected: + Model *model; + +public: + BoatRenderer(); + + virtual void render(shared_ptr boat, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/BookModel.cpp b/Minecraft.Client/BookModel.cpp new file mode 100644 index 0000000..bc4769b --- /dev/null +++ b/Minecraft.Client/BookModel.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "BookModel.h" +#include "ModelPart.h" + +BookModel::BookModel() +{ + leftLid = (new ModelPart(this))->texOffs(0, 0)->addBox(-6, -5, 0, 6, 10, 0); + rightLid = (new ModelPart(this))->texOffs(16, 0)->addBox(0, -5, 0, 6, 10, 0); + + seam = (new ModelPart(this))->texOffs(12, 0)->addBox(-1, -5, 0, 2, 10, 0); + + // 4J - added faceMasks here to remove sides of these page boxes which end up being nearly coplanar to the cover of the book and flickering when rendering at a distance + leftPages = (new ModelPart(this))->texOffs(0, 10)->addBoxWithMask(0, -4, -1 + 0.01f, 5, 8, 1, 47); // 4J - faceMask is binary 101111 + rightPages = (new ModelPart(this))->texOffs(12, 10)->addBoxWithMask(0, -4, -0.01f, 5, 8, 1, 31); // 4J - faceMask is binary 011111 + + flipPage1 = (new ModelPart(this))->texOffs(24, 10)->addBox(0, -4, 0, 5, 8, 0); + flipPage2 = (new ModelPart(this))->texOffs(24, 10)->addBox(0, -4, 0, 5, 8, 0); + + leftLid->setPos(0, 0, -1); + rightLid->setPos(0, 0, 1); + + seam->yRot = PI / 2; + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + leftLid->compile(1.0f/16.0f); + rightLid->compile(1.0f/16.0f); + seam->compile(1.0f/16.0f); + leftPages->compile(1.0f/16.0f); + rightPages->compile(1.0f/16.0f); + flipPage1->compile(1.0f/16.0f); + flipPage2->compile(1.0f/16.0f); + +} + +void BookModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + leftLid->render(scale,usecompiled); + rightLid->render(scale,usecompiled); + seam->render(scale,usecompiled); + + leftPages->render(scale,usecompiled); + rightPages->render(scale,usecompiled); + + flipPage1->render(scale,usecompiled); + flipPage2->render(scale,usecompiled); +} + +void BookModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + float openness = (Mth::sin(time * 0.02f) * 0.10f + 1.25f) * yRot; + + leftLid->yRot = PI + openness; + rightLid->yRot = -openness; + leftPages->yRot = +openness; + rightPages->yRot = -openness; + + flipPage1->yRot = +openness - openness * 2 * r; + flipPage2->yRot = +openness - openness * 2 * bob; + + leftPages->x = Mth::sin(openness); + rightPages->x = Mth::sin(openness); + flipPage1->x = Mth::sin(openness); + flipPage2->x = Mth::sin(openness); +} + diff --git a/Minecraft.Client/BookModel.h b/Minecraft.Client/BookModel.h new file mode 100644 index 0000000..8b367a3 --- /dev/null +++ b/Minecraft.Client/BookModel.h @@ -0,0 +1,16 @@ + +#pragma once +#include "Model.h" + +class BookModel : public Model +{ +public: + ModelPart *leftLid, *rightLid; + ModelPart *leftPages, *rightPages; + ModelPart *flipPage1, *flipPage2; + ModelPart *seam; + + BookModel(); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); +}; diff --git a/Minecraft.Client/BreakingItemParticle.cpp b/Minecraft.Client/BreakingItemParticle.cpp new file mode 100644 index 0000000..51b721f --- /dev/null +++ b/Minecraft.Client/BreakingItemParticle.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "BreakingItemParticle.h" +#include "Tesselator.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.h" + +void BreakingItemParticle::_init(Item *item, Textures *textures, int data) +{ + this->setTex(textures, item->getIcon(data)); + rCol = gCol = bCol = 1.0f; + gravity = Tile::snow->gravity; + size /= 2; +} + +BreakingItemParticle::BreakingItemParticle(Level *level, double x, double y, double z, Item *item, Textures *textures, int data) : Particle(level, x, y, z, 0, 0, 0) +{ + _init(item, textures, data); +} + +BreakingItemParticle::BreakingItemParticle(Level *level, double x, double y, double z, double xa, double ya, double za, Item *item, Textures *textures, int data) : Particle(level, x, y, z, 0, 0, 0) +{ + _init(item, textures, data); + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd += xa; + yd += ya; + zd += za; +} + +int BreakingItemParticle::getParticleTexture() +{ + return ParticleEngine::ITEM_TEXTURE; +} + +void BreakingItemParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float u0 = (texX + uo / 4.0f) / 16.0f; + float u1 = u0 + 0.999f / 16.0f / 4; + float v0 = (texY + vo / 4.0f) / 16.0f; + float v1 = v0 + 0.999f / 16.0f / 4; + float r = 0.1f * size; + + if (tex != NULL) + { + u0 = tex->getU((uo / 4.0f) * SharedConstants::WORLD_RESOLUTION); + u1 = tex->getU(((uo + 1) / 4.0f) * SharedConstants::WORLD_RESOLUTION); + v0 = tex->getV((vo / 4.0f) * SharedConstants::WORLD_RESOLUTION); + v1 = tex->getV(((vo + 1) / 4.0f) * SharedConstants::WORLD_RESOLUTION); + } + + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + float br = SharedConstants::TEXTURE_LIGHTING ? 1 : getBrightness(a); // 4J - change brought forward from 1.8.2 + t->color(br * rCol, br * gCol, br * bCol); + + t->vertexUV((float)(x - xa * r - xa2 * r), (float)( y - ya * r), (float)( z - za * r - za2 * r), (float)( u0), (float)( v1)); + t->vertexUV((float)(x - xa * r + xa2 * r), (float)( y + ya * r), (float)( z - za * r + za2 * r), (float)( u0), (float)( v0)); + t->vertexUV((float)(x + xa * r + xa2 * r), (float)( y + ya * r), (float)( z + za * r + za2 * r), (float)( u1), (float)( v0)); + t->vertexUV((float)(x + xa * r - xa2 * r), (float)( y - ya * r), (float)( z + za * r - za2 * r), (float)( u1), (float)( v1)); + +} \ No newline at end of file diff --git a/Minecraft.Client/BreakingItemParticle.h b/Minecraft.Client/BreakingItemParticle.h new file mode 100644 index 0000000..390426d --- /dev/null +++ b/Minecraft.Client/BreakingItemParticle.h @@ -0,0 +1,15 @@ +#pragma once +#include "Particle.h" + +class BreakingItemParticle : public Particle +{ + // virtual eINSTANCEOF GetType(); // 4J-IB/JEV TODO needs implementation + +public: + virtual eINSTANCEOF GetType() { return eType_BREAKINGITEMPARTICLE; } + void _init(Item *item, Textures *textures, int data); + BreakingItemParticle(Level *level, double x, double y, double z, Item *item, Textures *textures, int data = 0); + BreakingItemParticle(Level *level, double x, double y, double z, double xa, double ya, double za, Item *item, Textures *textures, int data = 0); + virtual int getParticleTexture(); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); +}; diff --git a/Minecraft.Client/BubbleParticle.cpp b/Minecraft.Client/BubbleParticle.cpp new file mode 100644 index 0000000..2d1380e --- /dev/null +++ b/Minecraft.Client/BubbleParticle.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "BubbleParticle.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.material.h" + +BubbleParticle::BubbleParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, xa, ya, za) + { + rCol = 1.0f; + gCol = 1.0f; + bCol = 1.0f; + setMiscTex(32); + this->setSize(0.02f, 0.02f); + + size = size*(random->nextFloat()*0.6f+0.2f); + + xd = xa*0.2f+(float)(Math::random()*2-1)*0.02f; + yd = ya*0.2f+(float)(Math::random()*2-1)*0.02f; + zd = za*0.2f+(float)(Math::random()*2-1)*0.02f; + + lifetime = (int) (8 / (Math::random() * 0.8 + 0.2)); +} + +void BubbleParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + yd += 0.002; + move(xd, yd, zd); + xd *= 0.85f; + yd *= 0.85f; + zd *= 0.85f; + + if (level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)) != Material::water) remove(); + + if (lifetime-- <= 0) remove(); +} \ No newline at end of file diff --git a/Minecraft.Client/BubbleParticle.h b/Minecraft.Client/BubbleParticle.h new file mode 100644 index 0000000..0e786d9 --- /dev/null +++ b/Minecraft.Client/BubbleParticle.h @@ -0,0 +1,10 @@ +#pragma once +#include "Particle.h" + +class BubbleParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_BUBBLEPARTICLE; } + BubbleParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/BufferedImage.cpp b/Minecraft.Client/BufferedImage.cpp new file mode 100644 index 0000000..658e934 --- /dev/null +++ b/Minecraft.Client/BufferedImage.cpp @@ -0,0 +1,400 @@ +#include "stdafx.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "Textures.h" +#include "..\Minecraft.World\ArrayWithLength.h" +#include "BufferedImage.h" + +#ifdef _XBOX +typedef struct +{ + unsigned int filesz; + unsigned short creator1; + unsigned short creator2; + unsigned int bmp_offset; + unsigned int header_sz; + unsigned int width; + unsigned int height; + unsigned short nplanes; + unsigned short bitspp; + unsigned int compress_type; + unsigned int bmp_bytesz; + int hres; + int vres; + unsigned int ncolors; + unsigned int nimpcolors; +} BITMAPINFOHEADER; +#endif + +BufferedImage::BufferedImage(int width,int height,int type) +{ + data[0] = new int[width*height]; + + for( int i = 1 ; i < 10; i++ ) + { + data[i] = NULL; + } + this->width = width; + this->height = height; +} + +void BufferedImage::ByteFlip4(unsigned int &data) +{ + data = ( data >> 24 ) | + ( ( data >> 8 ) & 0x0000ff00 ) | + ( ( data << 8 ) & 0x00ff0000 ) | + ( data << 24 ); +} +// Loads a bitmap into a buffered image - only currently supports the 2 types of 32-bit image that we've made so far +// and determines which of these is which by the compression method. Compression method 3 is a 32-bit image with only +// 24-bits used (ie no alpha channel) whereas method 0 is a full 32-bit image with a valid alpha channel. +BufferedImage::BufferedImage(const wstring& File, bool filenameHasExtension /*=false*/, bool bTitleUpdateTexture /*=false*/, const wstring &drive /*=L""*/) +{ + HRESULT hr; + wstring wDrive; + wstring filePath; + filePath = File; + + wDrive = drive; + if(wDrive.empty()) + { +#ifdef _XBOX + if(bTitleUpdateTexture) + { + // Make the content package point to to the UPDATE: drive is needed +#ifdef _TU_BUILD + wDrive=L"UPDATE:\\"; +#else + + wDrive=L"GAME:\\res\\TitleUpdate\\"; +#endif + } + else + { + wDrive=L"GAME:\\"; + } +#else + +#ifdef __PS3__ + + char *pchUsrDir; + if(app.GetBootedFromDiscPatch()) + { + const char *pchTextureName=wstringtofilename(File); + pchUsrDir = app.GetBDUsrDirPath(pchTextureName); + } + else + { + pchUsrDir=getUsrDirPath(); + } + + wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); + + if(bTitleUpdateTexture) + { + // Make the content package point to to the UPDATE: drive is needed + wDrive= wstr + L"\\Common\\res\\TitleUpdate\\"; + } + else + { + wDrive= wstr + L"/Common/"; + } +#elif __PSVITA__ + + /*char *pchUsrDir=getUsrDirPath(); + + wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); + + if(bTitleUpdateTexture) + { + // Make the content package point to to the UPDATE: drive is needed + wDrive= wstr + L"\\Common\\res\\TitleUpdate\\"; + } + else + { + wDrive= wstr + L"/Common/"; + }*/ + + if(bTitleUpdateTexture) + { + // Make the content package point to to the UPDATE: drive is needed + wDrive= L"Common\\res\\TitleUpdate\\"; + } + else + { + wDrive= L"Common/"; + } +#else + if(bTitleUpdateTexture) + { + // Make the content package point to to the UPDATE: drive is needed + wDrive= L"Common\\res\\TitleUpdate\\"; + } + else + { + wDrive= L"Common/"; + } +#endif + +#endif + } + + for( int l = 0 ; l < 10; l++ ) + { + data[l] = NULL; + } + + for( int l = 0; l < 10; l++ ) + { + wstring name; + wstring mipMapPath = L""; + if( l != 0 ) + { + mipMapPath = L"MipMapLevel" + _toString(l+1); + } + if( filenameHasExtension ) + { + name = wDrive + L"res" + filePath.substr(0,filePath.length()); + } + else + { + name = wDrive + L"res" + filePath.substr(0,filePath.length()-4) + mipMapPath + L".png"; + } + + const char *pchTextureName=wstringtofilename(name); + +#ifdef _DEBUG + app.DebugPrintf("\n--- Loading TEXTURE - %s\n\n",pchTextureName); +#endif + + D3DXIMAGE_INFO ImageInfo; + ZeroMemory(&ImageInfo,sizeof(D3DXIMAGE_INFO)); + hr=RenderManager.LoadTextureData(pchTextureName,&ImageInfo,&data[l]); + + + if(hr!=ERROR_SUCCESS) + { + // 4J - If we haven't loaded the non-mipmap version then exit the game + if( l == 0 ) + { + app.FatalLoadError(); + } + return; + } + + if( l == 0 ) + { + width=ImageInfo.Width; + height=ImageInfo.Height; + } + } +} + +BufferedImage::BufferedImage(DLCPack *dlcPack, const wstring& File, bool filenameHasExtension /*= false*/ ) +{ + HRESULT hr; + wstring filePath = File; + BYTE *pbData = NULL; + DWORD dwBytes = 0; + + for( int l = 0 ; l < 10; l++ ) + { + data[l] = NULL; + } + + for( int l = 0; l < 10; l++ ) + { + wstring name; + wstring mipMapPath = L""; + if( l != 0 ) + { + mipMapPath = L"MipMapLevel" + _toString(l+1); + } + if( filenameHasExtension ) + { + name = L"res" + filePath.substr(0,filePath.length()); + } + else + { + name = L"res" + filePath.substr(0,filePath.length()-4) + mipMapPath + L".png"; + } + + if(!dlcPack->doesPackContainFile(DLCManager::e_DLCType_All, name)) + { + // 4J - If we haven't loaded the non-mipmap version then exit the game + if( l == 0 ) + { + app.FatalLoadError(); + } + return; + } + + DLCFile *dlcFile = dlcPack->getFile(DLCManager::e_DLCType_All, name); + pbData = dlcFile->getData(dwBytes); + if(pbData == NULL || dwBytes == 0) + { + // 4J - If we haven't loaded the non-mipmap version then exit the game + if( l == 0 ) + { + app.FatalLoadError(); + } + return; + } + + D3DXIMAGE_INFO ImageInfo; + ZeroMemory(&ImageInfo,sizeof(D3DXIMAGE_INFO)); + hr=RenderManager.LoadTextureData(pbData,dwBytes,&ImageInfo,&data[l]); + + + if(hr!=ERROR_SUCCESS) + { + // 4J - If we haven't loaded the non-mipmap version then exit the game + if( l == 0 ) + { + app.FatalLoadError(); + } + return; + } + + if( l == 0 ) + { + width=ImageInfo.Width; + height=ImageInfo.Height; + } + } +} + + +BufferedImage::BufferedImage(BYTE *pbData, DWORD dwBytes) +{ + int iCurrentByte=0; + for( int l = 0 ; l < 10; l++ ) + { + data[l] = NULL; + } + + D3DXIMAGE_INFO ImageInfo; + ZeroMemory(&ImageInfo,sizeof(D3DXIMAGE_INFO)); + HRESULT hr=RenderManager.LoadTextureData(pbData,dwBytes,&ImageInfo,&data[0]); + + if(hr==ERROR_SUCCESS) + { + width=ImageInfo.Width; + height=ImageInfo.Height; + } + else + { + app.FatalLoadError(); + } +} + +BufferedImage::~BufferedImage() +{ + for(int i = 0; i < 10; i++ ) + { + delete[] data[i]; + } +} + +int BufferedImage::getWidth() +{ + return width; +} + +int BufferedImage::getHeight() +{ + return height; +} + +void BufferedImage::getRGB(int startX, int startY, int w, int h, intArray out,int offset,int scansize, int level) +{ + int ww = width >> level; + for( int y = 0; y < h; y++ ) + { + for( int x = 0; x < w; x++ ) + { + out[ y * scansize + offset + x] = data[level][ startX + x + ww * ( startY + y ) ]; + } + } +} + +int *BufferedImage::getData() +{ + return data[0]; +} + +int *BufferedImage::getData(int level) +{ + return data[level]; +} + +Graphics *BufferedImage::getGraphics() +{ + return NULL; +} + +//Returns the transparency. Returns either OPAQUE, BITMASK, or TRANSLUCENT. +//Specified by: +//getTransparency in interface Transparency +//Returns: +//the transparency of this BufferedImage. +int BufferedImage::getTransparency() +{ + // TODO - 4J Implement? + return 0; +} + +//Returns a subimage defined by a specified rectangular region. The returned BufferedImage shares the same data array as the original image. +//Parameters: +//x, y - the coordinates of the upper-left corner of the specified rectangular region +//w - the width of the specified rectangular region +//h - the height of the specified rectangular region +//Returns: +//a BufferedImage that is the subimage of this BufferedImage. +BufferedImage *BufferedImage::getSubimage(int x ,int y, int w, int h) +{ + // TODO - 4J Implement + + BufferedImage *img = new BufferedImage(w,h,0); + intArray arrayWrapper(img->data[0], w*h); + this->getRGB(x, y, w, h, arrayWrapper,0,w); + + int level = 1; + while(getData(level) != NULL) + { + int ww = w >> level; + int hh = h >> level; + int xx = x >> level; + int yy = y >> level; + img->data[level] = new int[ww*hh]; + intArray arrayWrapper(img->data[level], ww*hh); + this->getRGB(xx, yy, ww, hh, arrayWrapper,0,ww,level); + + ++level; + } + + return img; +} + + +void BufferedImage::preMultiplyAlpha() +{ + int *curData = data[0]; + + int cur = 0; + int alpha = 0; + int r = 0; + int g = 0; + int b = 0; + + int total = width * height; + for(unsigned int i = 0; i < total; ++i) + { + cur = curData[i]; + alpha = (cur >> 24) & 0xff; + r = ((cur >> 16) & 0xff) * (float)alpha/255; + g = ((cur >> 8) & 0xff) * (float)alpha/255; + b = (cur & 0xff) * (float)alpha/255; + + curData[i] = (r << 16) | (g << 8) | (b ) | (alpha << 24); + } +} diff --git a/Minecraft.Client/BufferedImage.h b/Minecraft.Client/BufferedImage.h new file mode 100644 index 0000000..a0227fe --- /dev/null +++ b/Minecraft.Client/BufferedImage.h @@ -0,0 +1,33 @@ +#pragma once +using namespace std; + +class Graphics; +class DLCPack; + +class BufferedImage +{ +private: + int *data[10]; // Arrays for mipmaps - NULL if not used + int width; + int height; + void ByteFlip4(unsigned int &data); // 4J added +public: + static const int TYPE_INT_ARGB = 0; + static const int TYPE_INT_RGB = 1; + BufferedImage(int width,int height,int type); + BufferedImage(const wstring& File, bool filenameHasExtension = false, bool bTitleUpdateTexture=false, const wstring &drive =L""); // 4J added + BufferedImage(DLCPack *dlcPack, const wstring& File, bool filenameHasExtension = false ); // 4J Added + BufferedImage(BYTE *pbData, DWORD dwBytes); // 4J added + ~BufferedImage(); + + int getWidth(); + int getHeight(); + void getRGB(int startX, int startY, int w, int h, intArray out,int offset,int scansize, int level = 0); // 4J Added level param + int *getData(); // 4J added + int *getData(int level); // 4J added + Graphics *getGraphics(); + int getTransparency(); + BufferedImage *getSubimage(int x, int y, int w, int h); + + void preMultiplyAlpha(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Button.cpp b/Minecraft.Client/Button.cpp new file mode 100644 index 0000000..7de105c --- /dev/null +++ b/Minecraft.Client/Button.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "Button.h" +#include "Textures.h" + +Button::Button(int id, int x, int y, const wstring& msg) +{ + init(id, x, y, 200, 20, msg); +} + +Button::Button(int id, int x, int y, int w, int h, const wstring& msg) +{ + init(id, x, y, w, h, msg); +} + +// 4J - added +void Button::init(int id, int x, int y, int w, int h, const wstring& msg) +{ + active = true; + visible = true; + + // this bit of code from original ctor + this->id = id; + this->x = x; + this->y = y; + this->w = w; + this->h = h; + this->msg = msg; +} + +int Button::getYImage(bool hovered) +{ + int res = 1; + if (!active) res = 0; + else if (hovered) res = 2; + return res; +} + +void Button::render(Minecraft *minecraft, int xm, int ym) +{ + if (!visible) return; + + Font *font = minecraft->font; + + glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture(TN_GUI_GUI)); // 4J was L"/gui/gui.png" + glColor4f(1, 1, 1, 1); + + + bool hovered = xm >= x && ym >= y && xm < x + w && ym < y + h; + int yImage = getYImage(hovered); + + blit(x, y, 0, 46 + yImage * 20, w / 2, h); + blit(x + w / 2, y, 200 - w / 2, 46 + yImage * 20, w / 2, h); + + renderBg(minecraft, xm, ym); + + if (!active) + { + drawCenteredString(font, msg, x + w / 2, y + (h - 8) / 2, 0xffa0a0a0); + } + else + { + if (hovered) + { + drawCenteredString(font, msg, x + w / 2, y + (h - 8) / 2, 0xffffa0); + } + else + { + drawCenteredString(font, msg, x + w / 2, y + (h - 8) / 2, 0xe0e0e0); + } + } + +} + +void Button::renderBg(Minecraft *minecraft, int xm, int ym) +{ +} + +void Button::released(int mx, int my) +{ +} + +bool Button::clicked(Minecraft *minecraft, int mx, int my) +{ + return active && mx >= x && my >= y && mx < x + w && my < y + h; +} \ No newline at end of file diff --git a/Minecraft.Client/Button.h b/Minecraft.Client/Button.h new file mode 100644 index 0000000..0bef133 --- /dev/null +++ b/Minecraft.Client/Button.h @@ -0,0 +1,30 @@ +#pragma once +#include "GuiComponent.h" +using namespace std; + +class Button : public GuiComponent +{ +protected: + int w; + int h; +public: + int x, y; + wstring msg; + int id; + bool active; + bool visible; + + Button(int id, int x, int y, const wstring& msg); + Button(int id, int x, int y, int w, int h, const wstring& msg); + void init(int id, int x, int y, int w, int h, const wstring& msg); // 4J - added +protected: + virtual int getYImage(bool hovered); +public: + virtual void render(Minecraft *minecraft, int xm, int ym); + +protected: + virtual void renderBg(Minecraft *minecraft, int xm, int ym); +public: + virtual void released(int mx, int my); + virtual bool clicked(Minecraft *minecraft, int mx, int my); +}; diff --git a/Minecraft.Client/Camera.cpp b/Minecraft.Client/Camera.cpp new file mode 100644 index 0000000..db6072b --- /dev/null +++ b/Minecraft.Client/Camera.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include "Camera.h" +#include "MemoryTracker.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\TilePos.h" + +float Camera::xPlayerOffs = 0.0f; +float Camera::yPlayerOffs = 0.0f; +float Camera::zPlayerOffs = 0.0f; + +//IntBuffer *Camera::viewport = MemoryTracker::createIntBuffer(16); +FloatBuffer *Camera::modelview = MemoryTracker::createFloatBuffer(16); +FloatBuffer *Camera::projection = MemoryTracker::createFloatBuffer(16); +//FloatBuffer *Camera::position = MemoryTracker::createFloatBuffer(3); + +float Camera::xa = 0.0f; +float Camera::ya = 0.0f; +float Camera::za = 0.0f; +float Camera::xa2 = 0.0f; +float Camera::za2 = 0.0f; + +void Camera::prepare(shared_ptr player, bool mirror) +{ + glGetFloat(GL_MODELVIEW_MATRIX, modelview); + glGetFloat(GL_PROJECTION_MATRIX, projection); + + /* Original java code for reference + glGetInteger(GL_VIEWPORT, viewport); + + float x = (viewport.get(0) + viewport.get(2)) / 2; + float y = (viewport.get(1) + viewport.get(3)) / 2; + gluUnProject(x, y, 0, modelview, projection, viewport, position); + + xPlayerOffs = position->get(0); + yPlayerOffs = position->get(1); + zPlayerOffs = position->get(2); + */ + + // Xbox conversion here... note that we don't bother getting the viewport as this is just working out how to get a (0,0,0) point in clip space to pass into the inverted + // combined model/view/projection matrix, so we just need to get this matrix and get its translation as an equivalent. + XMMATRIX _modelview, _proj, _final, _invert; + XMVECTOR _det; + XMFLOAT4 trans; + + memcpy( &_modelview, modelview->_getDataPointer(), 64 ); + memcpy( &_proj, projection->_getDataPointer(), 64 ); + +#if ( defined __ORBIS__ ) || ( defined __PSVITA__ ) + _modelview = transpose(_modelview); + _proj = transpose(_proj); + _final = _modelview * _proj; + _invert = sce::Vectormath::Simd::Aos::inverse(_final); + xPlayerOffs = _invert.getElem(0,3) / _invert.getElem(3,3); + yPlayerOffs = _invert.getElem(1,3) / _invert.getElem(3,3); + zPlayerOffs = _invert.getElem(2,3) / _invert.getElem(3,3); +#elif defined __PS3__ + _modelview = transpose(_modelview); + _proj = transpose(_proj); + _final = _modelview * _proj; + _invert = Vectormath::Aos::inverse(_final); + xPlayerOffs = _invert.getElem(0,3) / _invert.getElem(3,3); + yPlayerOffs = _invert.getElem(1,3) / _invert.getElem(3,3); + zPlayerOffs = _invert.getElem(2,3) / _invert.getElem(3,3); +#else + _final = XMMatrixMultiply( _modelview, _proj ); + _det = XMMatrixDeterminant(_final); + _invert = XMMatrixInverse(&_det, _final); + + XMStoreFloat4(&trans,_invert.r[3]); + + xPlayerOffs = trans.x / trans.w; + yPlayerOffs = trans.y / trans.w; + zPlayerOffs = trans.z / trans.w; +#endif + + int flipCamera = mirror ? 1 : 0; + + float xRot = player->xRot; + float yRot = player->yRot; + + xa = cosf(yRot * PI / 180.0f) * (1 - flipCamera * 2); + za = sinf(yRot * PI / 180.0f) * (1 - flipCamera * 2); + + xa2 = -za * sinf(xRot * PI / 180.0f) * (1 - flipCamera * 2); + za2 = xa * sinf(xRot * PI / 180.0f) * (1 - flipCamera * 2); + ya = cosf(xRot * PI / 180.0f); +} + +TilePos *Camera::getCameraTilePos(shared_ptr player, double alpha) +{ + return new TilePos(getCameraPos(player, alpha)); +} + +Vec3 *Camera::getCameraPos(shared_ptr player, double alpha) +{ + double xx = player->xo + (player->x - player->xo) * alpha; + double yy = player->yo + (player->y - player->yo) * alpha + player->getHeadHeight(); + double zz = player->zo + (player->z - player->zo) * alpha; + + double xt = xx + Camera::xPlayerOffs * 1; + double yt = yy + Camera::yPlayerOffs * 1; + double zt = zz + Camera::zPlayerOffs * 1; + + return Vec3::newTemp(xt, yt, zt); +} + +int Camera::getBlockAt(Level *level, shared_ptr player, float alpha) +{ + Vec3 *p = Camera::getCameraPos(player, alpha); + TilePos tp = TilePos(p); + int t = level->getTile(tp.x, tp.y, tp.z); + if (t != 0 && Tile::tiles[t]->material->isLiquid()) + { + float hh = LiquidTile::getHeight(level->getData(tp.x, tp.y, tp.z)) - 1 / 9.0f; + float h = tp.y + 1 - hh; + if (p->y >= h) + { + t = level->getTile(tp.x, tp.y + 1, tp.z); + } + } + return t; +} \ No newline at end of file diff --git a/Minecraft.Client/Camera.h b/Minecraft.Client/Camera.h new file mode 100644 index 0000000..cccb0b2 --- /dev/null +++ b/Minecraft.Client/Camera.h @@ -0,0 +1,32 @@ +#pragma once +#include "..\Minecraft.World\FloatBuffer.h" +#include "..\Minecraft.World\IntBuffer.h" + + +class TilePos; +class Vec3; +class Player; +class Mob; + +class Camera +{ +public: + static float xPlayerOffs; + static float yPlayerOffs; + static float zPlayerOffs; + +private: +// static IntBuffer *viewport; + static FloatBuffer *modelview; + static FloatBuffer *projection; +// static FloatBuffer *position; + +public: + static float xa, ya, za, xa2, za2; + + static void prepare(shared_ptr player, bool mirror); + + static TilePos *getCameraTilePos(shared_ptr player, double alpha); + static Vec3 *getCameraPos(shared_ptr player, double alpha); + static int getBlockAt(Level *level, shared_ptr player, float alpha); +}; \ No newline at end of file diff --git a/Minecraft.Client/ChatScreen.cpp b/Minecraft.Client/ChatScreen.cpp new file mode 100644 index 0000000..b68e6ca --- /dev/null +++ b/Minecraft.Client/ChatScreen.cpp @@ -0,0 +1,89 @@ +#include "stdafx.h" +#include "ChatScreen.h" +#include "MultiplayerLocalPlayer.h" +#include "..\Minecraft.World\SharedConstants.h" +#include "..\Minecraft.World\StringHelpers.h" + +const wstring ChatScreen::allowedChars = SharedConstants::acceptableLetters; + +ChatScreen::ChatScreen() +{ + frame = 0; +} + +void ChatScreen::init() +{ + Keyboard::enableRepeatEvents(true); +} + +void ChatScreen::removed() +{ + Keyboard::enableRepeatEvents(false); +} + +void ChatScreen::tick() +{ + frame++; +} + +void ChatScreen::keyPressed(wchar_t ch, int eventKey) +{ + if (eventKey == Keyboard::KEY_ESCAPE) + { + minecraft->setScreen(NULL); + return; + } + if (eventKey == Keyboard::KEY_RETURN) + { + wstring msg = trimString(message); + if (msg.length() > 0) + { + wstring trim = trimString(message); + if (!minecraft->handleClientSideCommand(trim)) + { + minecraft->player->chat(trim); + } + } + minecraft->setScreen(NULL); + return; + } + if (eventKey == Keyboard::KEY_BACK && message.length() > 0) message = message.substr(0, message.length() - 1); + if (allowedChars.find(ch) >= 0 && message.length() < SharedConstants::maxChatLength) + { + message += ch; + } + +} + +void ChatScreen::render(int xm, int ym, float a) +{ + fill(2, height - 14, width - 2, height - 2, 0x80000000); + drawString(font, L"> " + message + (frame / 6 % 2 == 0 ? L"_" : L""), 4, height - 12, 0xe0e0e0); + + Screen::render(xm, ym, a); +} + +void ChatScreen::mouseClicked(int x, int y, int buttonNum) +{ + if (buttonNum == 0) + { + if (minecraft->gui->selectedName != L"") // 4J - was NULL comparison + { + if (message.length() > 0 && message[message.length()-1]!=L' ') + { + message += L" "; + } + message += minecraft->gui->selectedName; + unsigned int maxLength = SharedConstants::maxChatLength; + if (message.length() > maxLength) + { + message = message.substr(0, maxLength); + } + } + else + { + Screen::mouseClicked(x, y, buttonNum); + } + } + +} \ No newline at end of file diff --git a/Minecraft.Client/ChatScreen.h b/Minecraft.Client/ChatScreen.h new file mode 100644 index 0000000..d715847 --- /dev/null +++ b/Minecraft.Client/ChatScreen.h @@ -0,0 +1,25 @@ +#pragma once +#include "Screen.h" +using namespace std; + +class ChatScreen : public Screen +{ +protected: + wstring message; +private: + int frame; + +public: + ChatScreen(); //4J added + virtual void init(); + virtual void removed(); + virtual void tick(); +private: + static const wstring allowedChars; +protected: + void keyPressed(wchar_t ch, int eventKey); +public: + void render(int xm, int ym, float a); +protected: + void mouseClicked(int x, int y, int buttonNum); +}; \ No newline at end of file diff --git a/Minecraft.Client/ChestModel.cpp b/Minecraft.Client/ChestModel.cpp new file mode 100644 index 0000000..63f27c4 --- /dev/null +++ b/Minecraft.Client/ChestModel.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "ChestModel.h" +#include "ModelPart.h" + +ChestModel::ChestModel() +{ + lid = ((new ModelPart(this, 0, 0)))->setTexSize(64, 64); + lid->addBox(0.0f, -5.0f, -14.0f, 14, 5, 14, 0.0f); + lid->x = 1; + lid->y = 7; + lid->z = 15; + + lock = ((new ModelPart(this, 0, 0)))->setTexSize(64, 64); + lock->addBox(-1.0f, -2.0f, -15.0f, 2, 4, 1, 0.0f); + lock->x = 8; + lock->y = 7; + lock->z = 15; + + bottom = ((new ModelPart(this, 0, 19)))->setTexSize(64, 64); + bottom->addBox(0.0f, 0.0f, 0.0f, 14, 10, 14, 0.0f); + bottom->x = 1; + bottom->y = 6; + bottom->z = 1; + + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + lid->compile(1.0f/16.0f); + lock->compile(1.0f/16.0f); + bottom->compile(1.0f/16.0f); +} + +void ChestModel::render(bool usecompiled) +{ + lock->xRot = lid->xRot; + + lock->render(1 / 16.0f, usecompiled); + bottom->render(1 / 16.0f, usecompiled); + + // 4J - moved lid to last and added z-bias to avoid glitching caused by z-fighting between the area of overlap between the lid & bottom of the chest + glPolygonOffset(-0.3f, -0.3f); + lid->render(1 / 16.0f, usecompiled); + glPolygonOffset(0.0f, 0.0f); +} \ No newline at end of file diff --git a/Minecraft.Client/ChestModel.h b/Minecraft.Client/ChestModel.h new file mode 100644 index 0000000..416ccf7 --- /dev/null +++ b/Minecraft.Client/ChestModel.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Model.h" + +class Cube; + +class ChestModel : public Model +{ +public: + using Model::render; + + ModelPart *lid; + ModelPart *bottom; + ModelPart *lock; + + ChestModel(); + void render(bool usecompiled); +}; diff --git a/Minecraft.Client/ChestRenderer.cpp b/Minecraft.Client/ChestRenderer.cpp new file mode 100644 index 0000000..d1b5cf9 --- /dev/null +++ b/Minecraft.Client/ChestRenderer.cpp @@ -0,0 +1,105 @@ +#include "stdafx.h" +#include "ChestRenderer.h" +#include "ChestModel.h" +#include "LargeChestModel.h" +#include "ModelPart.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" + +ChestRenderer::ChestRenderer() +{ + chestModel = new ChestModel(); + largeChestModel = new LargeChestModel(); +} + +ChestRenderer::~ChestRenderer() +{ + delete chestModel; + delete largeChestModel; +} + +void ChestRenderer::render(shared_ptr _chest, double x, double y, double z, float a, bool setColor, float alpha, bool useCompiled) +{ + // 4J Convert as we aren't using a templated class + shared_ptr chest = dynamic_pointer_cast(_chest); + + int data; + + if (!chest->hasLevel()) + { + data = 0; + } + else + { + Tile *tile = chest->getTile(); + data = chest->getData(); + + if (tile != NULL && data == 0) + { + ((ChestTile *) tile)->recalcLockDir(chest->getLevel(), chest->x, chest->y, chest->z); + data = chest->getData(); + } + + chest->checkNeighbors(); + } + if (chest->n.lock() != NULL || chest->w.lock() != NULL) return; + + + ChestModel *model; + if (chest->e.lock() != NULL || chest->s.lock() != NULL) + { + model = largeChestModel; + bindTexture(TN_TILE_LARGE_CHEST); // 4J Was "/item/largechest.png" + } + else + { + model = chestModel; + bindTexture(TN_TILE_CHEST); // 4J Was "/item/chest.png" + } + + glPushMatrix(); + glEnable(GL_RESCALE_NORMAL); + //if( setColor ) glColor4f(1, 1, 1, 1); + if( setColor ) glColor4f(1, 1, 1, alpha); + glTranslatef((float) x, (float) y + 1, (float) z + 1); + glScalef(1, -1, -1); + + glTranslatef(0.5f, 0.5f, 0.5f); + int rot = 0; + if (data == 2) rot = 180; + if (data == 3) rot = 0; + if (data == 4) rot = 90; + if (data == 5) rot = -90; + + if (data == 2 && chest->e.lock() != NULL) + { + glTranslatef(1, 0, 0); + } + if (data == 5 && chest->s.lock() != NULL) + { + glTranslatef(0, 0, -1); + } + glRotatef(rot, 0, 1, 0); + glTranslatef(-0.5f, -0.5f, -0.5f); + + float open = chest->oOpenness + (chest->openness - chest->oOpenness) * a; + if (chest->n.lock() != NULL) + { + float open2 = chest->n.lock()->oOpenness + (chest->n.lock()->openness - chest->n.lock()->oOpenness) * a; + if (open2 > open) open = open2; + } + if (chest->w.lock() != NULL) + { + float open2 = chest->w.lock()->oOpenness + (chest->w.lock()->openness - chest->w.lock()->oOpenness) * a; + if (open2 > open) open = open2; + } + + open = 1 - open; + open = 1 - open * open * open; + + model->lid->xRot = -(open * PI / 2); + model->render(useCompiled); + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); + if( setColor ) glColor4f(1, 1, 1, 1); +} diff --git a/Minecraft.Client/ChestRenderer.h b/Minecraft.Client/ChestRenderer.h new file mode 100644 index 0000000..06c6bff --- /dev/null +++ b/Minecraft.Client/ChestRenderer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TileEntityRenderer.h" + +class ChestModel; + +class ChestRenderer : public TileEntityRenderer +{ +private: + ChestModel *chestModel; + ChestModel *largeChestModel; + +public: + ChestRenderer(); + ~ChestRenderer(); + + void render(shared_ptr _chest, double x, double y, double z, float a, bool setColor, float alpha=1.0f, bool useCompiled = true); // 4J added setColor param +}; diff --git a/Minecraft.Client/ChickenModel.cpp b/Minecraft.Client/ChickenModel.cpp new file mode 100644 index 0000000..21dc205 --- /dev/null +++ b/Minecraft.Client/ChickenModel.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "ChickenModel.h" +#include "ModelPart.h" + +ChickenModel::ChickenModel() : Model() +{ + int yo = 16; + head = new ModelPart(this, 0, 0); + head->addBox(-2.0f, -6.0f, -2.0f, 4, 6, 3, 0.0f); // Head + head->setPos(0, (float)(-1 + yo), -4); + + beak = new ModelPart(this, 14, 0); + beak->addBox(-2.0f, -4.0f, -4.0f, 4, 2, 2, 0.0f); // Beak + beak->setPos(0, (float)(-1 + yo), -4); + + redThing = new ModelPart(this, 14, 4); + redThing->addBox(-1.0f, -2.0f, -3.0f, 2, 2, 2, 0.0f); // Beak + redThing->setPos(0, (float)(-1 + yo), -4); + + body = new ModelPart(this, 0, 9); + body->addBox(-3.0f, -4.0f, -3.0f, 6, 8, 6, 0.0f); // Body + body->setPos(0, (float)(0 + yo), 0); + + leg0 = new ModelPart(this, 26, 0); + leg0->addBox(-1.0f, 0.0f, -3.0f, 3, 5, 3); // Leg0 + leg0->setPos(-2, (float)(3 + yo), 1); + + leg1 = new ModelPart(this, 26, 0); + leg1->addBox(-1.0f, 0.0f, -3.0f, 3, 5, 3); // Leg1 + leg1->setPos(1, (float)(3 + yo), 1); + + wing0 = new ModelPart(this, 24, 13); + wing0->addBox(0.0f, 0.0f, -3.0f, 1, 4, 6); // Wing0 + wing0->setPos(-4, (float)(-3 + yo), 0); + + wing1 = new ModelPart(this, 24, 13); + wing1->addBox(-1.0f, 0.0f, -3.0f, 1, 4, 6); // Wing1 + wing1->setPos(4, (float)(-3 + yo), 0); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + head->compile(1.0f/16.0f); + beak->compile(1.0f/16.0f); + redThing->compile(1.0f/16.0f); + body->compile(1.0f/16.0f); + leg0->compile(1.0f/16.0f); + leg1->compile(1.0f/16.0f); + wing0->compile(1.0f/16.0f); + wing1->compile(1.0f/16.0f); +} + +void ChickenModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + if (young) + { + float ss = 2; + glPushMatrix(); + glTranslatef(0, 5 * scale, 2 * scale); + head->render(scale,usecompiled); + beak->render(scale,usecompiled); + redThing->render(scale,usecompiled); + glPopMatrix(); + glPushMatrix(); + glScalef(1 / ss, 1 / ss, 1 / ss); + glTranslatef(0, 24 * scale, 0); + body->render(scale,usecompiled); + leg0->render(scale,usecompiled); + leg1->render(scale,usecompiled); + wing0->render(scale,usecompiled); + wing1->render(scale,usecompiled); + glPopMatrix(); + } + else + { + head->render(scale,usecompiled); + beak->render(scale,usecompiled); + redThing->render(scale,usecompiled); + body->render(scale,usecompiled); + leg0->render(scale,usecompiled); + leg1->render(scale,usecompiled); + wing0->render(scale,usecompiled); + wing1->render(scale,usecompiled); + } +} + +void ChickenModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + head->xRot = xRot / (float) (180 / PI); + head->yRot = yRot / (float) (180 / PI); + + beak->xRot = head->xRot; + beak->yRot = head->yRot; + + redThing->xRot = head->xRot; + redThing->yRot = head->yRot; + + body->xRot = 90 / (float) (180 / PI); + + leg0->xRot = (Mth::cos(time * 0.6662f) * 1.4f) * r; + leg1->xRot = ( Mth::cos(time * 0.6662f + PI) * 1.4f) * r; + wing0->zRot = bob; + wing1->zRot = -bob; +} diff --git a/Minecraft.Client/ChickenModel.h b/Minecraft.Client/ChickenModel.h new file mode 100644 index 0000000..9ee0858 --- /dev/null +++ b/Minecraft.Client/ChickenModel.h @@ -0,0 +1,11 @@ +#include "Model.h" + +class ChickenModel : public Model +{ +public: + ModelPart *head, *hair, *body, *leg0, *leg1, *wing0,* wing1, *beak, *redThing; + + ChickenModel(); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); +}; diff --git a/Minecraft.Client/ChickenRenderer.cpp b/Minecraft.Client/ChickenRenderer.cpp new file mode 100644 index 0000000..908ba50 --- /dev/null +++ b/Minecraft.Client/ChickenRenderer.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "ChickenRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" + +ChickenRenderer::ChickenRenderer(Model *model, float shadow) : MobRenderer(model,shadow) +{ +} + +void ChickenRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + MobRenderer::render(_mob, x, y, z, rot, a); +} + +float ChickenRenderer::getBob(shared_ptr _mob, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + + float flap = mob->oFlap+(mob->flap-mob->oFlap)*a; + float flapSpeed = mob->oFlapSpeed+(mob->flapSpeed-mob->oFlapSpeed)*a; + + return (Mth::sin(flap)+1)*flapSpeed; +} \ No newline at end of file diff --git a/Minecraft.Client/ChickenRenderer.h b/Minecraft.Client/ChickenRenderer.h new file mode 100644 index 0000000..b81c913 --- /dev/null +++ b/Minecraft.Client/ChickenRenderer.h @@ -0,0 +1,11 @@ +#pragma once +#include "MobRenderer.h" + +class ChickenRenderer : public MobRenderer +{ +public: + ChickenRenderer(Model *model, float shadow); + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); +protected: + virtual float getBob(shared_ptr _mob, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp new file mode 100644 index 0000000..99933db --- /dev/null +++ b/Minecraft.Client/Chunk.cpp @@ -0,0 +1,1045 @@ +#include "stdafx.h" +#include "Chunk.h" +#include "TileRenderer.h" +#include "TileEntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "LevelRenderer.h" + +#ifdef __PS3__ +#include "PS3\SPU_Tasks\ChunkUpdate\ChunkRebuildData.h" +#include "PS3\SPU_Tasks\ChunkUpdate\TileRenderer_SPU.h" +#include "PS3\SPU_Tasks\CompressedTile\CompressedTileStorage_SPU.h" + +#include "C4JThread_SPU.h" +#include "C4JSpursJob.h" +#endif + +int Chunk::updates = 0; + +#ifdef _LARGE_WORLDS +DWORD Chunk::tlsIdx = TlsAlloc(); + +void Chunk::CreateNewThreadStorage() +{ + unsigned char *tileIds = new unsigned char[16 * 16 * Level::maxBuildHeight]; + TlsSetValue(tlsIdx, tileIds); +} + +void Chunk::ReleaseThreadStorage() +{ + unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); + delete tileIds; +} + +unsigned char *Chunk::GetTileIdsStorage() +{ + unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); + return tileIds; +} +#else +// 4J Stu - Don't want this when multi-threaded +Tesselator *Chunk::t = Tesselator::getInstance(); +#endif +LevelRenderer *Chunk::levelRenderer; + +// TODO - 4J see how input entity vector is set up and decide what way is best to pass this to the function +Chunk::Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities, CRITICAL_SECTION& globalRenderableTileEntities_cs, int x, int y, int z, ClipChunk *clipChunk) + : globalRenderableTileEntities( &globalRenderableTileEntities ), globalRenderableTileEntities_cs(&globalRenderableTileEntities_cs) +{ + clipChunk->visible = false; + bb = NULL; + id = 0; + + this->level = level; + //this->globalRenderableTileEntities = globalRenderableTileEntities; + + assigned = false; + this->clipChunk = clipChunk; + setPos(x, y, z); +} + +void Chunk::setPos(int x, int y, int z) +{ + if(assigned && (x == this->x && y == this->y && z == this->z)) return; + + reset(); + + this->x = x; + this->y = y; + this->z = z; + xm = x + XZSIZE / 2; + ym = y + SIZE / 2; + zm = z + XZSIZE / 2; + clipChunk->xm = xm; + clipChunk->ym = ym; + clipChunk->zm = zm; + + clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); + +#if 1 + // 4J - we're not using offsetted renderlists anymore, so just set the full position of this chunk into x/y/zRenderOffs where + // it will be used directly in the renderlist of this chunk + xRenderOffs = x; + yRenderOffs = y; + zRenderOffs = z; + xRender = 0; + yRender = 0; + zRender = 0; +#else + xRenderOffs = x & 1023; + yRenderOffs = y; + zRenderOffs = z & 1023; + xRender = x - xRenderOffs; + yRender = y - yRenderOffs; + zRender = z - zRenderOffs; +#endif + + float g = 6.0f; + // 4J - changed to just set the value rather than make a new one, if we've already created storage + if( bb == NULL ) + { + bb = AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g); + } + else + { + // 4J MGH - bounds are relative to the position now, so the AABB will be setup already, either above, or from the tesselator bounds. +// bb->set(-g, -g, -g, SIZE+g, SIZE+g, SIZE+g); + } + clipChunk->aabb[0] = bb->x0 + x; + clipChunk->aabb[1] = bb->y0 + y; + clipChunk->aabb[2] = bb->z0 + z; + clipChunk->aabb[3] = bb->x1 + x; + clipChunk->aabb[4] = bb->y1 + y; + clipChunk->aabb[5] = bb->z1 + z; + + assigned = true; + + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + unsigned char refCount = levelRenderer->incGlobalChunkRefCount(x, y, z, level); +// printf("\t\t [inc] refcount %d at %d, %d, %d\n",refCount,x,y,z); + +// int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); + + // If we're the first thing to be referencing this, mark it up as dirty to get rebuilt + if( refCount == 1 ) + { +// printf("Setting %d %d %d dirty [%d]\n",x,y,z, idx); + // Chunks being made dirty in this way can be very numerous (eg the full visible area of the world at start up, or a whole edge of the world when moving). + // On account of this, don't want to stick them into our lock free queue that we would normally use for letting the render update thread know about this chunk. + // Instead, just set the flag to say this is dirty, and then pass a special value of 1 through to the lock free stack which lets that thread know that at least + // one chunk other than the ones in the stack itself have been made dirty. + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY ); +#ifdef _XBOX + PIXSetMarker(0,"Non-stack event pushed"); +#else + PIXSetMarkerDeprecated(0,"Non-stack event pushed"); +#endif + } + + LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + + +} + +void Chunk::translateToPos() +{ + glTranslatef((float)xRenderOffs, (float)yRenderOffs, (float)zRenderOffs); +} + + +Chunk::Chunk() +{ +} + +void Chunk::makeCopyForRebuild(Chunk *source) +{ + this->level = source->level; + this->x = source->x; + this->y = source->y; + this->z = source->z; + this->xRender = source->xRender; + this->yRender = source->yRender; + this->zRender = source->zRender; + this->xRenderOffs = source->xRenderOffs; + this->yRenderOffs = source->yRenderOffs; + this->zRenderOffs = source->zRenderOffs; + this->xm = source->xm; + this->ym = source->ym; + this->zm = source->zm; + this->bb = source->bb; + this->clipChunk = NULL; + this->id = source->id; + this->globalRenderableTileEntities = source->globalRenderableTileEntities; + this->globalRenderableTileEntities_cs = source->globalRenderableTileEntities_cs; +} + +void Chunk::rebuild() +{ + PIXBeginNamedEvent(0,"Rebuilding chunk %d, %d, %d", x, y, z); +#if defined __PS3__ && !defined DISABLE_SPU_CODE + rebuild_SPU(); + return; +#endif // __PS3__ + +// if (!dirty) return; + PIXBeginNamedEvent(0,"Rebuild section A"); + +#ifdef _LARGE_WORLDS + Tesselator *t = Tesselator::getInstance(); +#else + Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time +#endif + + updates++; + + int x0 = x; + int y0 = y; + int z0 = z; + int x1 = x + XZSIZE; + int y1 = y + SIZE; + int z1 = z + XZSIZE; + + LevelChunk::touchedSky = false; + +// unordered_set > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line +// renderableTileEntities.clear(); + + vector > renderableTileEntities; // 4J - added + + int r = 1; + + int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; + lists += levelRenderer->chunkLists; + + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Rebuild section B"); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 4J - optimisation begins. + + // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, + // render chunk is 16 x 16 x 16. We wouldn't have to actually get all of it if the data was ordered differently, but currently + // it is ordered by x then z then y so just getting a small range of y out of it would involve getting the whole thing into + // the cache anyway. + +#ifdef _LARGE_WORLDS + unsigned char *tileIds = GetTileIdsStorage(); +#else + static unsigned char tileIds[16 * 16 * Level::maxBuildHeight]; +#endif + byteArray tileArray = byteArray(tileIds, 16 * 16 * Level::maxBuildHeight); + level->getChunkAt(x,z)->getBlockData(tileArray); // 4J - TODO - now our data has been re-arranged, we could just extra the vertical slice of this chunk rather than the whole thing + + LevelSource *region = new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); + TileRenderer *tileRenderer = new TileRenderer(region, this->x, this->y, this->z, tileIds); + + // AP - added a caching system for Chunk::rebuild to take advantage of + // Basically we're storing of copy of the tileIDs array inside the region so that calls to Region::getTile can grab data + // more quickly from this array rather than calling CompressedTileStorage. On the Vita the total thread time spent in + // Region::getTile went from 20% to 4%. +#ifdef __PSVITA__ + int xc = x >> 4; + int zc = z >> 4; + ((Region*)region)->setCachedTiles(tileIds, xc, zc); +#endif + + // We now go through the vertical section of this level chunk that we are interested in and try and establish + // (1) if it is completely empty + // (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and + // so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version + // of this function fall into this category. By far the largest category of these are tiles in solid regions of rock. + bool empty = true; + for( int yy = y0; yy < y1; yy++ ) + { + for( int zz = 0; zz < 16; zz++ ) + { + for( int xx = 0; xx < 16; xx++ ) + { + // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 + int indexY = yy; + int offset = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + unsigned char tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ]; + if( tileId > 0 ) empty = false; + + // Don't bother trying to work out neighbours for this tile if we are at the edge of the chunk - apart from the very + // bottom of the world where we shouldn't ever be able to see + if( yy == (Level::maxBuildHeight - 1) ) continue; + if(( xx == 0 ) || ( xx == 15 )) continue; + if(( zz == 0 ) || ( zz == 15 )) continue; + + // Establish whether this tile and its neighbours are all made of rock, dirt, unbreakable tiles, or have already + // been determined to meet this criteria themselves and have a tile of 255 set. + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + // Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible + // if they are surrounded on sides other than the bottom + if( yy > 0 ) + { + int indexYMinusOne = yy - 1; + int yMinusOneOffset = 0; + if(indexYMinusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + } + int indexYPlusOne = yy + 1; + int yPlusOneOffset = 0; + if(indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + + // This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255. + tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff; + } + } + } + PIXEndNamedEvent(); + // Nothing at all to do for this chunk? + if( empty ) + { + // 4J - added - clear any renderer data associated with this + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + + delete region; + delete tileRenderer; + return; + } + // 4J - optimisation ends + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + PIXBeginNamedEvent(0,"Rebuild section C"); + Tesselator::Bounds bounds; // 4J MGH - added + { + // this was the old default clip bounds for the chunk, set in Chunk::setPos. + float g = 6.0f; + bounds.boundingBox[0] = -g; + bounds.boundingBox[1] = -g; + bounds.boundingBox[2] = -g; + bounds.boundingBox[3] = XZSIZE+g; + bounds.boundingBox[4] = SIZE+g; + bounds.boundingBox[5] = XZSIZE+g; + } + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + bool renderNextLayer = false; + bool rendered = false; + + bool started = false; + + // 4J - changed loop order here to leave y as the innermost loop for better cache performance + for (int z = z0; z < z1; z++) + { + for (int x = x0; x < x1; x++) + { + for (int y = y0; y < y1; y++) + { + // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 + int indexY = y; + int offset = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + // 4J - get tile from those copied into our local array in earlier optimisation + unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ]; + // If flagged as not visible, drop out straight away + if( tileId == 0xff ) continue; +// int tileId = region->getTile(x,y,z); + if (tileId > 0) + { + if (!started) + { + started = true; + + MemSect(31); + glNewList(lists + currentLayer, GL_COMPILE); + MemSect(0); + glPushMatrix(); + glDepthMask(true); // 4J added + t->useCompactVertices(true); // 4J added + translateToPos(); + float ss = 1.000001f; + // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex + // shader so it doesn't do anything other than translate with this matrix anyway +#if 0 + glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); + glScalef(ss, ss, ss); + glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); +#endif + t->begin(); + t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); + } + + Tile *tile = Tile::tiles[tileId]; + if (currentLayer == 0 && tile->isEntityTile()) + { + shared_ptr et = region->getTileEntity(x, y, z); + if (TileEntityRenderDispatcher::instance->hasRenderer(et)) + { + renderableTileEntities.push_back(et); + } + } + int renderLayer = tile->getRenderLayer(); + + if (renderLayer != currentLayer) + { + renderNextLayer = true; + } + else if (renderLayer == currentLayer) + { + rendered |= tileRenderer->tesselateInWorld(tile, x, y, z); + } + } + } + } + } + +#ifdef __PSVITA__ + if( currentLayer==0 ) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); + } +#endif + + if (started) + { +#ifdef __PSVITA__ + // AP - make sure we don't attempt to render chunks without cutout geometry + if( t->getCutOutFound() ) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); + } +#endif + t->end(); + bounds.addBounds(t->bounds); // 4J MGH - added + glPopMatrix(); + glEndList(); + t->useCompactVertices(false); // 4J added + t->offset(0, 0, 0); + } + else + { + rendered = false; + } + + if (rendered) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + } + else + { + // 4J - added - clear any renderer data associated with this unused list + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + if((currentLayer==0)&&(!renderNextLayer)) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY1); + RenderManager.CBuffClear(lists + 1); + break; + } + } + + // 4J MGH - added this to take the bound from the value calc'd in the tesselator + if( bb ) + { + bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], + bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); + } + + delete tileRenderer; + delete region; + + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Rebuild section D"); + + // 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now + // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index + // as is used for global flags) +#if 1 + int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); + EnterCriticalSection(globalRenderableTileEntities_cs); + if( renderableTileEntities.size() ) + { + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. + // We need to flag any that we don't need any more to be removed, keep those that we do, and add any new ones + + // First pass - flag everything already existing to be removed + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + + // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); + if( it2 == it->second.end() ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + else + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); + } + } + } + else + { + // Easy case - nothing already existing for this chunk. Add them all in. + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + } + } + else + { + // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + } + } + LeaveCriticalSection(globalRenderableTileEntities_cs); + PIXEndNamedEvent(); +#else + // Find the removed ones: + + // 4J - original code for this section: + /* + Set newTileEntities = new HashSet(); + newTileEntities.addAll(renderableTileEntities); + newTileEntities.removeAll(oldTileEntities); + globalRenderableTileEntities.addAll(newTileEntities); + + oldTileEntities.removeAll(renderableTileEntities); + globalRenderableTileEntities.removeAll(oldTileEntities); + */ + + + unordered_set > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); + + AUTO_VAR(endIt, oldTileEntities.end()); + for( unordered_set >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) + { + newTileEntities.erase(*it); + } + + // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) + + EnterCriticalSection(globalRenderableTileEntities_cs); + endIt = newTileEntities.end(); + for( unordered_set >::iterator it = newTileEntities.begin(); it != endIt; it++ ) + { + globalRenderableTileEntities->push_back(*it); + } + + // 4J - All these new things added to globalRenderableTileEntities + + AUTO_VAR(endItRTE, renderableTileEntities.end()); + for( vector >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) + { + oldTileEntities.erase(*it); + } + // 4J - oldTileEntities is now the removed items + vector >::iterator it = globalRenderableTileEntities->begin(); + while( it != globalRenderableTileEntities->end() ) + { + if( oldTileEntities.find(*it) != oldTileEntities.end() ) + { + it = globalRenderableTileEntities->erase(it); + } + else + { + ++it; + } + } + + LeaveCriticalSection(globalRenderableTileEntities_cs); +#endif + + // 4J - These removed items are now also removed from globalRenderableTileEntities + + if( LevelChunk::touchedSky ) + { + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + else + { + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); + PIXEndNamedEvent(); + return; + +} + + +#ifdef __PS3__ +ChunkRebuildData g_rebuildDataIn __attribute__((__aligned__(16))); +ChunkRebuildData g_rebuildDataOut __attribute__((__aligned__(16))); +TileCompressData_SPU g_tileCompressDataIn __attribute__((__aligned__(16))); +unsigned char* g_tileCompressDataOut = (unsigned char*)&g_rebuildDataIn.m_tileIds; + +#define TILE_RENDER_SPU + + +void RunSPURebuild() +{ + + static C4JSpursJobQueue::Port p("C4JSpursJob_ChunkUpdate"); + C4JSpursJob_CompressedTile tileJob(&g_tileCompressDataIn,g_tileCompressDataOut); + C4JSpursJob_ChunkUpdate chunkJob(&g_rebuildDataIn, &g_rebuildDataOut); + + if(g_rebuildDataIn.m_currentLayer == 0) // only need to create the tiles on the first layer + { + p.submitJob(&tileJob); + p.submitSync(); + } + + p.submitJob(&chunkJob); + p.waitForCompletion(); + + assert(g_rebuildDataIn.m_x0 == g_rebuildDataOut.m_x0); +} + +void Chunk::rebuild_SPU() +{ + +// if (!dirty) return; + Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time + updates++; + + int x0 = x; + int y0 = y; + int z0 = z; + int x1 = x + SIZE; + int y1 = y + SIZE; + int z1 = z + SIZE; + + LevelChunk::touchedSky = false; + +// unordered_set > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line +// renderableTileEntities.clear(); + + vector > renderableTileEntities; // 4J - added + +// List newTileEntities = new ArrayList(); +// newTileEntities.clear(); +// renderableTileEntities.clear(); + + int r = 1; + + Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); + TileRenderer tileRenderer(®ion); + + int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; + lists += levelRenderer->chunkLists; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 4J - optimisation begins. + + // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, + // render chunk is 16 x 16 x 16. We wouldn't have to actually get all of it if the data was ordered differently, but currently + // it is ordered by x then z then y so just getting a small range of y out of it would involve getting the whole thing into + // the cache anyway. + ChunkRebuildData* pOutData = NULL; + g_rebuildDataIn.buildForChunk(®ion, level, x0, y0, z0); + + Tesselator::Bounds bounds; + { + // this was the old default clip bounds for the chunk, set in Chunk::setPos. + float g = 6.0f; + bounds.boundingBox[0] = -g; + bounds.boundingBox[1] = -g; + bounds.boundingBox[2] = -g; + bounds.boundingBox[3] = SIZE+g; + bounds.boundingBox[4] = SIZE+g; + bounds.boundingBox[5] = SIZE+g; + } + + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + bool rendered = false; + + { + glNewList(lists + currentLayer, GL_COMPILE); + MemSect(0); + glPushMatrix(); + glDepthMask(true); // 4J added + t->useCompactVertices(true); // 4J added + translateToPos(); + float ss = 1.000001f; + // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex + // shader so it doesn't do anything other than translate with this matrix anyway + #if 0 + glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); + glScalef(ss, ss, ss); + glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); + #endif + t->begin(); + t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); + } + + g_rebuildDataIn.copyFromTesselator(); + intArray_SPU tesselatorArray((unsigned int*)g_rebuildDataIn.m_tesselator.m_PPUArray); + g_rebuildDataIn.m_tesselator._array = &tesselatorArray; + g_rebuildDataIn.m_currentLayer = currentLayer; + #ifdef TILE_RENDER_SPU + g_tileCompressDataIn.setForChunk(®ion, x0, y0, z0); + RunSPURebuild(); + g_rebuildDataOut.storeInTesselator(); + pOutData = &g_rebuildDataOut; + #else + g_rebuildDataIn.disableUnseenTiles(); + TileRenderer_SPU *pTileRenderer = new TileRenderer_SPU(&g_rebuildDataIn); + g_rebuildDataIn.tesselateAllTiles(pTileRenderer); + g_rebuildDataIn.storeInTesselator(); + pOutData = &g_rebuildDataIn; + #endif + if(pOutData->m_flags & ChunkRebuildData::e_flag_Rendered) + rendered = true; + + // 4J - changed loop order here to leave y as the innermost loop for better cache performance + for (int z = z0; z < z1; z++) + { + for (int x = x0; x < x1; x++) + { + for (int y = y0; y < y1; y++) + { + // 4J - get tile from those copied into our local array in earlier optimisation + unsigned char tileId = pOutData->getTile(x,y,z); + if (tileId > 0) + { + if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) + { + shared_ptr et = region.getTileEntity(x, y, z); + if (TileEntityRenderDispatcher::instance->hasRenderer(et)) + { + renderableTileEntities.push_back(et); + } + } + int flags = pOutData->getFlags(x,y,z); + if(flags & ChunkRebuildData::e_flag_SPURenderCodeMissing) + { + + Tile *tile = Tile::tiles[tileId]; + int renderLayer = tile->getRenderLayer(); + + if (renderLayer != currentLayer) + { + // renderNextLayer = true; + } + else if (renderLayer == currentLayer) + { + //if(currentLayer == 0) + // numRenderedLayer0++; + rendered |= tileRenderer.tesselateInWorld(tile, x, y, z); + } + } + } + } + } + } + + + { + t->end(); + bounds.addBounds(t->bounds); + glPopMatrix(); + glEndList(); + t->useCompactVertices(false); // 4J added + t->offset(0, 0, 0); + } + if (rendered) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + } + else + { + // 4J - added - clear any renderer data associated with this unused list + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + + } + + if( bb ) + { + bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], + bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); + } + + + if(pOutData->m_flags & ChunkRebuildData::e_flag_TouchedSky) + LevelChunk::touchedSky = true; + + + // 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now + // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index + // as is used for global flags) +#if 1 + int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); + EnterCriticalSection(globalRenderableTileEntities_cs); + if( renderableTileEntities.size() ) + { + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. + // We need to flag any that we don't need any more to be removed, keep those that we do, and add any new ones + + // First pass - flag everything already existing to be removed + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + + // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); + if( it2 == it->second.end() ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + else + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); + } + } + } + else + { + // Easy case - nothing already existing for this chunk. Add them all in. + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + } + } + else + { + // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + } + } + LeaveCriticalSection(globalRenderableTileEntities_cs); +#else + // Find the removed ones: + + // 4J - original code for this section: + /* + Set newTileEntities = new HashSet(); + newTileEntities.addAll(renderableTileEntities); + newTileEntities.removeAll(oldTileEntities); + globalRenderableTileEntities.addAll(newTileEntities); + + oldTileEntities.removeAll(renderableTileEntities); + globalRenderableTileEntities.removeAll(oldTileEntities); + */ + + + unordered_set > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); + + AUTO_VAR(endIt, oldTileEntities.end()); + for( unordered_set >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) + { + newTileEntities.erase(*it); + } + + // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) + + EnterCriticalSection(globalRenderableTileEntities_cs); + endIt = newTileEntities.end(); + for( unordered_set >::iterator it = newTileEntities.begin(); it != endIt; it++ ) + { + globalRenderableTileEntities.push_back(*it); + } + + // 4J - All these new things added to globalRenderableTileEntities + + AUTO_VAR(endItRTE, renderableTileEntities.end()); + for( vector >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) + { + oldTileEntities.erase(*it); + } + // 4J - oldTileEntities is now the removed items + vector >::iterator it = globalRenderableTileEntities->begin(); + while( it != globalRenderableTileEntities->end() ) + { + if( oldTileEntities.find(*it) != oldTileEntities.end() ) + { + it = globalRenderableTileEntities->erase(it); + } + else + { + ++it; + } + } + + LeaveCriticalSection(globalRenderableTileEntities_cs); +#endif + + // 4J - These removed items are now also removed from globalRenderableTileEntities + + if( LevelChunk::touchedSky ) + { + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + else + { + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); + return; + +} +#endif // _PS3_ + + +float Chunk::distanceToSqr(shared_ptr player) const +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym); + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +float Chunk::squishedDistanceToSqr(shared_ptr player) +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym) * 2; + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +void Chunk::reset() +{ + if( assigned ) + { + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); + assigned = false; +// printf("\t\t [dec] refcount %d at %d, %d, %d\n",refCount,x,y,z); + if( refCount == 0 ) + { + int lists = levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; + if(lists >= 0) + { + lists += levelRenderer->chunkLists; + for (int i = 0; i < 2; i++) + { + // 4J - added - clear any renderer data associated with this unused list + RenderManager.CBuffClear(lists + i); + } + levelRenderer->setGlobalChunkFlags(x, y, z, level, 0); + } + } + LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + } + + clipChunk->visible = false; +} + +void Chunk::_delete() +{ + reset(); + level = NULL; +} + +int Chunk::getList(int layer) +{ + if (!clipChunk->visible) return -1; + + int lists = levelRenderer->getGlobalIndexForChunk(x, y, z,level) * 2; + lists += levelRenderer->chunkLists; + + bool empty = levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); + if (!empty) return lists + layer; + return -1; +} + +void Chunk::cull(Culler *culler) +{ + clipChunk->visible = culler->isVisible(bb); +} + +void Chunk::renderBB() +{ +// glCallList(lists + 2); // 4J - removed - TODO put back in +} + +bool Chunk::isEmpty() +{ + if (!levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED)) return false; + return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTYBOTH); +} + +void Chunk::setDirty() +{ + // 4J - not used, but if this starts being used again then we'll need to investigate how best to handle it. + __debugbreak(); + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); +} + +void Chunk::clearDirty() +{ + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); +#ifdef _CRITICAL_CHUNKS + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_CRITICAL); +#endif +} + +Chunk::~Chunk() +{ + delete bb; +} + +bool Chunk::emptyFlagSet(int layer) +{ + return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); +} diff --git a/Minecraft.Client/Chunk.h b/Minecraft.Client/Chunk.h new file mode 100644 index 0000000..f794715 --- /dev/null +++ b/Minecraft.Client/Chunk.h @@ -0,0 +1,87 @@ +#pragma once +#include "AllowAllCuller.h" +#include "Tesselator.h" +#include "..\Minecraft.World\ArrayWithLength.h" +#include "LevelRenderer.h" + +class Level; +class TileEntity; +class Entity; +using namespace std; + +class ClipChunk +{ +public: + Chunk *chunk; + int globalIdx; + bool visible; + float aabb[6]; + int xm, ym, zm; +}; + +class Chunk +{ +private: + static const int XZSIZE = LevelRenderer::CHUNK_XZSIZE; + static const int SIZE = LevelRenderer::CHUNK_SIZE; + +public: + Level *level; + static LevelRenderer *levelRenderer; +private: +#ifndef _LARGE_WORLDS + static Tesselator *t; +#else + static DWORD tlsIdx; +public: + static void CreateNewThreadStorage(); + static void ReleaseThreadStorage(); + static unsigned char *GetTileIdsStorage(); +#endif + +public: + static int updates; + + int x, y, z; + int xRender, yRender, zRender; + int xRenderOffs, yRenderOffs, zRenderOffs; + + int xm, ym, zm; + AABB *bb; + ClipChunk *clipChunk; + + int id; +//public: +// vector > renderableTileEntities; // 4J - removed + +private: + LevelRenderer::rteMap *globalRenderableTileEntities; + CRITICAL_SECTION *globalRenderableTileEntities_cs; + bool assigned; +public: + Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities, CRITICAL_SECTION &globalRenderableTileEntities_cs, int x, int y, int z, ClipChunk *clipChunk); + Chunk(); + + void setPos(int x, int y, int z); +private: + void translateToPos(); +public: + void makeCopyForRebuild(Chunk *source); + void rebuild(); +#ifdef __PS3__ + void rebuild_SPU(); +#endif // __PS3__ + float distanceToSqr(shared_ptr player) const; + float squishedDistanceToSqr(shared_ptr player); + void reset(); + void _delete(); + + int getList(int layer); + void cull(Culler *culler); + void renderBB() ; + bool isEmpty(); + void setDirty(); + void clearDirty(); // 4J added + bool emptyFlagSet(int layer); + ~Chunk(); +}; diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp new file mode 100644 index 0000000..b80eaed --- /dev/null +++ b/Minecraft.Client/ClientConnection.cpp @@ -0,0 +1,3347 @@ +#include "stdafx.h" +#include "ClientConnection.h" +#include "MultiPlayerLevel.h" +#include "MultiPlayerLocalPlayer.h" +#include "StatsCounter.h" +#include "ReceivingLevelScreen.h" +#include "RemotePlayer.h" +#include "DisconnectedScreen.h" +#include "TakeAnimationParticle.h" +#include "CritParticle.h" +#include "User.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.entity.npc.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.global.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.trading.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\Minecraft.World\net.minecraft.world.food.h" +#include "..\Minecraft.World\SharedConstants.h" +#include "..\Minecraft.World\AABB.h" +#include "..\Minecraft.World\Pos.h" +#include "..\Minecraft.World\Socket.h" +#include "Minecraft.h" +#include "ProgressRenderer.h" +#include "LevelRenderer.h" +#include "Options.h" +#include "MinecraftServer.h" +#include "ClientConstants.h" +#include "..\Minecraft.World\SoundTypes.h" +#include "TexturePackRepository.h" +#ifdef _XBOX +#include "Common\XUI\XUI_Scene_Trading.h" +#else +#include "Common\UI\UI.h" +#endif +#ifdef __PS3__ +#include "PS3/Network/SonyVoiceChat.h" +#endif +#include "DLCTexturePack.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#include "..\Minecraft.World\GenericStats.h" +#endif + +ClientConnection::ClientConnection(Minecraft *minecraft, const wstring& ip, int port) +{ + // 4J Stu - No longer used as we use the socket version below. + assert(FALSE); +#if 0 + // 4J - added initiliasers + random = new Random(); + done = false; + level = false; + started = false; + + this->minecraft = minecraft; + + Socket *socket; + if( gNetworkManager.IsHost() ) + { + socket = new Socket(); // 4J - Local connection + } + else + { + socket = new Socket(ip); // 4J - Connection over xrnm - hardcoded IP at present + } + createdOk = socket->createdOk; + if( createdOk ) + { + connection = new Connection(socket, L"Client", this); + } + else + { + connection = NULL; + delete socket; + } +#endif +} + +ClientConnection::ClientConnection(Minecraft *minecraft, Socket *socket, int iUserIndex /*= -1*/) +{ + // 4J - added initiliasers + random = new Random(); + done = false; + level = NULL; + started = false; + savedDataStorage = new SavedDataStorage(NULL); + maxPlayers = 20; + + this->minecraft = minecraft; + + if( iUserIndex < 0 ) + { + m_userIndex = ProfileManager.GetPrimaryPad(); + } + else + { + m_userIndex = iUserIndex; + } + + if( socket == NULL ) + { + socket = new Socket(); // 4J - Local connection + } + + createdOk = socket->createdOk; + if( createdOk ) + { + connection = new Connection(socket, L"Client", this); + } + else + { + connection = NULL; + // TODO 4J Stu - This will cause issues since the session player owns the socket + //delete socket; + } +} + +ClientConnection::~ClientConnection() +{ + delete connection; + delete random; + delete savedDataStorage; +} + +void ClientConnection::tick() +{ + if (!done) connection->tick(); + connection->flush(); +} + +INetworkPlayer *ClientConnection::getNetworkPlayer() +{ + if( connection != NULL && connection->getSocket() != NULL) return connection->getSocket()->getPlayer(); + else return NULL; +} + +void ClientConnection::handleLogin(shared_ptr packet) +{ + if (done) return; + + PlayerUID OnlineXuid; + ProfileManager.GetXUID(m_userIndex,&OnlineXuid,true); // online xuid + MOJANG_DATA *pMojangData = NULL; + + if(!g_NetworkManager.IsLocalGame()) + { + pMojangData=app.GetMojangDataForXuid(OnlineXuid); + } + + if(!g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCLoginReceived * 100)/ (eCCConnected)); + } + + // 4J-PB - load the local player skin (from the global title user storage area) if there is one + // the primary player on the host machine won't have a qnet player from the socket + INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer(); + int iUserID=-1; + + if( m_userIndex == ProfileManager.GetPrimaryPad() ) + { + iUserID=m_userIndex; + + TelemetryManager->SetMultiplayerInstanceId(packet->m_multiplayerInstanceId); + } + else + { + if(!networkPlayer->IsGuest() && networkPlayer->IsLocal()) + { + // find the pad number of this local player + for(int i=0;iwchSkin[0]!=0L) + { + wstring wstr=pMojangData->wchSkin; + // check the file is not already in + bRes=app.IsFileInMemoryTextures(wstr); + if(!bRes) + { +#ifdef _XBOX + C4JStorage::ETMSStatus eTMSStatus; + eTMSStatus=StorageManager.ReadTMSFile(iUserID,C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic,pMojangData->wchSkin,&pBuffer, &dwSize); + + bRes=(eTMSStatus==C4JStorage::ETMSStatus_Idle); +#endif + } + + if(bRes) + { + app.AddMemoryTextureFile(wstr,pBuffer,dwSize); + } + } + + // a cloak? + if(pMojangData->wchCape[0]!=0L) + { + wstring wstr=pMojangData->wchCape; + // check the file is not already in + bRes=app.IsFileInMemoryTextures(wstr); + if(!bRes) + { +#ifdef _XBOX + C4JStorage::ETMSStatus eTMSStatus; + eTMSStatus=StorageManager.ReadTMSFile(iUserID,C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic,pMojangData->wchCape,&pBuffer, &dwSize); + bRes=(eTMSStatus==C4JStorage::ETMSStatus_Idle); +#endif + } + + if(bRes) + { + app.AddMemoryTextureFile(wstr,pBuffer,dwSize); + } + } + } + + // If we're online, read the banned game list + app.ReadBannedList(iUserID); + // mark the level as not checked against banned levels - it'll be checked once the level starts + app.SetBanListCheck(iUserID,false); + } + + if( m_userIndex == ProfileManager.GetPrimaryPad() ) + { + if( app.GetTutorialMode() ) + { + minecraft->gameMode = new FullTutorialMode(ProfileManager.GetPrimaryPad(), minecraft, this); + } + // check if we're in the trial version + else if(ProfileManager.IsFullVersion()==false) + { + minecraft->gameMode = new TrialMode(ProfileManager.GetPrimaryPad(), minecraft, this); + } + else + { + MemSect(13); + minecraft->gameMode = new ConsoleGameMode(ProfileManager.GetPrimaryPad(), minecraft, this); + MemSect(0); + } + + + Level *dimensionLevel = minecraft->getLevel( packet->dimension ); + if( dimensionLevel == NULL ) + { + level = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty); + + // 4J Stu - We want to share the SavedDataStorage between levels + int otherDimensionId = packet->dimension == 0 ? -1 : 0; + Level *activeLevel = minecraft->getLevel(otherDimensionId); + if( activeLevel != NULL ) + { + // Don't need to delete it here as it belongs to a client connection while will delete it when it's done + //if( level->savedDataStorage != NULL ) delete level->savedDataStorage; + level->savedDataStorage = activeLevel->savedDataStorage; + } + + app.DebugPrintf("ClientConnection - DIFFICULTY --- %d\n",packet->difficulty); + level->difficulty = packet->difficulty; // 4J Added + level->isClientSide = true; + minecraft->setLevel(level); + } + + minecraft->player->setPlayerIndex( packet->m_playerIndex ); + minecraft->player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) ); + minecraft->player->setCustomCape( app.GetPlayerCapeId(m_userIndex) ); + + + minecraft->createPrimaryLocalPlayer(ProfileManager.GetPrimaryPad()); + + minecraft->player->dimension = packet->dimension; + //minecraft->setScreen(new ReceivingLevelScreen(this)); + minecraft->player->entityId = packet->clientVersion; + + BYTE networkSmallId = getSocket()->getSmallId(); + app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges); + minecraft->player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); + + // Assume all privileges are on, so that the first message we see only indicates things that have been turned off + unsigned int startingPrivileges = 0; + Player::enableAllPlayerPrivileges(startingPrivileges,true); + + if(networkPlayer->IsHost()) + { + Player::setPlayerGamePrivilege(startingPrivileges, Player::ePlayerGamePrivilege_HOST,1); + } + + displayPrivilegeChanges(minecraft->player,startingPrivileges); + + // update the debugoptions + app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),app.GetGameSettingsDebugMask(-1,true)); + } + else + { + // 4J-PB - this isn't the level we want + //level = (MultiPlayerLevel *)minecraft->level; + level = (MultiPlayerLevel *)minecraft->getLevel( packet->dimension ); + shared_ptr player; + + if(level==NULL) + { + int otherDimensionId = packet->dimension == 0 ? -1 : 0; + MultiPlayerLevel *activeLevel = minecraft->getLevel(otherDimensionId); + + if(activeLevel == NULL) + { + otherDimensionId = packet->dimension == 0 ? 1 : (packet->dimension == -1 ? 1 : -1); + activeLevel = minecraft->getLevel(otherDimensionId); + } + + MultiPlayerLevel *dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->seed, GameType::byId(packet->gameType), false, false, packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty); + + dimensionLevel->savedDataStorage = activeLevel->savedDataStorage; + + dimensionLevel->difficulty = packet->difficulty; // 4J Added + dimensionLevel->isClientSide = true; + level = dimensionLevel; + // 4J Stu - At time of writing ProfileManager.GetGamertag() does not always return the correct name, + // if sign-ins are turned off while the player signed in. Using the qnetPlayer instead. + // need to have a level before create extra local player + MultiPlayerLevel *levelpassedin=(MultiPlayerLevel *)level; + player = minecraft->createExtraLocalPlayer(m_userIndex, networkPlayer->GetOnlineName(), m_userIndex, packet->dimension, this,levelpassedin); + + // need to have a player before the setlevel + shared_ptr lastPlayer = minecraft->player; + minecraft->player = minecraft->localplayers[m_userIndex]; + minecraft->setLevel(level); + minecraft->player = lastPlayer; + } + else + { + player = minecraft->createExtraLocalPlayer(m_userIndex, networkPlayer->GetOnlineName(), m_userIndex, packet->dimension, this); + } + + + //level->addClientConnection( this ); + player->dimension = packet->dimension; + player->entityId = packet->clientVersion; + + player->setPlayerIndex( packet->m_playerIndex ); + player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) ); + player->setCustomCape( app.GetPlayerCapeId(m_userIndex) ); + + + BYTE networkSmallId = getSocket()->getSmallId(); + app.UpdatePlayerInfo(networkSmallId, packet->m_playerIndex, packet->m_uiGamePrivileges); + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); + + // Assume all privileges are on, so that the first message we see only indicates things that have been turned off + unsigned int startingPrivileges = 0; + Player::enableAllPlayerPrivileges(startingPrivileges,true); + + displayPrivilegeChanges(minecraft->localplayers[m_userIndex],startingPrivileges); + } + + maxPlayers = packet->maxPlayers; + + // need to have a player before the setLocalCreativeMode + shared_ptr lastPlayer = minecraft->player; + minecraft->player = minecraft->localplayers[m_userIndex]; + ((MultiPlayerGameMode *)minecraft->localgameModes[m_userIndex])->setLocalMode(GameType::byId(packet->gameType)); + minecraft->player = lastPlayer; + + // make sure the UI offsets for this player are set correctly + if(iUserID!=-1) + { + ui.UpdateSelectedItemPos(iUserID); + } + + TelemetryManager->RecordLevelStart(m_userIndex, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->getLevel(packet->dimension)->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount()); +} + +void ClientConnection::handleAddEntity(shared_ptr packet) +{ + double x = packet->x / 32.0; + double y = packet->y / 32.0; + double z = packet->z / 32.0; + shared_ptr e; + boolean setRot = true; + + // 4J-PB - replacing this massive if nest with switch + switch(packet->type) + { + case AddEntityPacket::MINECART_RIDEABLE: + e = shared_ptr( new Minecart(level, x, y, z, Minecart::RIDEABLE) ); + break; + case AddEntityPacket::MINECART_CHEST: + e = shared_ptr( new Minecart(level, x, y, z, Minecart::CHEST) ); + break; + + case AddEntityPacket::MINECART_FURNACE: + e = shared_ptr( new Minecart(level, x, y, z, Minecart::FURNACE) ); + break; + + case AddEntityPacket::FISH_HOOK: + { + // 4J Stu - Brought forward from 1.4 to be able to drop XP from fishing + shared_ptr owner = getEntity(packet->data); + + // 4J - check all local players to find match + if( owner == NULL ) + { + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->data ) + { + + owner = minecraft->localplayers[i]; + break; + } + } + } + } + shared_ptr player = dynamic_pointer_cast(owner); + if (player != NULL) + { + shared_ptr hook = shared_ptr( new FishingHook(level, x, y, z, player) ); + e = hook; + // 4J Stu - Move the player->fishing out of the ctor as we cannot reference 'this' + player->fishing = hook; + } + packet->data = 0; + } + break; + case AddEntityPacket::ARROW: + e = shared_ptr( new Arrow(level, x, y, z) ); + break; + case AddEntityPacket::SNOWBALL: + e = shared_ptr( new Snowball(level, x, y, z) ); + break; + case AddEntityPacket::ITEM_FRAME: + { + int ix=(int) x; + int iy=(int) y; + int iz = (int) z; + app.DebugPrintf("ClientConnection ITEM_FRAME xyz %d,%d,%d\n",ix,iy,iz); + } + e = shared_ptr(new ItemFrame(level, (int) x, (int) y, (int) z, packet->data)); + packet->data = 0; + setRot = false; + break; + case AddEntityPacket::THROWN_ENDERPEARL: + e = shared_ptr( new ThrownEnderpearl(level, x, y, z) ); + break; + case AddEntityPacket::EYEOFENDERSIGNAL: + e = shared_ptr( new EyeOfEnderSignal(level, x, y, z) ); + break; + case AddEntityPacket::FIREBALL: + e = shared_ptr( new Fireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + break; + case AddEntityPacket::SMALL_FIREBALL: + e = shared_ptr( new SmallFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + break; + case AddEntityPacket::DRAGON_FIRE_BALL: + e = shared_ptr( new DragonFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + break; + case AddEntityPacket::EGG: + e = shared_ptr( new ThrownEgg(level, x, y, z) ); + break; + case AddEntityPacket::THROWN_POTION: + e = shared_ptr( new ThrownPotion(level, x, y, z, packet->data) ); + packet->data = 0; + break; + case AddEntityPacket::THROWN_EXPBOTTLE: + e = shared_ptr( new ThrownExpBottle(level, x, y, z) ); + packet->data = 0; + break; + case AddEntityPacket::BOAT: + e = shared_ptr( new Boat(level, x, y, z) ); + break; + case AddEntityPacket::PRIMED_TNT: + e = shared_ptr( new PrimedTnt(level, x, y, z) ); + break; + case AddEntityPacket::ENDER_CRYSTAL: + e = shared_ptr( new EnderCrystal(level, x, y, z) ); + break; + case AddEntityPacket::ITEM: + e = shared_ptr( new ItemEntity(level, x, y, z) ); + break; + case AddEntityPacket::FALLING: + e = shared_ptr( new FallingTile(level, x, y, z, packet->data & 0xFFFF, packet->data >> 16) ); + packet->data = 0; + break; + + + + } + /* if (packet->type == AddEntityPacket::MINECART_RIDEABLE) e = shared_ptr( new Minecart(level, x, y, z, Minecart::RIDEABLE) ); + if (packet->type == AddEntityPacket::MINECART_CHEST) e = shared_ptr( new Minecart(level, x, y, z, Minecart::CHEST) ); + if (packet->type == AddEntityPacket::MINECART_FURNACE) e = shared_ptr( new Minecart(level, x, y, z, Minecart::FURNACE) ); + if (packet->type == AddEntityPacket::FISH_HOOK) + { + // 4J Stu - Brought forward from 1.4 to be able to drop XP from fishing + shared_ptr owner = getEntity(packet->data); + + // 4J - check all local players to find match + if( owner == NULL ) + { + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->data ) + { + + owner = minecraft->localplayers[i]; + break; + } + } + } + } + shared_ptr player = dynamic_pointer_cast(owner); + if (player != NULL) + { + shared_ptr hook = shared_ptr( new FishingHook(level, x, y, z, player) ); + e = hook; + // 4J Stu - Move the player->fishing out of the ctor as we cannot reference 'this' + player->fishing = hook; + } + packet->data = 0; + } + + if (packet->type == AddEntityPacket::ARROW) e = shared_ptr( new Arrow(level, x, y, z) ); + if (packet->type == AddEntityPacket::SNOWBALL) e = shared_ptr( new Snowball(level, x, y, z) ); + if (packet->type == AddEntityPacket::THROWN_ENDERPEARL) e = shared_ptr( new ThrownEnderpearl(level, x, y, z) ); + if (packet->type == AddEntityPacket::EYEOFENDERSIGNAL) e = shared_ptr( new EyeOfEnderSignal(level, x, y, z) ); + if (packet->type == AddEntityPacket::FIREBALL) + { + e = shared_ptr( new Fireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + } + if (packet->type == AddEntityPacket::SMALL_FIREBALL) + { + e = shared_ptr( new SmallFireball(level, x, y, z, packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0) ); + packet->data = 0; + } + if (packet->type == AddEntityPacket::EGG) e = shared_ptr( new ThrownEgg(level, x, y, z) ); + if (packet->type == AddEntityPacket::THROWN_POTION) + { + e = shared_ptr( new ThrownPotion(level, x, y, z, packet->data) ); + packet->data = 0; + } + if (packet->type == AddEntityPacket::THROWN_EXPBOTTLE) + { + e = shared_ptr( new ThrownExpBottle(level, x, y, z) ); + packet->data = 0; + } + if (packet->type == AddEntityPacket::BOAT) e = shared_ptr( new Boat(level, x, y, z) ); + if (packet->type == AddEntityPacket::PRIMED_TNT) e = shared_ptr( new PrimedTnt(level, x, y, z) ); + if (packet->type == AddEntityPacket::ENDER_CRYSTAL) e = shared_ptr( new EnderCrystal(level, x, y, z) ); + if (packet->type == AddEntityPacket::FALLING_SAND) e = shared_ptr( new FallingTile(level, x, y, z, Tile::sand->id) ); + if (packet->type == AddEntityPacket::FALLING_GRAVEL) e = shared_ptr( new FallingTile(level, x, y, z, Tile::gravel->id) ); + if (packet->type == AddEntityPacket::FALLING_EGG) e = shared_ptr( new FallingTile(level, x, y, z, Tile::dragonEgg_Id) ); + + */ + + if (e != NULL) + { + e->xp = packet->x; + e->yp = packet->y; + e->zp = packet->z; + + float yRot = packet->yRot * 360 / 256.0f; + float xRot = packet->xRot * 360 / 256.0f; + e->yRotp = packet->yRot; + e->xRotp = packet->xRot; + + if (setRot) + { + e->yRot = 0.0f; + e->xRot = 0.0f; + } + + vector > *subEntities = e->getSubEntities(); + if (subEntities != NULL) + { + int offs = packet->id - e->entityId; + //for (int i = 0; i < subEntities.length; i++) + for(AUTO_VAR(it, subEntities->begin()); it != subEntities->end(); ++it) + { + (*it)->entityId += offs; + //subEntities[i].entityId += offs; + //System.out.println(subEntities[i].entityId); + } + } + + // Note - not doing this move for frame, as the ctor for these objects does some adjustments on the position based on direction to move the object out slightly from what it is attached to, and this just overwrites it + if( packet->type != AddEntityPacket::ITEM_FRAME ) + { + e->absMoveTo(x,y,z,yRot,xRot); + } + e->entityId = packet->id; + level->putEntity(packet->id, e); + + if (packet->data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 + { + + if (packet->type == AddEntityPacket::ARROW) + { + shared_ptr owner = getEntity(packet->data); + + // 4J - check all local players to find match + if( owner == NULL ) + { + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->data ) + { + owner = minecraft->localplayers[i]; + break; + } + } + } + } + + if (dynamic_pointer_cast(owner) != NULL) + { + dynamic_pointer_cast(e)->owner = dynamic_pointer_cast(owner); + } + } + + e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0); + } + } + +} + +void ClientConnection::handleAddExperienceOrb(shared_ptr packet) +{ + shared_ptr e = shared_ptr( new ExperienceOrb(level, packet->x / 32.0, packet->y / 32.0, packet->z / 32.0, packet->value) ); + e->xp = packet->x; + e->yp = packet->y; + e->zp = packet->z; + e->yRot = 0; + e->xRot = 0; + e->entityId = packet->id; + level->putEntity(packet->id, e); +} + +void ClientConnection::handleAddGlobalEntity(shared_ptr packet) +{ + double x = packet->x / 32.0; + double y = packet->y / 32.0; + double z = packet->z / 32.0; + shared_ptr e;// = nullptr; + if (packet->type == AddGlobalEntityPacket::LIGHTNING) e = shared_ptr( new LightningBolt(level, x, y, z) ); + if (e != NULL) + { + e->xp = packet->x; + e->yp = packet->y; + e->zp = packet->z; + e->yRot = 0; + e->xRot = 0; + e->entityId = packet->id; + level->addGlobalEntity(e); + } +} + +void ClientConnection::handleAddPainting(shared_ptr packet) +{ + shared_ptr painting = shared_ptr( new Painting(level, packet->x, packet->y, packet->z, packet->dir, packet->motive) ); + level->putEntity(packet->id, painting); +} + +void ClientConnection::handleSetEntityMotion(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + e->lerpMotion(packet->xa / 8000.0, packet->ya / 8000.0, packet->za / 8000.0); +} + +void ClientConnection::handleSetEntityData(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e != NULL && packet->getUnpackedData() != NULL) + { + e->getEntityData()->assignValues(packet->getUnpackedData()); + } +} + +void ClientConnection::handleAddPlayer(shared_ptr packet) +{ + // Some remote players could actually be local players that are already added + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + // need to use the XUID here + PlayerUID playerXUIDOnline = INVALID_XUID, playerXUIDOffline = INVALID_XUID; + ProfileManager.GetXUID(idx,&playerXUIDOnline,true); + ProfileManager.GetXUID(idx,&playerXUIDOffline,false); + if( (playerXUIDOnline != INVALID_XUID && ProfileManager.AreXUIDSEqual(playerXUIDOnline,packet->xuid) ) || + (playerXUIDOffline != INVALID_XUID && ProfileManager.AreXUIDSEqual(playerXUIDOffline,packet->xuid) ) ) + { + app.DebugPrintf("AddPlayerPacket received with XUID of local player\n"); + return; + } + } + + double x = packet->x / 32.0; + double y = packet->y / 32.0; + double z = packet->z / 32.0; + float yRot = packet->yRot * 360 / 256.0f; + float xRot = packet->xRot * 360 / 256.0f; + shared_ptr player = shared_ptr( new RemotePlayer(minecraft->level, packet->name) ); + player->xo = player->xOld = player->xp = packet->x; + player->yo = player->yOld = player->yp = packet->y; + player->zo = player->zOld = player->zp = packet->z; + player->xRotp = packet->xRot; + player->yRotp = packet->yRot; + player->yHeadRot = packet->yHeadRot * 360 / 256.0f; + player->setXuid(packet->xuid); + +#ifdef _DURANGO + // On Durango request player display name from network manager + INetworkPlayer *networkPlayer = g_NetworkManager.GetPlayerByXuid(player->getXuid()); + if (networkPlayer != NULL) player->displayName = networkPlayer->GetDisplayName(); +#else + // On all other platforms display name is just gamertag so don't check with the network manager + player->displayName = player->name; +#endif + + // printf("\t\t\t\t%d: Add player\n",packet->id,packet->yRot); + + int item = packet->carriedItem; + if (item == 0) + { + player->inventory->items[player->inventory->selected] = shared_ptr(); // NULL; + } + else + { + player->inventory->items[player->inventory->selected] = shared_ptr( new ItemInstance(item, 1, 0) ); + } + player->absMoveTo(x, y, z, yRot, xRot); + + player->setPlayerIndex( packet->m_playerIndex ); + player->setCustomSkin( packet->m_skinId ); + player->setCustomCape( packet->m_capeId ); + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_uiGamePrivileges); + + if(!player->customTextureUrl.empty() && player->customTextureUrl.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl)) + { + if( minecraft->addPendingClientTextureRequest(player->customTextureUrl) ) + { + app.DebugPrintf("Client sending TextureAndGeometryPacket to get custom skin %ls for player %ls\n",player->customTextureUrl.c_str(), player->name.c_str()); + + send(shared_ptr( new TextureAndGeometryPacket(player->customTextureUrl,NULL,0) ) ); + } + } + else if(!player->customTextureUrl.empty() && app.IsFileInMemoryTextures(player->customTextureUrl)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(player->customTextureUrl,NULL,0); + } + + app.DebugPrintf("Custom skin for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl.c_str()); + + if(!player->customTextureUrl2.empty() && player->customTextureUrl2.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl2)) + { + if( minecraft->addPendingClientTextureRequest(player->customTextureUrl2) ) + { + app.DebugPrintf("Client sending texture packet to get custom cape %ls for player %ls\n",player->customTextureUrl2.c_str(), player->name.c_str()); + send(shared_ptr( new TexturePacket(player->customTextureUrl2,NULL,0) ) ); + } + } + else if(!player->customTextureUrl2.empty() && app.IsFileInMemoryTextures(player->customTextureUrl2)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(player->customTextureUrl2,NULL,0); + } + + app.DebugPrintf("Custom cape for player %ls is %ls\n",player->name.c_str(),player->customTextureUrl2.c_str()); + + level->putEntity(packet->id, player); + + vector > *unpackedData = packet->getUnpackedData(); + if (unpackedData != NULL) + { + player->getEntityData()->assignValues(unpackedData); + } + +} + +void ClientConnection::handleTeleportEntity(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + e->xp = packet->x; + e->yp = packet->y; + e->zp = packet->z; + double x = e->xp / 32.0; + double y = e->yp / 32.0 + 1 / 64.0f; + double z = e->zp / 32.0; + // 4J - make sure xRot stays within -90 -> 90 range + int ixRot = packet->xRot; + if( ixRot >= 128 ) ixRot -= 256; + float yRot = packet->yRot * 360 / 256.0f; + float xRot = ixRot * 360 / 256.0f; + e->yRotp = packet->yRot; + e->xRotp = ixRot; + +// printf("\t\t\t\t%d: Teleport to %d (lerp to %f)\n",packet->id,packet->yRot,yRot); + e->lerpTo(x, y, z, yRot, xRot, 3); +} + +void ClientConnection::handleMoveEntity(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + e->xp += packet->xa; + e->yp += packet->ya; + e->zp += packet->za; + double x = e->xp / 32.0; + // 4J - The original code did not add the 1/64.0f like the teleport above did, which caused minecarts to fall through the ground + double y = e->yp / 32.0 + 1 / 64.0f; + double z = e->zp / 32.0; + // 4J - have changed rotation to be relative here too + e->yRotp += packet->yRot; + e->xRotp += packet->xRot; + float yRot = ( e->yRotp * 360 ) / 256.0f; + float xRot = ( e->xRotp * 360 ) / 256.0f; +// float yRot = packet->hasRot ? packet->yRot * 360 / 256.0f : e->yRot; +// float xRot = packet->hasRot ? packet->xRot * 360 / 256.0f : e->xRot; + e->lerpTo(x, y, z, yRot, xRot, 3); +} + +void ClientConnection::handleRotateMob(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + float yHeadRot = packet->yHeadRot * 360 / 256.f; + e->setYHeadRot(yHeadRot); +} + +void ClientConnection::handleMoveEntitySmall(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + e->xp += packet->xa; + e->yp += packet->ya; + e->zp += packet->za; + double x = e->xp / 32.0; + // 4J - The original code did not add the 1/64.0f like the teleport above did, which caused minecarts to fall through the ground + double y = e->yp / 32.0 + 1 / 64.0f; + double z = e->zp / 32.0; + // 4J - have changed rotation to be relative here too + e->yRotp += packet->yRot; + e->xRotp += packet->xRot; + float yRot = ( e->yRotp * 360 ) / 256.0f; + float xRot = ( e->xRotp * 360 ) / 256.0f; +// float yRot = packet->hasRot ? packet->yRot * 360 / 256.0f : e->yRot; +// float xRot = packet->hasRot ? packet->xRot * 360 / 256.0f : e->xRot; + e->lerpTo(x, y, z, yRot, xRot, 3); +} + +void ClientConnection::handleRemoveEntity(shared_ptr packet) +{ + for (int i = 0; i < packet->ids.length; i++) + { + level->removeEntity(packet->ids[i]); + } +} + +void ClientConnection::handleMovePlayer(shared_ptr packet) +{ + shared_ptr player = minecraft->localplayers[m_userIndex]; //minecraft->player; + + double x = player->x; + double y = player->y; + double z = player->z; + float yRot = player->yRot; + float xRot = player->xRot; + + if (packet->hasPos) + { + x = packet->x; + y = packet->y; + z = packet->z; + } + if (packet->hasRot) + { + yRot = packet->yRot; + xRot = packet->xRot; + } + + player->ySlideOffset = 0; + player->xd = player->yd = player->zd = 0; + player->absMoveTo(x, y, z, yRot, xRot); + packet->x = player->x; + packet->y = player->bb->y0; + packet->z = player->z; + packet->yView = player->y; + connection->send(packet); + if (!started) + { + + if(!g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCConnected * 100)/ (eCCConnected)); + } + player->xo = player->x; + player->yo = player->y; + player->zo = player->z; + // 4J - added setting xOld/yOld/zOld here too, as otherwise at the start of the game we interpolate the player position from the origin to wherever its first position really is + player->xOld = player->x; + player->yOld = player->y; + player->zOld = player->z; + + started = true; + minecraft->setScreen(NULL); + + // Fix for #105852 - TU12: Content: Gameplay: Local splitscreen Players are spawned at incorrect places after re-joining previously saved and loaded "Mass Effect World". + // Move this check from Minecraft::createExtraLocalPlayer + // 4J-PB - can't call this when this function is called from the qnet thread (GetGameStarted will be false) + if(app.GetGameStarted()) + { + ui.CloseUIScenes(m_userIndex); + } + } + +} + +// 4J Added +void ClientConnection::handleChunkVisibilityArea(shared_ptr packet) +{ + for(int z = packet->m_minZ; z <= packet->m_maxZ; ++z) + for(int x = packet->m_minX; x <= packet->m_maxX; ++x) + level->setChunkVisible(x, z, true); +} + +void ClientConnection::handleChunkVisibility(shared_ptr packet) +{ + level->setChunkVisible(packet->x, packet->z, packet->visible); +} + +void ClientConnection::handleChunkTilesUpdate(shared_ptr packet) +{ + // 4J - changed to encode level in packet + MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; + if( dimensionLevel ) + { + PIXBeginNamedEvent(0,"Handle chunk tiles update"); + LevelChunk *lc = dimensionLevel->getChunk(packet->xc, packet->zc); + int xo = packet->xc * 16; + int zo = packet->zc * 16; + // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us + // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water. + // This is quite expensive to do, so only consider unsharing if this tile setting is going to actually + // change something + bool forcedUnshare = false; + for (int i = 0; i < packet->count; i++) + { + int pos = packet->positions[i]; + int tile = packet->blocks[i] & 0xff; + int data = packet->data[i]; + + + int x = (pos >> 12) & 15; + int z = (pos >> 8) & 15; + int y = ((pos) & 255); + + // If this is going to actually change a tile, we'll need to unshare + int prevTile = lc->getTile(x, y, z); + if( ( tile != prevTile && !forcedUnshare ) ) + { + PIXBeginNamedEvent(0,"Chunk data unsharing\n"); + dimensionLevel->unshareChunkAt(xo,zo); + PIXEndNamedEvent(); + forcedUnshare = true; + } + + // 4J - Changes now that lighting is done at the client side of things... + // Note - the java version now calls the doSetTileAndData method from the level here rather than the levelchunk, which ultimately ends up + // calling checkLight for the altered tile. For us this doesn't always work as when sharing tile data between a local server & client, the + // tile might not be considered to be being changed on the client as the server already has changed the shared data, and so the checkLight + // doesn't happen. Hence doing an explicit checkLight here instead. + lc->setTileAndData(x, y, z, tile, data); + dimensionLevel->checkLight(x + xo, y, z + zo); + + dimensionLevel->clearResetRegion(x + xo, y, z + zo, x + xo, y, z + zo); + + // Don't bother setting this to dirty if it isn't going to visually change - we get a lot of + // water changing from static to dynamic for instance + if(!( ( ( prevTile == Tile::water_Id ) && ( tile == Tile::calmWater_Id ) ) || + ( ( prevTile == Tile::calmWater_Id ) && ( tile == Tile::water_Id ) ) || + ( ( prevTile == Tile::lava_Id ) && ( tile == Tile::calmLava_Id ) ) || + ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::calmLava_Id ) ) || + ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::lava_Id ) ) ) ) + { + dimensionLevel->setTilesDirty(x + xo, y, z + zo, x + xo, y, z + zo); + } + + // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray + // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT + dimensionLevel->removeUnusedTileEntitiesInRegion(xo + x, y, zo + z, xo + x+1, y+1, zo + z+1); + } + PIXBeginNamedEvent(0,"Chunk data sharing\n"); + dimensionLevel->shareChunkAt(xo,zo); // 4J - added - only shares if chunks are same on server & client + PIXEndNamedEvent(); + + PIXEndNamedEvent(); + } +} + +void ClientConnection::handleBlockRegionUpdate(shared_ptr packet) +{ + // 4J - changed to encode level in packet + MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; + if( dimensionLevel ) + { + PIXBeginNamedEvent(0,"Handle block region update"); + + int y1 = packet->y + packet->ys; + if(packet->bIsFullChunk) + { + y1 = Level::maxBuildHeight; + if(packet->buffer.length > 0) LevelChunk::reorderBlocksAndDataToXZY(packet->y, packet->xs, packet->ys, packet->zs, &packet->buffer); + } + dimensionLevel->clearResetRegion(packet->x, packet->y, packet->z, packet->x + packet->xs - 1, y1 - 1, packet->z + packet->zs - 1); + + // Only full chunks send lighting information now - added flag to end of this call + dimensionLevel->setBlocksAndData(packet->x, packet->y, packet->z, packet->xs, packet->ys, packet->zs, packet->buffer, packet->bIsFullChunk); + +// OutputDebugString("END BRU\n"); + + // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray + // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT + dimensionLevel->removeUnusedTileEntitiesInRegion(packet->x, packet->y, packet->z, packet->x + packet->xs, y1, packet->z + packet->zs ); + + // If this is a full packet for a chunk, make sure that the cache now considers that it has data for this chunk - this is used to determine whether to bother + // rendering mobs or not, so we don't have them in crazy positions before the data is there + if( packet->bIsFullChunk ) + { + dimensionLevel->dataReceivedForChunk( packet->x >> 4, packet->z >> 4 ); + } + PIXEndNamedEvent(); + } +} + +void ClientConnection::handleTileUpdate(shared_ptr packet) +{ + // 4J added - using a block of 255 to signify that this is a packet for destroying a tile, where we need to inform the level renderer that we are about to do so. + // This is used in creative mode as the point where a tile is first destroyed at the client end of things. Packets formed like this are potentially sent from + // ServerPlayerGameMode::destroyBlock + bool destroyTilePacket = false; + if( packet->block == 255 ) + { + packet->block = 0; + destroyTilePacket = true; + } + // 4J - changed to encode level in packet + MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->levels[packet->levelIdx]; + if( dimensionLevel ) + { + PIXBeginNamedEvent(0,"Handle tile update"); + + if( g_NetworkManager.IsHost() ) + { + // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us + // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water. + // This is quite expensive to do, so only consider unsharing if this tile setting is going to actually + // change something + int prevTile = dimensionLevel->getTile(packet->x, packet->y, packet->z); + int prevData = dimensionLevel->getData(packet->x, packet->y, packet->z); + if( packet->block != prevTile || packet->data != prevData ) + { + PIXBeginNamedEvent(0,"Chunk data unsharing\n"); + dimensionLevel->unshareChunkAt(packet->x,packet->z); + PIXEndNamedEvent(); + } + } + + // 4J - In creative mode, we don't update the tile locally then get it confirmed by the server - the first point that we know we are about to destroy a tile is here. Let + // the rendering side of thing know so we can synchronise collision with async render data upates. + if( destroyTilePacket ) + { + minecraft->levelRenderer->destroyedTileManager->destroyingTileAt(dimensionLevel, packet->x, packet->y, packet->z); + } + + PIXBeginNamedEvent(0,"Setting data\n"); + bool tileWasSet = dimensionLevel->doSetTileAndData(packet->x, packet->y, packet->z, packet->block, packet->data); + + PIXEndNamedEvent(); + + // 4J - remove any tite entities in this region which are associated with a tile that is now no longer a tile entity. Without doing this we end up with stray + // tile entities kicking round, which leads to a bug where chests can't be properly placed again in a location after (say) a chest being removed by TNT + dimensionLevel->removeUnusedTileEntitiesInRegion(packet->x, packet->y, packet->z, packet->x+1, packet->y+1, packet->z+1 ); + + PIXBeginNamedEvent(0,"Sharing data\n"); + dimensionLevel->shareChunkAt(packet->x,packet->z); // 4J - added - only shares if chunks are same on server & client + PIXEndNamedEvent(); + + PIXEndNamedEvent(); + } +} + +void ClientConnection::handleDisconnect(shared_ptr packet) +{ + connection->close(DisconnectPacket::eDisconnect_Kicked); + done = true; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->connectionDisconnected( m_userIndex , packet->reason ); + app.SetDisconnectReason( packet->reason ); + + app.SetAction(m_userIndex,eAppAction_ExitWorld,(void *)TRUE); + //minecraft->setLevel(NULL); + //minecraft->setScreen(new DisconnectedScreen(L"disconnect.disconnected", L"disconnect.genericReason", &packet->reason)); + +} + +void ClientConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) +{ + if (done) return; + done = true; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->connectionDisconnected( m_userIndex , reason ); + + // 4J Stu - TU-1 hotfix + // Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost + // In the (now unlikely) event that the host connections times out, allow the player to save their game + if(g_NetworkManager.IsHost() && + (reason == DisconnectPacket::eDisconnect_TimeOut || reason == DisconnectPacket::eDisconnect_Overflow) && + m_userIndex == ProfileManager.GetPrimaryPad() && + !MinecraftServer::saveOnExitAnswered() ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXITING_GAME, IDS_GENERIC_ERROR, uiIDA, 1, ProfileManager.GetPrimaryPad(),&ClientConnection::HostDisconnectReturned,NULL, app.GetStringTable()); + } + else + { + app.SetAction(m_userIndex,eAppAction_ExitWorld,(void *)TRUE); + } + + //minecraft->setLevel(NULL); + //minecraft->setScreen(new DisconnectedScreen(L"disconnect.lost", reason, reasonObjects)); +} + +void ClientConnection::sendAndDisconnect(shared_ptr packet) +{ + if (done) return; + connection->send(packet); + connection->sendAndQuit(); +} + +void ClientConnection::send(shared_ptr packet) +{ + if (done) return; + connection->send(packet); +} + +void ClientConnection::handleTakeItemEntity(shared_ptr packet) +{ + shared_ptr from = getEntity(packet->itemId); + shared_ptr to = dynamic_pointer_cast(getEntity(packet->playerId)); + + // 4J - the original game could assume that if getEntity didn't find the player, it must be the local player. We + // need to search all local players + bool isLocalPlayer = false; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->playerId ) + { + isLocalPlayer = true; + to = minecraft->localplayers[i]; + break; + } + } + } + + if (to == NULL) + { + // Don't know if this should ever really happen, but seems safest to try and remove the entity that has been collected even if we can't + // create a particle as we don't know what really collected it + level->removeEntity(packet->itemId); + return; + } + + if (from != NULL) + { + // If this is a local player, then we only want to do processing for it if this connection is associated with the player it is for. In + // particular, we don't want to remove the item entity until we are processing it for the right connection, or else we won't have a valid + // "from" reference if we've already removed the item for an earlier processed connection + if( isLocalPlayer ) + { + shared_ptr player = dynamic_pointer_cast(to); + + // 4J Stu - Fix for #10213 - UI: Local clients cannot progress through the tutorial normally. + // We only send this packet once if many local players can see the event, so make sure we update + // the tutorial for the player that actually picked up the item + int playerPad = player->GetXboxPad(); + + if( minecraft->localgameModes[playerPad] != NULL ) + { + // 4J-PB - add in the XP orb sound + if(from->GetType() == eTYPE_EXPERIENCEORB) + { + float fPitch=((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f; + app.DebugPrintf("XP Orb with pitch %f\n",fPitch); + level->playSound(from, eSoundType_RANDOM_ORB, 0.2f, fPitch); + } + else + { + level->playSound(from, eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f); + } + + minecraft->particleEngine->add( shared_ptr( new TakeAnimationParticle(minecraft->level, from, to, -0.5f) ) ); + level->removeEntity(packet->itemId); + } + else + { + // Don't know if this should ever really happen, but seems safest to try and remove the entity that has been collected even if it + // somehow isn't an itementity + level->removeEntity(packet->itemId); + } + } + else + { + level->playSound(from, eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f); + minecraft->particleEngine->add( shared_ptr( new TakeAnimationParticle(minecraft->level, from, to, -0.5f) ) ); + level->removeEntity(packet->itemId); + } + } + +} + +void ClientConnection::handleChat(shared_ptr packet) +{ + wstring message; + int iPos; + bool displayOnGui = true; + + wstring playerDisplayName = L""; + wstring sourceDisplayName = L""; + + // On platforms other than Xbox One this just sets display name to gamertag + if (packet->m_stringArgs.size() >= 1) playerDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[0]); + if (packet->m_stringArgs.size() >= 2) sourceDisplayName = GetDisplayNameByGamertag(packet->m_stringArgs[1]); + + switch(packet->m_messageType) + { + case ChatPacket::e_ChatBedOccupied: + message = app.GetString(IDS_TILE_BED_OCCUPIED); + break; + case ChatPacket::e_ChatBedNoSleep: + message = app.GetString(IDS_TILE_BED_NO_SLEEP); + break; + case ChatPacket::e_ChatBedNotValid: + message = app.GetString(IDS_TILE_BED_NOT_VALID); + break; + case ChatPacket::e_ChatBedNotSafe: + message = app.GetString(IDS_TILE_BED_NOTSAFE); + break; + case ChatPacket::e_ChatBedPlayerSleep: + message=app.GetString(IDS_TILE_BED_PLAYERSLEEP); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + case ChatPacket::e_ChatBedMeSleep: + message=app.GetString(IDS_TILE_BED_MESLEEP); + break; + case ChatPacket::e_ChatPlayerJoinedGame: + message=app.GetString(IDS_PLAYER_JOINED); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + case ChatPacket::e_ChatPlayerLeftGame: + message=app.GetString(IDS_PLAYER_LEFT); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + case ChatPacket::e_ChatPlayerKickedFromGame: + message=app.GetString(IDS_PLAYER_KICKED); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + case ChatPacket::e_ChatCannotPlaceLava: + displayOnGui = false; + app.SetGlobalXuiAction(eAppAction_DisplayLavaMessage); + break; + case ChatPacket::e_ChatDeathInFire: + message=app.GetString(IDS_DEATH_INFIRE); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathOnFire: + message=app.GetString(IDS_DEATH_ONFIRE); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathLava: + message=app.GetString(IDS_DEATH_LAVA); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathInWall: + message=app.GetString(IDS_DEATH_INWALL); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathDrown: + message=app.GetString(IDS_DEATH_DROWN); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathStarve: + message=app.GetString(IDS_DEATH_STARVE); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathCactus: + message=app.GetString(IDS_DEATH_CACTUS); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathFall: + message=app.GetString(IDS_DEATH_FALL); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathOutOfWorld: + message=app.GetString(IDS_DEATH_OUTOFWORLD); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathGeneric: + message=app.GetString(IDS_DEATH_GENERIC); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathExplosion: + message=app.GetString(IDS_DEATH_EXPLOSION); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathMagic: + message=app.GetString(IDS_DEATH_MAGIC); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathAnvil: + message=app.GetString(IDS_DEATH_FALLING_ANVIL); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathFallingBlock: + message=app.GetString(IDS_DEATH_FALLING_TILE); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathDragonBreath: + message=app.GetString(IDS_DEATH_DRAGON_BREATH); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatDeathMob: + message=app.GetString(IDS_DEATH_MOB); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + break; + case ChatPacket::e_ChatDeathPlayer: + message=app.GetString(IDS_DEATH_PLAYER); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + message = replaceAll(message,L"{*SOURCE*}",sourceDisplayName); + break; + case ChatPacket::e_ChatDeathArrow: + message=app.GetString(IDS_DEATH_ARROW); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*SOURCE*}",sourceDisplayName); + } + else + { + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatDeathFireball: + message=app.GetString(IDS_DEATH_FIREBALL); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*SOURCE*}",packet->m_stringArgs[1]); + } + else + { + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatDeathThrown: + message=app.GetString(IDS_DEATH_THROWN); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*SOURCE*}",sourceDisplayName); + } + else + { + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatDeathIndirectMagic: + message=app.GetString(IDS_DEATH_INDIRECT_MAGIC); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*SOURCE*}",sourceDisplayName); + } + else + { + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatDeathThorns: + message=app.GetString(IDS_DEATH_THORNS); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*SOURCE*}",sourceDisplayName); + } + else + { + message = replaceAll(message,L"{*SOURCE*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatPlayerEnteredEnd: + message=app.GetString(IDS_PLAYER_ENTERED_END); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + case ChatPacket::e_ChatPlayerLeftEnd: + message=app.GetString(IDS_PLAYER_LEFT_END); + iPos=message.find(L"%s"); + message.replace(iPos,2,playerDisplayName); + break; + + case ChatPacket::e_ChatPlayerMaxEnemies: + message=app.GetString(IDS_MAX_ENEMIES_SPAWNED); + break; + // Spawn eggs + case ChatPacket::e_ChatPlayerMaxVillagers: + message=app.GetString(IDS_MAX_VILLAGERS_SPAWNED); + break; + case ChatPacket::e_ChatPlayerMaxPigsSheepCows: + message=app.GetString(IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED); + break; + case ChatPacket::e_ChatPlayerMaxChickens: + message=app.GetString(IDS_MAX_CHICKENS_SPAWNED); + break; + case ChatPacket::e_ChatPlayerMaxSquid: + message=app.GetString(IDS_MAX_SQUID_SPAWNED); + break; + case ChatPacket::e_ChatPlayerMaxMooshrooms: + message=app.GetString(IDS_MAX_MOOSHROOMS_SPAWNED); + break; + case ChatPacket::e_ChatPlayerMaxWolves: + message=app.GetString(IDS_MAX_WOLVES_SPAWNED); + break; + + // Breeding + case ChatPacket::e_ChatPlayerMaxBredPigsSheepCows: + message=app.GetString(IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED); + break; + case ChatPacket::e_ChatPlayerMaxBredChickens: + message=app.GetString(IDS_MAX_CHICKENS_BRED); + break; + case ChatPacket::e_ChatPlayerMaxBredMooshrooms: + message=app.GetString(IDS_MAX_MUSHROOMCOWS_BRED); + break; + + case ChatPacket::e_ChatPlayerMaxBredWolves: + message=app.GetString(IDS_MAX_WOLVES_BRED); + break; + + // can't shear the mooshroom + case ChatPacket::e_ChatPlayerCantShearMooshroom: + message=app.GetString(IDS_CANT_SHEAR_MOOSHROOM); + break; + + // Paintings/Item Frames + case ChatPacket::e_ChatPlayerMaxHangingEntities: + message=app.GetString(IDS_MAX_HANGINGENTITIES); + break; + // Enemy spawn eggs in peaceful + case ChatPacket::e_ChatPlayerCantSpawnInPeaceful: + message=app.GetString(IDS_CANT_SPAWN_IN_PEACEFUL); + break; + + // Enemy spawn eggs in peaceful + case ChatPacket::e_ChatPlayerMaxBoats: + message=app.GetString(IDS_MAX_BOATS); + break; + + case ChatPacket::e_ChatCommandTeleportSuccess: + message=app.GetString(IDS_COMMAND_TELEPORT_SUCCESS); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + if(packet->m_intArgs[0] == eTYPE_SERVERPLAYER) + { + message = replaceAll(message,L"{*DESTINATION*}",packet->m_stringArgs[1]); + } + else + { + message = replaceAll(message,L"{*DESTINATION*}",app.getEntityName((eINSTANCEOF)packet->m_intArgs[0])); + } + break; + case ChatPacket::e_ChatCommandTeleportMe: + message=app.GetString(IDS_COMMAND_TELEPORT_ME); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + case ChatPacket::e_ChatCommandTeleportToMe: + message=app.GetString(IDS_COMMAND_TELEPORT_TO_ME); + message = replaceAll(message,L"{*PLAYER*}",playerDisplayName); + break; + + default: + message = playerDisplayName; + break; + } + + // flag that a message is a death message + bool bIsDeathMessage = (packet->m_messageType>=ChatPacket::e_ChatDeathInFire) && (packet->m_messageType<=ChatPacket::e_ChatDeathDragonBreath); + + if( displayOnGui ) minecraft->gui->addMessage(message,m_userIndex, bIsDeathMessage); +} + +void ClientConnection::handleAnimate(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + if (packet->action == AnimatePacket::SWING) + { + shared_ptr player = dynamic_pointer_cast(e); + if(player != NULL) player->swing(); + } + else if (packet->action == AnimatePacket::HURT) + { + e->animateHurt(); + } + else if (packet->action == AnimatePacket::WAKE_UP) + { + shared_ptr player = dynamic_pointer_cast(e); + if(player != NULL) player->stopSleepInBed(false, false, false); + } + else if (packet->action == AnimatePacket::RESPAWN) + { + } + else if (packet->action == AnimatePacket::CRITICAL_HIT) + { + shared_ptr critParticle = shared_ptr( new CritParticle(minecraft->level, e) ); + critParticle->CritParticlePostConstructor(); + minecraft->particleEngine->add( critParticle ); + } + else if (packet->action == AnimatePacket::MAGIC_CRITICAL_HIT) + { + shared_ptr critParticle = shared_ptr( new CritParticle(minecraft->level, e, eParticleType_magicCrit) ); + critParticle->CritParticlePostConstructor(); + minecraft->particleEngine->add(critParticle); + } + else if (packet->action == AnimatePacket::EAT && dynamic_pointer_cast(e) != NULL) + { + } + +} + +void ClientConnection::handleEntityActionAtPosition(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + if (packet->action == EntityActionAtPositionPacket::START_SLEEP) + { + shared_ptr player = dynamic_pointer_cast(e); + player->startSleepInBed(packet->x, packet->y, packet->z); + + if( player == minecraft->localplayers[m_userIndex] ) + { + TelemetryManager->RecordEnemyKilledOrOvercome(m_userIndex, 0, player->y, 0, 0, 0, 0, eTelemetryInGame_UseBed); + } + } +} + +void ClientConnection::handlePreLogin(shared_ptr packet) +{ +// printf("Client: handlePreLogin\n"); +#if 1 + // 4J - Check that we can play with all the players already in the game who have Friends-Only UGC set + BOOL canPlay = TRUE; + BOOL canPlayLocal = TRUE; + BOOL isAtLeastOneFriend = g_NetworkManager.IsHost(); + BOOL isFriendsWithHost = TRUE; + BOOL cantPlayContentRestricted = FALSE; + + if(!g_NetworkManager.IsHost()) + { + // set the game host settings + app.SetGameHostOption(eGameHostOption_All,packet->m_serverSettings); + + // 4J-PB - if we go straight in from the menus via an invite, we won't have the DLC info + if(app.GetTMSGlobalFileListRead()==false) + { + app.SetTMSAction(ProfileManager.GetPrimaryPad(),eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + } + } + +#ifdef _XBOX + if(!g_NetworkManager.IsHost() && !app.GetGameHostOption(eGameHostOption_FriendsOfFriends)) + { + if(m_userIndex == ProfileManager.GetPrimaryPad() ) + { + for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(ProfileManager.IsSignedIn(m_userIndex) && ProfileManager.IsGuest(idx)) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + else + { + PlayerUID playerXuid = INVALID_XUID; + if( ProfileManager.IsSignedInLive(idx) ) + { + ProfileManager.GetXUID(idx,&playerXuid,true); + } + if( playerXuid != INVALID_XUID ) + { + // Is this user friends with the host player? + BOOL result; + DWORD error; + error = XUserAreUsersFriends(idx,&packet->m_playerXuids[packet->m_hostIndex],1,&result,NULL); + if(error == ERROR_SUCCESS && result != TRUE) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + } + } + if(!canPlay) break; + } + } + else + { + if(ProfileManager.IsSignedIn(m_userIndex) && ProfileManager.IsGuest(m_userIndex)) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + else + { + PlayerUID playerXuid = INVALID_XUID; + if( ProfileManager.IsSignedInLive(m_userIndex) ) + { + ProfileManager.GetXUID(m_userIndex,&playerXuid,true); + } + if( playerXuid != INVALID_XUID ) + { + // Is this user friends with the host player? + BOOL result; + DWORD error; + error = XUserAreUsersFriends(m_userIndex,&packet->m_playerXuids[packet->m_hostIndex],1,&result,NULL); + if(error == ERROR_SUCCESS && result != TRUE) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + } + } + } + } + + if( canPlay ) + { + for(DWORD i = 0; i < packet->m_dwPlayerCount; ++i) + { + bool localPlayer = false; + for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if( ProfileManager.IsSignedInLive(idx) ) + { + // need to use the XUID here + PlayerUID playerXUID = INVALID_XUID; + if( !ProfileManager.IsGuest( idx ) ) + { + // Guest don't have an offline XUID as they cannot play offline, so use their online one + ProfileManager.GetXUID(idx,&playerXUID,true); + } + if( ProfileManager.AreXUIDSEqual(playerXUID,packet->m_playerXuids[i]) ) localPlayer = true; + } + else if (ProfileManager.IsSignedIn(idx)) + { + // If we aren't signed into live then they have to be a local player + localPlayer = true; + } + } + if(!localPlayer) + { + // First check our own permissions to see if we can play with this player + if(m_userIndex == ProfileManager.GetPrimaryPad() ) + { + canPlayLocal = ProfileManager.CanViewPlayerCreatedContent(m_userIndex,false,&packet->m_playerXuids[i],1); + + // 4J Stu - Everyone joining needs to have at least one friend in the game + // Local players are implied friends + if( isAtLeastOneFriend != TRUE ) + { + BOOL result; + DWORD error; + for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if( ProfileManager.IsSignedIn(idx) && !ProfileManager.IsGuest(idx) ) + { + error = XUserAreUsersFriends(idx,&packet->m_playerXuids[i],1,&result,NULL); + if(error == ERROR_SUCCESS && result == TRUE) isAtLeastOneFriend = TRUE; + } + } + } + } + else + { + // Friends with the primary player on this system + isAtLeastOneFriend = true; + + canPlayLocal = ProfileManager.CanViewPlayerCreatedContent(m_userIndex,true,&packet->m_playerXuids[i],1); + } + + // If we can play with them, then check if they can play with us + if( canPlayLocal && ( packet->m_friendsOnlyBits & (1<m_playerXuids[i],1,&result,NULL); + if(error == ERROR_SUCCESS) canPlay &= result; + } + if(!canPlay) break; + } + } + if(!canPlay || !canPlayLocal) break; + } + } + } +#else + // TODO - handle this kind of things for non-360 platforms + canPlay = TRUE; + canPlayLocal = TRUE; + isAtLeastOneFriend = TRUE; + cantPlayContentRestricted= FALSE; + +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) + + if(!g_NetworkManager.IsHost() && !app.GetGameHostOption(eGameHostOption_FriendsOfFriends)) + { + bool bChatRestricted=false; + + ProfileManager.GetChatAndContentRestrictions(m_userIndex,true,&bChatRestricted,NULL,NULL); + + // Chat restricted orbis players can still play online +#ifndef __ORBIS__ + canPlay = !bChatRestricted; +#endif + + if(m_userIndex == ProfileManager.GetPrimaryPad() ) + { + // Is this user friends with the host player? + bool isFriend = true; + unsigned int friendCount = 0; +#ifdef __PS3__ + int ret = sceNpBasicGetFriendListEntryCount(&friendCount); +#elif defined __PSVITA__ + sce::Toolkit::NP::Utilities::Future friendList; + int ret = -1; + if(!CGameNetworkManager::usingAdhocMode()) // we don't need to be friends in PSN for adhoc mode + { + int ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, false); + if(ret == SCE_TOOLKIT_NP_SUCCESS) + { + if( friendList.hasResult() ) + { + friendCount = friendList.get()->size(); + } + } + } +#else // __ORBIS__ + + sce::Toolkit::NP::Utilities::Future friendList; + + sce::Toolkit::NP::FriendInfoRequest requestParam; + memset(&requestParam,0,sizeof(requestParam)); + requestParam.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_ALL; + requestParam.limit = 0; + requestParam.offset = 0; + requestParam.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + int ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, &requestParam, false); + if( ret == 0 ) + { + if( friendList.hasResult() ) + { + friendCount = friendList.get()->size(); + } + } +#endif + if( ret == 0 ) + { + isFriend = false; + SceNpId npid; + for( unsigned int i = 0; i < friendCount; i++ ) + { +#ifdef __PS3__ + ret = sceNpBasicGetFriendListEntry( i, &npid ); +#else + npid = friendList.get()->at(i).npid; +#endif + if( ret == 0 ) + { + if(strcmp(npid.handle.data, packet->m_playerXuids[packet->m_hostIndex].getOnlineID()) == 0) + { + isFriend = true; + break; + } + } + } + } + + if( !isFriend ) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + } + } + // is it an online game, and a player has chat restricted? + else if(!g_NetworkManager.IsLocalGame()) + { + // if the player is chat restricted, then they can't play an online game + bool bChatRestricted=false; + bool bContentRestricted=false; + + // If this is a pre-login packet for the first player on the machine, then accumulate up these flags for everyone signed in. We can handle exiting the game + // much more cleanly at this point by exiting the level, rather than waiting for a prelogin packet for the other players, when we have to exit the player + // which seems to be very unstable at the point of starting up the game + if(m_userIndex == ProfileManager.GetPrimaryPad()) + { + ProfileManager.GetChatAndContentRestrictions(m_userIndex,false,&bChatRestricted,&bContentRestricted,NULL); + } + else + { + ProfileManager.GetChatAndContentRestrictions(m_userIndex,true,&bChatRestricted,&bContentRestricted,NULL); + } + + // Chat restricted orbis players can still play online +#ifndef __ORBIS__ + canPlayLocal = !bChatRestricted; +#endif + + cantPlayContentRestricted = bContentRestricted ? 1 : 0; + } + + +#endif + +#ifdef _XBOX_ONE + if(!g_NetworkManager.IsHost() && m_userIndex == ProfileManager.GetPrimaryPad()) + { + long long startTime = System::currentTimeMillis(); + + auto friendsXuids = DQRNetworkManager::GetFriends(); + + if (app.GetGameHostOption(eGameHostOption_FriendsOfFriends)) + { + // Check that the user has at least one friend in the game + isAtLeastOneFriend = false; + + for (int i = 0; i < friendsXuids->Size; i++) + { + auto friendsXuid = friendsXuids->GetAt(i); + + // Check this friend against each player, if we find them we have at least one friend + for (int j = 0; j < g_NetworkManager.GetPlayerCount(); j++) + { + Platform::String^ xboxUserId = ref new Platform::String(g_NetworkManager.GetPlayerByIndex(j)->GetUID().toString().data()); + if (friendsXuid == xboxUserId) + { + isAtLeastOneFriend = true; + break; + } + } + } + + app.DebugPrintf("ClientConnection::handlePreLogin: User has at least one friend? %s\n", isAtLeastOneFriend ? "Yes" : "No"); + } + else + { + // Check that the user is friends with the host + bool isFriend = false; + + Platform::String^ hostXboxUserId = ref new Platform::String(g_NetworkManager.GetHostPlayer()->GetUID().toString().data()); + + for (int i = 0; i < friendsXuids->Size; i++) + { + if (friendsXuids->GetAt(i) == hostXboxUserId) + { + isFriend = true; + break; + } + } + + if( !isFriend ) + { + canPlay = FALSE; + isFriendsWithHost = FALSE; + } + + app.DebugPrintf("ClientConnection::handlePreLogin: User is friends with the host? %s\n", isFriendsWithHost ? "Yes" : "No"); + } + + app.DebugPrintf("ClientConnection::handlePreLogin: Friendship checks took %i ms\n", System::currentTimeMillis() - startTime); + } +#endif + +#endif // _XBOX + + if(!canPlay || !canPlayLocal || !isAtLeastOneFriend || cantPlayContentRestricted) + { +#ifndef __PS3__ + DisconnectPacket::eDisconnectReason reason = DisconnectPacket::eDisconnect_NoUGC_Remote; +#else + DisconnectPacket::eDisconnectReason reason = DisconnectPacket::eDisconnect_None; +#endif + if(m_userIndex == ProfileManager.GetPrimaryPad()) + { + if(!isFriendsWithHost) reason = DisconnectPacket::eDisconnect_NotFriendsWithHost; + else if(!isAtLeastOneFriend) reason = DisconnectPacket::eDisconnect_NoFriendsInGame; + else if(!canPlayLocal) reason = DisconnectPacket::eDisconnect_NoUGC_AllLocal; + else if(cantPlayContentRestricted) reason = DisconnectPacket::eDisconnect_ContentRestricted_AllLocal; + + app.DebugPrintf("Exiting world on handling Pre-Login packet due UGC privileges: %d\n", reason); + app.SetDisconnectReason( reason ); + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); + } + else + { + if(!isFriendsWithHost) reason = DisconnectPacket::eDisconnect_NotFriendsWithHost; + else if(!canPlayLocal) reason = DisconnectPacket::eDisconnect_NoUGC_Single_Local; + else if(cantPlayContentRestricted) reason = DisconnectPacket::eDisconnect_ContentRestricted_Single_Local; + + app.DebugPrintf("Exiting player %d on handling Pre-Login packet due UGC privileges: %d\n", m_userIndex, reason); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + if(!isFriendsWithHost) ui.RequestMessageBox( IDS_CANTJOIN_TITLE, IDS_NOTALLOWED_FRIENDSOFFRIENDS, uiIDA,1,m_userIndex,NULL,NULL, app.GetStringTable()); + else ui.RequestMessageBox( IDS_CANTJOIN_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL, uiIDA,1,m_userIndex,NULL,NULL, app.GetStringTable()); + + app.SetDisconnectReason( reason ); + + // 4J-PB - this locks up on the read and write threads not closing down, because they are trying to lock the incoming critsec when it's already locked by this thread +// Minecraft::GetInstance()->connectionDisconnected( m_userIndex , reason ); +// done = true; +// connection->flush(); +// connection->close(reason); +// app.SetAction(m_userIndex,eAppAction_ExitPlayer); + + // 4J-PB - doing this instead + app.SetAction(m_userIndex,eAppAction_ExitPlayerPreLogin); + } + } + else + { + // Texture pack handling + // If we have the texture pack for the game, load it + // If we don't then send a packet to the host to request it. We need to send this before the LoginPacket so that it gets handled first, + // as once the LoginPacket is received on the client the game is close to starting + if(m_userIndex == ProfileManager.GetPrimaryPad()) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->skins->selectTexturePackById(packet->m_texturePackId) ) + { + app.DebugPrintf("Selected texture pack %d from Pre-Login packet\n", packet->m_texturePackId); + } + else + { + app.DebugPrintf("Could not select texture pack %d from Pre-Login packet, requesting from host\n", packet->m_texturePackId); + + // 4J-PB - we need to upsell the texture pack to the player + //app.SetAction(m_userIndex,eAppAction_TexturePackRequired); + // Let the player go into the game, and we'll check that they are using the right texture pack when in + } + } + + if(!g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCPreLoginReceived * 100)/ (eCCConnected)); + } + // need to use the XUID here + PlayerUID offlineXUID = INVALID_XUID; + PlayerUID onlineXUID = INVALID_XUID; + if( ProfileManager.IsSignedInLive(m_userIndex) ) + { + // Guest don't have an offline XUID as they cannot play offline, so use their online one + ProfileManager.GetXUID(m_userIndex,&onlineXUID,true); + } +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() && onlineXUID.getOnlineID()[0] == 0) + { + // player doesn't have an online UID, set it from the player name + onlineXUID.setForAdhoc(); + } +#endif + + // On PS3, all non-signed in players (even guests) can get a useful offlineXUID +#if !(defined __PS3__ || defined _DURANGO ) + if( !ProfileManager.IsGuest( m_userIndex ) ) +#endif + { + // All other players we use their offline XUID so that they can play the game offline + ProfileManager.GetXUID(m_userIndex,&offlineXUID,false); + } + BOOL allAllowed, friendsAllowed; + ProfileManager.AllowedPlayerCreatedContent(m_userIndex,true,&allAllowed,&friendsAllowed); + send( shared_ptr( new LoginPacket(minecraft->user->name, SharedConstants::NETWORK_PROTOCOL_VERSION, offlineXUID, onlineXUID, (allAllowed!=TRUE && friendsAllowed==TRUE), + packet->m_ugcPlayersVersion, app.GetPlayerSkinId(m_userIndex), app.GetPlayerCapeId(m_userIndex), ProfileManager.IsGuest( m_userIndex )))); + + if(!g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStagePercentage((eCCLoginSent * 100)/ (eCCConnected)); + } + } +#else + // 4J - removed + if (packet->loginKey.equals("-")) { + send(new LoginPacket(minecraft->user.name, SharedConstants.NETWORK_PROTOCOL_VERSION)); + } else { + try { + URL url = new URL("http://www.minecraft->net/game/joinserver.jsp?user=" + minecraft->user.name + "&sessionId=" + minecraft->user.sessionId + "&serverId=" + packet->loginKey); + BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); + String msg = br.readLine(); + br.close(); + + if (msg.equalsIgnoreCase("ok")) { + send(new LoginPacket(minecraft->user.name, SharedConstants.NETWORK_PROTOCOL_VERSION)); + } else { + connection.close("disconnect.loginFailedInfo", msg); + } + } catch (Exception e) { + e.printStackTrace(); + connection.close("disconnect.genericReason", "Internal client error: " + e.toString()); + } + } +#endif +} + +void ClientConnection::close() +{ + // If it's already done, then we don't need to do anything here. And in fact trying to do something could cause a crash + if(done) return; + done = true; + connection->flush(); + connection->close(DisconnectPacket::eDisconnect_Closed); +} + +void ClientConnection::handleAddMob(shared_ptr packet) +{ + double x = packet->x / 32.0; + double y = packet->y / 32.0; + double z = packet->z / 32.0; + float yRot = packet->yRot * 360 / 256.0f; + float xRot = packet->xRot * 360 / 256.0f; + + shared_ptr mob = dynamic_pointer_cast(EntityIO::newById(packet->type, level)); + mob->xp = packet->x; + mob->yp = packet->y; + mob->zp = packet->z; + mob->yHeadRot = packet->yHeadRot * 360 / 256.0f; + mob->yRotp = packet->yRot; + mob->xRotp = packet->xRot; + + vector > *subEntities = mob->getSubEntities(); + if (subEntities != NULL) + { + int offs = packet->id - mob->entityId; + //for (int i = 0; i < subEntities.length; i++) + for(AUTO_VAR(it, subEntities->begin()); it != subEntities->end(); ++it) + { + //subEntities[i].entityId += offs; + (*it)->entityId += offs; + } + } + + mob->entityId = packet->id; + +// printf("\t\t\t\t%d: Add mob rot %d\n",packet->id,packet->yRot); + + mob->absMoveTo(x, y, z, yRot, xRot); + mob->xd = packet->xd / 8000.0f; + mob->yd = packet->yd / 8000.0f; + mob->zd = packet->zd / 8000.0f; + level->putEntity(packet->id, mob); + + vector > *unpackedData = packet->getUnpackedData(); + if (unpackedData != NULL) + { + mob->getEntityData()->assignValues(unpackedData); + } + + // Fix for #65236 - TU8: Content: Gameplay: Magma Cubes' have strange hit boxes. + // 4J Stu - Slimes have a different BB depending on their size which is set in the entity data, so update the BB + if(mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME) + { + shared_ptr slime = dynamic_pointer_cast(mob); + slime->setSize( slime->getSize() ); + } +} + +void ClientConnection::handleSetTime(shared_ptr packet) +{ + minecraft->level->setTime(packet->time); +} + +void ClientConnection::handleSetSpawn(shared_ptr packet) +{ + //minecraft->player->setRespawnPosition(new Pos(packet->x, packet->y, packet->z)); + minecraft->localplayers[m_userIndex]->setRespawnPosition(new Pos(packet->x, packet->y, packet->z)); + minecraft->level->getLevelData()->setSpawn(packet->x, packet->y, packet->z); + +} + +void ClientConnection::handleRidePacket(shared_ptr packet) +{ + shared_ptr rider = getEntity(packet->riderId); + shared_ptr ridden = getEntity(packet->riddenId); + + shared_ptr boat = dynamic_pointer_cast(ridden); + //if (packet->riderId == minecraft->player->entityId) rider = minecraft->player; + if (packet->riderId == minecraft->localplayers[m_userIndex]->entityId) + { + rider = minecraft->localplayers[m_userIndex]; + + if (boat) boat->setDoLerp(false); + } + else if (boat) + { + boat->setDoLerp(true); + } + if (rider == NULL) return; + + rider->ride(ridden); +} + +void ClientConnection::handleEntityEvent(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->entityId); + if (e != NULL) e->handleEntityEvent(packet->eventId); +} + +shared_ptr ClientConnection::getEntity(int entityId) +{ + //if (entityId == minecraft->player->entityId) + if(entityId == minecraft->localplayers[m_userIndex]->entityId) + { + //return minecraft->player; + return minecraft->localplayers[m_userIndex]; + } + return level->getEntity(entityId); +} + +void ClientConnection::handleSetHealth(shared_ptr packet) +{ + //minecraft->player->hurtTo(packet->health); + minecraft->localplayers[m_userIndex]->hurtTo(packet->health,packet->damageSource); + minecraft->localplayers[m_userIndex]->getFoodData()->setFoodLevel(packet->food); + minecraft->localplayers[m_userIndex]->getFoodData()->setSaturation(packet->saturation); + + // We need food + if(packet->food < FoodConstants::HEAL_LEVEL - 1) + { + if(minecraft->localgameModes[m_userIndex] != NULL && !minecraft->localgameModes[m_userIndex]->hasInfiniteItems() ) + { + minecraft->localgameModes[m_userIndex]->getTutorial()->changeTutorialState(e_Tutorial_State_Food_Bar); + } + } +} + +void ClientConnection::handleSetExperience(shared_ptr packet) +{ + minecraft->localplayers[m_userIndex]->setExperienceValues(packet->experienceProgress, packet->totalExperience, packet->experienceLevel); +} + +void ClientConnection::handleTexture(shared_ptr packet) +{ + // Both PlayerConnection and ClientConnection should handle this mostly the same way + // Server side also needs to store a list of those clients waiting to get a texture the server doesn't have yet + // so that it can send it out to them when it comes in + + if(packet->dwBytes==0) + { + // Request for texture +#ifndef _CONTENT_PACKAGE + wprintf(L"Client received request for custom texture %ls\n",packet->textureName.c_str()); +#endif + PBYTE pbData=NULL; + DWORD dwBytes=0; + app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes); + + if(dwBytes!=0) + { + send( shared_ptr( new TexturePacket(packet->textureName,pbData,dwBytes) ) ); + } + } + else + { + // Response with texture data +#ifndef _CONTENT_PACKAGE + wprintf(L"Client received custom texture %ls\n",packet->textureName.c_str()); +#endif + app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwBytes); + Minecraft::GetInstance()->handleClientTextureReceived(packet->textureName); + } +} + +void ClientConnection::handleTextureAndGeometry(shared_ptr packet) +{ + // Both PlayerConnection and ClientConnection should handle this mostly the same way + // Server side also needs to store a list of those clients waiting to get a texture the server doesn't have yet + // so that it can send it out to them when it comes in + + if(packet->dwTextureBytes==0) + { + // Request for texture +#ifndef _CONTENT_PACKAGE + wprintf(L"Client received request for custom texture and geometry %ls\n",packet->textureName.c_str()); +#endif + PBYTE pbData=NULL; + DWORD dwBytes=0; + app.GetMemFileDetails(packet->textureName,&pbData,&dwBytes); + DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(packet->textureName); + + if(dwBytes!=0) + { + if(pDLCSkinFile) + { + if(pDLCSkinFile->getAdditionalBoxesCount()!=0) + { + send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes,pDLCSkinFile) ) ); + } + else + { + send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes) ) ); + } + } + else + { + unsigned int uiAnimOverrideBitmask= app.GetAnimOverrideBitmask(packet->dwSkinID); + + send( shared_ptr( new TextureAndGeometryPacket(packet->textureName,pbData,dwBytes,app.GetAdditionalSkinBoxes(packet->dwSkinID),uiAnimOverrideBitmask) ) ); + } + } + } + else + { + // Response with texture data +#ifndef _CONTENT_PACKAGE + wprintf(L"Client received custom TextureAndGeometry %ls\n",packet->textureName.c_str()); +#endif + // Add the texture data + app.AddMemoryTextureFile(packet->textureName,packet->pbData,packet->dwTextureBytes); + // Add the geometry data + if(packet->dwBoxC!=0) + { + app.SetAdditionalSkinBoxes(packet->dwSkinID,packet->BoxDataA,packet->dwBoxC); + } + // Add the anim override + app.SetAnimOverrideBitmask(packet->dwSkinID,packet->uiAnimOverrideBitmask); + + // clear out the pending texture request + Minecraft::GetInstance()->handleClientTextureReceived(packet->textureName); + } +} + +void ClientConnection::handleTextureChange(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + shared_ptr player = dynamic_pointer_cast(e); + if( e == NULL) return; + + bool isLocalPlayer = false; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->id ) + { + isLocalPlayer = true; + break; + } + } + } + if(isLocalPlayer) return; + + switch(packet->action) + { + case TextureChangePacket::e_TextureChange_Skin: + player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); +#ifndef _CONTENT_PACKAGE + wprintf(L"Skin for remote player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); +#endif + break; + case TextureChangePacket::e_TextureChange_Cape: + player->setCustomCape( Player::getCapeIdFromPath( packet->path ) ); + //player->customTextureUrl2 = packet->path; +#ifndef _CONTENT_PACKAGE + wprintf(L"Cape for remote player %ls has changed to %ls\n", player->name.c_str(), player->customTextureUrl2.c_str() ); +#endif + break; + } + + if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) + { + if( minecraft->addPendingClientTextureRequest(packet->path) ) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"handleTextureChange - Client sending texture packet to get custom skin %ls for player %ls\n",packet->path.c_str(), player->name.c_str()); +#endif + send(shared_ptr( new TexturePacket(packet->path,NULL,0) ) ); + } + } + else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(packet->path,NULL,0); + } +} + +void ClientConnection::handleTextureAndGeometryChange(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->id); + if (e == NULL) return; + shared_ptr player = dynamic_pointer_cast(e); + if( e == NULL) return; + + bool isLocalPlayer = false; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + if( minecraft->localplayers[i]->entityId == packet->id ) + { + isLocalPlayer = true; + break; + } + } + } + if(isLocalPlayer) return; + + + player->setCustomSkin( app.getSkinIdFromPath( packet->path ) ); + +#ifndef _CONTENT_PACKAGE + wprintf(L"Skin for remote player %ls has changed to %ls (%d)\n", player->name.c_str(), player->customTextureUrl.c_str(), player->getPlayerDefaultSkin() ); +#endif + + if(!packet->path.empty() && packet->path.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(packet->path)) + { + if( minecraft->addPendingClientTextureRequest(packet->path) ) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"handleTextureAndGeometryChange - Client sending TextureAndGeometryPacket to get custom skin %ls for player %ls\n",packet->path.c_str(), player->name.c_str()); +#endif + send(shared_ptr( new TextureAndGeometryPacket(packet->path,NULL,0) ) ); + } + } + else if(!packet->path.empty() && app.IsFileInMemoryTextures(packet->path)) + { + // Update the ref count on the memory texture data + app.AddMemoryTextureFile(packet->path,NULL,0); + + } +} + +void ClientConnection::handleRespawn(shared_ptr packet) +{ + //if (packet->dimension != minecraft->player->dimension) + if( packet->dimension != minecraft->localplayers[m_userIndex]->dimension || packet->mapSeed != minecraft->localplayers[m_userIndex]->level->getSeed() ) + { + int oldDimension = minecraft->localplayers[m_userIndex]->dimension; + started = false; + + // Remove client connection from this level + level->removeClientConnection(this, false); + + MultiPlayerLevel *dimensionLevel = (MultiPlayerLevel *)minecraft->getLevel( packet->dimension ); + if( dimensionLevel == NULL ) + { + dimensionLevel = new MultiPlayerLevel(this, new LevelSettings(packet->mapSeed, packet->playerGameType, false, minecraft->level->getLevelData()->isHardcore(), packet->m_newSeaLevel, packet->m_pLevelType, packet->m_xzSize, packet->m_hellScale), packet->dimension, packet->difficulty); + + // 4J Stu - We want to shared the savedDataStorage between both levels + //if( dimensionLevel->savedDataStorage != NULL ) + //{ + // Don't need to delete it here as it belongs to a client connection while will delete it when it's done + // delete dimensionLevel->savedDataStorage;+ + //} + dimensionLevel->savedDataStorage = level->savedDataStorage; + + dimensionLevel->difficulty = packet->difficulty; // 4J Added + app.DebugPrintf("dimensionLevel->difficulty - Difficulty = %d\n",packet->difficulty); + + dimensionLevel->isClientSide = true; + } + else + { + dimensionLevel->addClientConnection(this); + } + + // Remove the player entity from the current level + level->removeEntity( shared_ptr(minecraft->localplayers[m_userIndex]) ); + + level = dimensionLevel; + + // Whilst calling setLevel, make sure that minecraft::player is set up to be correct for this + // connection + shared_ptr lastPlayer = minecraft->player; + minecraft->player = minecraft->localplayers[m_userIndex]; + minecraft->setLevel(dimensionLevel); + minecraft->player = lastPlayer; + + TelemetryManager->RecordLevelExit(m_userIndex, eSen_LevelExitStatus_Succeeded); + + //minecraft->player->dimension = packet->dimension; + minecraft->localplayers[m_userIndex]->dimension = packet->dimension; + //minecraft->setScreen(new ReceivingLevelScreen(this)); +// minecraft->addPendingLocalConnection(m_userIndex, this); + +#ifdef _XBOX + TelemetryManager->RecordLevelStart(m_userIndex, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->getLevel(packet->dimension)->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount()); +#endif + + if( minecraft->localgameModes[m_userIndex] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)minecraft->localgameModes[m_userIndex]; + gameMode->getTutorial()->showTutorialPopup(false); + } + + // 4J-JEV: Fix for Durango #156334 - Content: UI: Rich Presence 'In the Nether' message is updating with a 3 to 10 minute delay. + minecraft->localplayers[m_userIndex]->updateRichPresence(); + + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = m_userIndex; + if( packet->dimension == -1) + { + param->stringId = IDS_PROGRESS_ENTERING_NETHER; + } + else if( oldDimension == -1) + { + param->stringId = IDS_PROGRESS_LEAVING_NETHER; + } + else if( packet->dimension == 1) + { + param->stringId = IDS_PROGRESS_ENTERING_END; + } + else if( oldDimension == 1) + { + param->stringId = IDS_PROGRESS_LEAVING_END; + } + param->showTooltips = false; + param->setFailTimer = false; + + // 4J Stu - Fix for #13543 - Crash: Game crashes if entering a portal with the inventory menu open + ui.CloseUIScenes( m_userIndex ); + + if(app.GetLocalPlayerCount()>1) + { + ui.NavigateToScene(m_userIndex, eUIScene_ConnectingProgress, param); + } + else + { + ui.NavigateToScene(m_userIndex, eUIScene_ConnectingProgress, param); + } + + app.SetAction( m_userIndex, eAppAction_WaitForDimensionChangeComplete); + } + + //minecraft->respawnPlayer(minecraft->player->GetXboxPad(),true, packet->dimension); + + // Wrap respawnPlayer call up in code to set & restore the player/gamemode etc. as some things + // in there assume that we are set up for the player that the respawn is coming in for + int oldIndex = minecraft->getLocalPlayerIdx(); + minecraft->setLocalPlayerIdx(m_userIndex); + minecraft->respawnPlayer(minecraft->localplayers[m_userIndex]->GetXboxPad(),packet->dimension,packet->m_newEntityId); + ((MultiPlayerGameMode *) minecraft->localgameModes[m_userIndex])->setLocalMode(packet->playerGameType); + minecraft->setLocalPlayerIdx(oldIndex); +} + +void ClientConnection::handleExplosion(shared_ptr packet) +{ + if(!packet->m_bKnockbackOnly) + { + //app.DebugPrintf("Received ExplodePacket with explosion data\n"); + PIXBeginNamedEvent(0,"Handling explosion"); + Explosion *e = new Explosion(minecraft->level, nullptr, packet->x, packet->y, packet->z, packet->r); + PIXBeginNamedEvent(0,"Finalizing"); + + // Fix for #81758 - TCR 006 BAS Non-Interactive Pause: TU9: Performance: Gameplay: After detonating bunch of TNT, game enters unresponsive state for couple of seconds. + // The changes we are making here have been decided by the server, so we don't need to add them to the vector that resets tiles changes made + // on the client as we KNOW that the server is matching these changes + MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)minecraft->level; + mpLevel->enableResetChanges(false); + // 4J - now directly pass a pointer to the toBlow array in the packet rather than copying around + e->finalizeExplosion(true, &packet->toBlow); + mpLevel->enableResetChanges(true); + PIXEndNamedEvent(); + PIXEndNamedEvent(); + delete e; + } + else + { + //app.DebugPrintf("Received ExplodePacket with knockback only data\n"); + } + + //app.DebugPrintf("Adding knockback (%f,%f,%f) for player %d\n", packet->getKnockbackX(), packet->getKnockbackY(), packet->getKnockbackZ(), m_userIndex); + minecraft->localplayers[m_userIndex]->xd += packet->getKnockbackX(); + minecraft->localplayers[m_userIndex]->yd += packet->getKnockbackY(); + minecraft->localplayers[m_userIndex]->zd += packet->getKnockbackZ(); +} + +void ClientConnection::handleContainerOpen(shared_ptr packet) +{ + bool failed = false; + shared_ptr player = minecraft->localplayers[m_userIndex]; + switch(packet->type) + { + case ContainerOpenPacket::CONTAINER: + { + if( player->openContainer(shared_ptr( new SimpleContainer(packet->title, packet->size) ))) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::FURNACE: + { + if( player->openFurnace(shared_ptr( new FurnaceTileEntity() )) ) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::BREWING_STAND: + { + if( player->openBrewingStand(shared_ptr( new BrewingStandTileEntity() )) ) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::TRAP: + { + if( player->openTrap(shared_ptr( new DispenserTileEntity() )) ) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::WORKBENCH: + { + if( player->startCrafting(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)) ) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::ENCHANTMENT: + { + if( player->startEnchanting(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)) ) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::TRADER_NPC: + { + shared_ptr csm = shared_ptr(new ClientSideMerchant(player,packet->title)); + csm->createContainer(); + if(player->openTrading(csm)) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + case ContainerOpenPacket::REPAIR_TABLE: + { + if(player->startRepairing(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z))) + { + player->containerMenu->containerId = packet->containerId; + } + else + { + failed = true; + } + } + break; + }; + + if(failed) + { + // Failed - if we've got a non-inventory container currently here, close that, which locally should put us back + // to not having a container open, and should send a containerclose to the server so it doesn't have a container open. + // If we don't have a non-inventory container open, just send the packet, and again we ought to be in sync with the server. + if( player->containerMenu != player->inventoryMenu ) + { + ui.CloseUIScenes(m_userIndex); + } + else + { + send(shared_ptr(new ContainerClosePacket(packet->containerId))); + } + } +} + +void ClientConnection::handleContainerSetSlot(shared_ptr packet) +{ + shared_ptr player = minecraft->localplayers[m_userIndex]; + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_CARRIED ) + { + player->inventory->setCarried(packet->item); + } + else + { + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY) + { + // 4J Stu - Reworked a bit to fix a bug where things being collected while the creative menu was up replaced items in the creative menu + if(packet->slot >= 36 && packet->slot < 36 + 9) + { + shared_ptr lastItem = player->inventoryMenu->getSlot(packet->slot)->getItem(); + if (packet->item != NULL) + { + if (lastItem == NULL || lastItem->count < packet->item->count) + { + packet->item->popTime = Inventory::POP_TIME_DURATION; + } + } + } + player->inventoryMenu->setItem(packet->slot, packet->item); + } + else if (packet->containerId == player->containerMenu->containerId) + { + player->containerMenu->setItem(packet->slot, packet->item); + } + } +} + +void ClientConnection::handleContainerAck(shared_ptr packet) +{ + shared_ptr player = minecraft->localplayers[m_userIndex]; + AbstractContainerMenu *menu = NULL; + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY) + { + menu = player->inventoryMenu; + } + else if (packet->containerId == player->containerMenu->containerId) + { + menu = player->containerMenu; + } + if (menu != NULL) + { + if (!packet->accepted) + { + send( shared_ptr( new ContainerAckPacket(packet->containerId, packet->uid, true) )); + } + } +} + +void ClientConnection::handleContainerContent(shared_ptr packet) +{ + shared_ptr player = minecraft->localplayers[m_userIndex]; + if (packet->containerId == AbstractContainerMenu::CONTAINER_ID_INVENTORY) + { + player->inventoryMenu->setAll(&packet->items); + } + else if (packet->containerId == player->containerMenu->containerId) + { + player->containerMenu->setAll(&packet->items); + } +} + +void ClientConnection::handleSignUpdate(shared_ptr packet) +{ + app.DebugPrintf("ClientConnection::handleSignUpdate - "); + if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z)) + { + shared_ptr te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z); + + // 4J-PB - on a client connecting, the line below fails + if (dynamic_pointer_cast(te) != NULL) + { + shared_ptr ste = dynamic_pointer_cast(te); + for (int i = 0; i < MAX_SIGN_LINES; i++) + { + ste->SetMessage(i,packet->lines[i]); + } + + app.DebugPrintf("verified = %d\tCensored = %d\n",packet->m_bVerified,packet->m_bCensored); + ste->SetVerified(packet->m_bVerified); + ste->SetCensored(packet->m_bCensored); + + ste->setChanged(); + } + else + { + app.DebugPrintf("dynamic_pointer_cast(te) == NULL\n"); + } + } + else + { + app.DebugPrintf("hasChunkAt failed\n"); + } +} + +void ClientConnection::handleTileEntityData(shared_ptr packet) +{ + if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z)) + { + shared_ptr te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z); + + if (te != NULL) + { + if (packet->type == TileEntityDataPacket::TYPE_MOB_SPAWNER && dynamic_pointer_cast(te) != NULL) + { + dynamic_pointer_cast(te)->load(packet->tag); + } + //else if (packet.type == TileEntityDataPacket.TYPE_ADV_COMMAND && (te instanceof CommandBlockEntity)) + //{ + // ((CommandBlockEntity) te).load(packet.tag); + //} + //else if (packet.type == TileEntityDataPacket.TYPE_BEACON && (te instanceof BeaconTileEntity)) + //{ + // ((BeaconTileEntity) te).load(packet.tag); + //} + else if (packet->type == TileEntityDataPacket::TYPE_SKULL && dynamic_pointer_cast(te) != NULL) + { + dynamic_pointer_cast(te)->load(packet->tag); + } + } + } +} + +void ClientConnection::handleContainerSetData(shared_ptr packet) +{ + onUnhandledPacket(packet); + if (minecraft->localplayers[m_userIndex]->containerMenu != NULL && minecraft->localplayers[m_userIndex]->containerMenu->containerId == packet->containerId) + { + minecraft->localplayers[m_userIndex]->containerMenu->setData(packet->id, packet->value); + } +} + +void ClientConnection::handleSetEquippedItem(shared_ptr packet) +{ + shared_ptr entity = getEntity(packet->entity); + if (entity != NULL) + { + // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + entity->setEquippedSlot(packet->slot, packet->getItem() ); + } +} + +void ClientConnection::handleContainerClose(shared_ptr packet) +{ + minecraft->localplayers[m_userIndex]->closeContainer(); +} + +void ClientConnection::handleTileEvent(shared_ptr packet) +{ + PIXBeginNamedEvent(0,"Handle tile event\n"); + minecraft->level->tileEvent(packet->x, packet->y, packet->z, packet->tile, packet->b0, packet->b1); + PIXEndNamedEvent(); +} + +void ClientConnection::handleTileDestruction(shared_ptr packet) +{ + minecraft->level->destroyTileProgress(packet->getEntityId(), packet->getX(), packet->getY(), packet->getZ(), packet->getState()); +} + +bool ClientConnection::canHandleAsyncPackets() +{ + return minecraft != NULL && minecraft->level != NULL && minecraft->localplayers[m_userIndex] != NULL && level != NULL; +} + +void ClientConnection::handleGameEvent(shared_ptr gameEventPacket) +{ + int event = gameEventPacket->_event; + int param = gameEventPacket->param; + if (event >= 0 && event < GameEventPacket::EVENT_LANGUAGE_ID_LENGTH) + { + if (GameEventPacket::EVENT_LANGUAGE_ID[event] > 0) // 4J - was NULL check + { + minecraft->localplayers[m_userIndex]->displayClientMessage(GameEventPacket::EVENT_LANGUAGE_ID[event]); + } + } + if (event == GameEventPacket::START_RAINING) + { + level->getLevelData()->setRaining(true); + level->setRainLevel(1); + } + else if (event == GameEventPacket::STOP_RAINING) + { + level->getLevelData()->setRaining(false); + level->setRainLevel(0); + } + else if (event == GameEventPacket::CHANGE_GAME_MODE) + { + minecraft->localgameModes[m_userIndex]->setLocalMode(GameType::byId(param)); + } + else if (event == GameEventPacket::WIN_GAME) + { + ui.SetWinUserIndex( (BYTE)gameEventPacket->param ); + +#ifdef _XBOX + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // Hide the other players scenes + ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false); + + // This just allows it to be shown + if(minecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) minecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false); + // Temporarily make this scene fullscreen + CXuiSceneBase::SetPlayerBaseScenePosition( ProfileManager.GetPrimaryPad(), CXuiSceneBase::e_BaseScene_Fullscreen ); + + app.CloseXuiScenesAndNavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_EndPoem); +#else + app.DebugPrintf("handleGameEvent packet for WIN_GAME - %d\n", m_userIndex); + // This just allows it to be shown + if(minecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) minecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false); + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_EndPoem, NULL, eUILayer_Scene, eUIGroup_Fullscreen); +#endif + } + else if( event == GameEventPacket::START_SAVING ) + { + if(!g_NetworkManager.IsHost()) + { + // Move app started to here so that it happens immediately otherwise back-to-back START/STOP packets + // leave the client stuck in the loading screen + app.SetGameStarted(false); + app.SetAction( ProfileManager.GetPrimaryPad(), eAppAction_RemoteServerSave ); + } + } + else if( event == GameEventPacket::STOP_SAVING ) + { + if(!g_NetworkManager.IsHost() ) app.SetGameStarted(true); + } +} + +void ClientConnection::handleComplexItemData(shared_ptr packet) +{ + if (packet->itemType == Item::map->id) + { + MapItem::getSavedData(packet->itemId, minecraft->level)->handleComplexItemData(packet->data); + } + else + { +// System.out.println("Unknown itemid: " + packet->itemId); // 4J removed + } +} + + + +void ClientConnection::handleLevelEvent(shared_ptr packet) +{ + switch(packet->type) + { + case LevelEvent::SOUND_DRAGON_DEATH: + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(pMinecraft->localplayers[i] != NULL && pMinecraft->localplayers[i]->level != NULL && pMinecraft->localplayers[i]->level->dimension->id == 1) + { + pMinecraft->localplayers[i]->awardStat(GenericStats::completeTheEnd(),GenericStats::param_noArgs()); + } + } + } + minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data); + + break; + default: + minecraft->level->levelEvent(packet->type, packet->x, packet->y, packet->z, packet->data); + break; + } +} + +void ClientConnection::handleAwardStat(shared_ptr packet) +{ + minecraft->localplayers[m_userIndex]->awardStatFromServer(GenericStats::stat(packet->statId), packet->getParamData()); +} + +void ClientConnection::handleUpdateMobEffect(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->entityId); + if (e == NULL || dynamic_pointer_cast(e) == NULL) return; + + ( dynamic_pointer_cast(e) )->addEffect(new MobEffectInstance(packet->effectId, packet->effectDurationTicks, packet->effectAmplifier)); +} + +void ClientConnection::handleRemoveMobEffect(shared_ptr packet) +{ + shared_ptr e = getEntity(packet->entityId); + if (e == NULL || dynamic_pointer_cast(e) == NULL) return; + + ( dynamic_pointer_cast(e) )->removeEffectNoUpdate(packet->effectId); +} + +bool ClientConnection::isServerPacketListener() +{ + return false; +} + +void ClientConnection::handlePlayerInfo(shared_ptr packet) +{ + unsigned int startingPrivileges = app.GetPlayerPrivileges(packet->m_networkSmallId); + + INetworkPlayer *networkPlayer = g_NetworkManager.GetPlayerBySmallId(packet->m_networkSmallId); + + if(networkPlayer != NULL && networkPlayer->IsHost()) + { + // Some settings should always be considered on for the host player + Player::enableAllPlayerPrivileges(startingPrivileges,true); + Player::setPlayerGamePrivilege(startingPrivileges,Player::ePlayerGamePrivilege_HOST,1); + } + + // 4J Stu - Repurposed this packet for player info that we want + app.UpdatePlayerInfo(packet->m_networkSmallId, packet->m_playerColourIndex, packet->m_playerPrivileges); + + shared_ptr entity = getEntity(packet->m_entityId); + if(entity != NULL && entity->GetType() == eTYPE_PLAYER) + { + shared_ptr player = dynamic_pointer_cast(entity); + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, packet->m_playerPrivileges); + } + if(networkPlayer != NULL && networkPlayer->IsLocal()) + { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + shared_ptr localPlayer = minecraft->localplayers[i]; + if(localPlayer != NULL && localPlayer->connection != NULL && localPlayer->connection->getNetworkPlayer() == networkPlayer ) + { + localPlayer->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All,packet->m_playerPrivileges); + displayPrivilegeChanges(localPlayer,startingPrivileges); + break; + } + } + } + + // 4J Stu - I don't think we care about this, so not converting it (came from 1.8.2) +#if 0 + PlayerInfo pi = playerInfoMap.get(packet.name); + if (pi == null && packet.add) { + pi = new PlayerInfo(packet.name); + playerInfoMap.put(packet.name, pi); + playerInfos.add(pi); + } + if (pi != null && !packet.add) { + playerInfoMap.remove(packet.name); + playerInfos.remove(pi); + } + if (packet.add && pi != null) { + pi.latency = packet.latency; + } +#endif +} + + +void ClientConnection::displayPrivilegeChanges(shared_ptr player, unsigned int oldPrivileges) +{ + int userIndex = player->GetXboxPad(); + unsigned int newPrivileges = player->getAllPlayerGamePrivileges(); + Player::EPlayerGamePrivileges priv = (Player::EPlayerGamePrivileges)0; + bool privOn = false; + for(unsigned int i = 0; i < Player::ePlayerGamePrivilege_MAX; ++i) + { + priv = (Player::EPlayerGamePrivileges) i; + if( Player::getPlayerGamePrivilege(newPrivileges,priv) != Player::getPlayerGamePrivilege(oldPrivileges,priv)) + { + privOn = Player::getPlayerGamePrivilege(newPrivileges,priv); + wstring message = L""; + if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) + { + switch(priv) + { + case Player::ePlayerGamePrivilege_CannotMine: + if(privOn) message = app.GetString(IDS_PRIV_MINE_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_MINE_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CannotBuild: + if(privOn) message = app.GetString(IDS_PRIV_BUILD_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_BUILD_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches: + if(privOn) message = app.GetString(IDS_PRIV_USE_DOORS_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_USE_DOORS_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanUseContainers: + if(privOn) message = app.GetString(IDS_PRIV_USE_CONTAINERS_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_USE_CONTAINERS_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CannotAttackAnimals: + if(privOn) message = app.GetString(IDS_PRIV_ATTACK_ANIMAL_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_ATTACK_ANIMAL_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CannotAttackMobs: + if(privOn) message = app.GetString(IDS_PRIV_ATTACK_MOB_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_ATTACK_MOB_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CannotAttackPlayers: + if(privOn) message = app.GetString(IDS_PRIV_ATTACK_PLAYER_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_ATTACK_PLAYER_TOGGLE_OFF); + break; + }; + } + switch(priv) + { + case Player::ePlayerGamePrivilege_Op: + if(privOn) message = app.GetString(IDS_PRIV_MODERATOR_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_MODERATOR_TOGGLE_OFF); + break; + }; + if(app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0) + { + switch(priv) + { + case Player::ePlayerGamePrivilege_CanFly: + if(privOn) message = app.GetString(IDS_PRIV_FLY_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_FLY_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_ClassicHunger: + if(privOn) message = app.GetString(IDS_PRIV_EXHAUSTION_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_EXHAUSTION_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_Invisible: + if(privOn) message = app.GetString(IDS_PRIV_INVISIBLE_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_INVISIBLE_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_Invulnerable: + if(privOn) message = app.GetString(IDS_PRIV_INVULNERABLE_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_INVULNERABLE_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanToggleInvisible: + if(privOn) message = app.GetString(IDS_PRIV_CAN_INVISIBLE_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_CAN_INVISIBLE_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanToggleFly: + if(privOn) message = app.GetString(IDS_PRIV_CAN_FLY_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_CAN_FLY_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanToggleClassicHunger: + if(privOn) message = app.GetString(IDS_PRIV_CAN_EXHAUSTION_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_CAN_EXHAUSTION_TOGGLE_OFF); + break; + case Player::ePlayerGamePrivilege_CanTeleport: + if(privOn) message = app.GetString(IDS_PRIV_CAN_TELEPORT_TOGGLE_ON); + else message = app.GetString(IDS_PRIV_CAN_TELEPORT_TOGGLE_OFF); + break; + }; + } + if(!message.empty()) minecraft->gui->addMessage(message,userIndex); + } + } +} + +void ClientConnection::handleKeepAlive(shared_ptr packet) +{ + send(shared_ptr(new KeepAlivePacket(packet->id))); +} + +void ClientConnection::handlePlayerAbilities(shared_ptr playerAbilitiesPacket) +{ + shared_ptr player = minecraft->localplayers[m_userIndex]; + player->abilities.flying = playerAbilitiesPacket->isFlying(); + player->abilities.instabuild = playerAbilitiesPacket->canInstabuild(); + player->abilities.invulnerable = playerAbilitiesPacket->isInvulnerable(); + player->abilities.mayfly = playerAbilitiesPacket->canFly(); + player->abilities.setFlyingSpeed(playerAbilitiesPacket->getFlyingSpeed()); +} + +void ClientConnection::handleSoundEvent(shared_ptr packet) +{ + minecraft->level->playLocalSound(packet->getX(), packet->getY(), packet->getZ(), packet->getSound(), packet->getVolume(), packet->getPitch()); +} + +void ClientConnection::handleCustomPayload(shared_ptr customPayloadPacket) +{ + if (CustomPayloadPacket::TRADER_LIST_PACKET.compare(customPayloadPacket->identifier) == 0) + { + ByteArrayInputStream bais(customPayloadPacket->data); + DataInputStream input(&bais); + int containerId = input.readInt(); + if (ui.IsSceneInStack(m_userIndex, eUIScene_TradingMenu) && containerId == minecraft->localplayers[m_userIndex]->containerMenu->containerId) + { + shared_ptr trader = nullptr; + +#ifdef _XBOX + HXUIOBJ scene = app.GetCurrentScene(m_userIndex); + HXUICLASS thisClass = XuiFindClass( L"CXuiSceneTrading" ); + HXUICLASS objClass = XuiGetObjectClass( scene ); + + // Also returns TRUE if they are the same (which is what we want) + if( XuiClassDerivesFrom( objClass, thisClass ) ) + { + CXuiSceneTrading *screen; + HRESULT hr = XuiObjectFromHandle(scene, (void **) &screen); + if (FAILED(hr)) return; + trader = screen->getMerchant(); + } +#else + UIScene *scene = ui.GetTopScene(m_userIndex, eUILayer_Scene); + UIScene_TradingMenu *screen = (UIScene_TradingMenu *)scene; + trader = screen->getMerchant(); +#endif + + MerchantRecipeList *recipeList = MerchantRecipeList::createFromStream(&input); + trader->overrideOffers(recipeList); + } + } +} + +Connection *ClientConnection::getConnection() +{ + return connection; +} + +// 4J Added +void ClientConnection::handleServerSettingsChanged(shared_ptr packet) +{ + if(packet->action==ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS) + { + app.SetGameHostOption(eGameHostOption_All, packet->data); + } + else if(packet->action==ServerSettingsChangedPacket::HOST_DIFFICULTY) + { + for(unsigned int i = 0; i < minecraft->levels.length; ++i) + { + if( minecraft->levels[i] != NULL ) + { + app.DebugPrintf("ClientConnection::handleServerSettingsChanged - Difficulty = %d",packet->data); + minecraft->levels[i]->difficulty = packet->data; + } + } + } + else + { + //options + //minecraft->options->SetGamertagSetting((packet->data==0)?false:true); + app.SetGameHostOption(eGameHostOption_Gamertags, packet->data); + } + +} + +void ClientConnection::handleXZ(shared_ptr packet) +{ + if(packet->action==XZPacket::STRONGHOLD) + { + minecraft->levels[0]->getLevelData()->setXStronghold(packet->x); + minecraft->levels[0]->getLevelData()->setZStronghold(packet->z); + minecraft->levels[0]->getLevelData()->setHasStronghold(); + } +} + +void ClientConnection::handleUpdateProgress(shared_ptr packet) +{ + if(!g_NetworkManager.IsHost() ) Minecraft::GetInstance()->progressRenderer->progressStagePercentage( packet->m_percentage ); +} + +void ClientConnection::handleUpdateGameRuleProgressPacket(shared_ptr packet) +{ + LPCWSTR string = app.GetGameRulesString(packet->m_messageId); + if(string != NULL) + { + wstring message(string); + message = GameRuleDefinition::generateDescriptionString(packet->m_definitionType,message,packet->m_data.data,packet->m_data.length); + if(minecraft->localgameModes[m_userIndex]!=NULL) + { + minecraft->localgameModes[m_userIndex]->getTutorial()->setMessage(message, packet->m_icon, packet->m_auxValue); + } + } + // If this rule has a data tag associated with it, then we save that in user profile data + if(packet->m_dataTag > 0 && packet->m_dataTag <= 32) + { + app.DebugPrintf("handleUpdateGameRuleProgressPacket: Data tag is in range, so updating profile data\n"); + app.SetSpecialTutorialCompletionFlag(m_userIndex, packet->m_dataTag - 1); + } + delete [] packet->m_data.data; +} + +// 4J Stu - TU-1 hotfix +// Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost +int ClientConnection::HostDisconnectReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // 4J-PB - if they have a trial texture pack, they don't get to save the world + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // no upsell, we're about to quit + MinecraftServer::getInstance()->setSaveOnExit( false ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + } + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Give the player the option to save their game + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists && StorageManager.GetSaveDisabled()) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&ClientConnection::ExitGameAndSaveReturned,NULL, app.GetStringTable()); + } + else +#else + // Give the player the option to save their game + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&ClientConnection::ExitGameAndSaveReturned,NULL, app.GetStringTable()); + } + else +#endif + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( true ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + + return 0; +} + +int ClientConnection::ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + //INT saveOrCheckpointId = 0; + //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId); +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( true ); + } + else + { + MinecraftServer::getInstance()->setSaveOnExit( false ); + } + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + return 0; +} + +// +wstring ClientConnection::GetDisplayNameByGamertag(wstring gamertag) +{ +#ifdef _DURANGO + wstring displayName = g_NetworkManager.GetDisplayNameByGamertag(gamertag); + return displayName; +#else + return gamertag; +#endif +} diff --git a/Minecraft.Client/ClientConnection.h b/Minecraft.Client/ClientConnection.h new file mode 100644 index 0000000..2ce46ce --- /dev/null +++ b/Minecraft.Client/ClientConnection.h @@ -0,0 +1,140 @@ +#pragma once +#include "..\Minecraft.World\net.minecraft.network.h" +class Minecraft; +class MultiPlayerLevel; +class SavedDataStorage; +class Socket; +class MultiplayerLocalPlayer; + +class ClientConnection : public PacketListener +{ +private: + enum eClientConnectionConnectingState + { + eCCPreLoginSent = 0, + eCCPreLoginReceived, + eCCLoginSent, + eCCLoginReceived, + eCCConnected + }; +private: + bool done; + Connection *connection; +public: + wstring message; + bool createdOk; // 4J added +private: + Minecraft *minecraft; + MultiPlayerLevel *level; + bool started; + + // 4J Stu - I don't think we are interested in the PlayerInfo data, so I'm not going to use it at the moment + //Map playerInfoMap = new HashMap(); +public: + //List playerInfos = new ArrayList(); + + int maxPlayers; + +public: + bool isStarted() { return started; } // 4J Added + bool isClosed() { return done; } // 4J Added + Socket *getSocket() { return connection->getSocket(); } // 4J Added + +private: + DWORD m_userIndex; // 4J Added +public: + SavedDataStorage *savedDataStorage; + ClientConnection(Minecraft *minecraft, const wstring& ip, int port); + ClientConnection(Minecraft *minecraft, Socket *socket, int iUserIndex = -1); + ~ClientConnection(); + void tick(); + INetworkPlayer *getNetworkPlayer(); + virtual void handleLogin(shared_ptr packet); + virtual void handleAddEntity(shared_ptr packet); + virtual void handleAddExperienceOrb(shared_ptr packet); + virtual void handleAddGlobalEntity(shared_ptr packet); + virtual void handleAddPainting(shared_ptr packet); + virtual void handleSetEntityMotion(shared_ptr packet); + virtual void handleSetEntityData(shared_ptr packet); + virtual void handleAddPlayer(shared_ptr packet); + virtual void handleTeleportEntity(shared_ptr packet); + virtual void handleMoveEntity(shared_ptr packet); + virtual void handleRotateMob(shared_ptr packet); + virtual void handleMoveEntitySmall(shared_ptr packet); + virtual void handleRemoveEntity(shared_ptr packet); + virtual void handleMovePlayer(shared_ptr packet); + + Random *random; + + // 4J Added + virtual void handleChunkVisibilityArea(shared_ptr packet); + + virtual void handleChunkVisibility(shared_ptr packet); + virtual void handleChunkTilesUpdate(shared_ptr packet); + virtual void handleBlockRegionUpdate(shared_ptr packet); + virtual void handleTileUpdate(shared_ptr packet); + virtual void handleDisconnect(shared_ptr packet); + virtual void onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects); + void sendAndDisconnect(shared_ptr packet); + void send(shared_ptr packet); + virtual void handleTakeItemEntity(shared_ptr packet); + virtual void handleChat(shared_ptr packet); + virtual void handleAnimate(shared_ptr packet); + virtual void handleEntityActionAtPosition(shared_ptr packet); + virtual void handlePreLogin(shared_ptr packet); + void close(); + virtual void handleAddMob(shared_ptr packet); + virtual void handleSetTime(shared_ptr packet); + virtual void handleSetSpawn(shared_ptr packet); + virtual void handleRidePacket(shared_ptr packet); + virtual void handleEntityEvent(shared_ptr packet); +private: + shared_ptr getEntity(int entityId); + wstring GetDisplayNameByGamertag(wstring gamertag); +public: + virtual void handleSetHealth(shared_ptr packet); + virtual void handleSetExperience(shared_ptr packet); + virtual void handleRespawn(shared_ptr packet); + virtual void handleExplosion(shared_ptr packet); + virtual void handleContainerOpen(shared_ptr packet); + virtual void handleContainerSetSlot(shared_ptr packet); + virtual void handleContainerAck(shared_ptr packet); + virtual void handleContainerContent(shared_ptr packet); + virtual void handleSignUpdate(shared_ptr packet); + virtual void handleTileEntityData(shared_ptr packet); + virtual void handleContainerSetData(shared_ptr packet); + virtual void handleSetEquippedItem(shared_ptr packet); + virtual void handleContainerClose(shared_ptr packet); + virtual void handleTileEvent(shared_ptr packet); + virtual void handleTileDestruction(shared_ptr packet); + virtual bool canHandleAsyncPackets(); + virtual void handleGameEvent(shared_ptr gameEventPacket); + virtual void handleComplexItemData(shared_ptr packet); + virtual void handleLevelEvent(shared_ptr packet); + virtual void handleAwardStat(shared_ptr packet); + virtual void handleUpdateMobEffect(shared_ptr packet); + virtual void handleRemoveMobEffect(shared_ptr packet); + virtual bool isServerPacketListener(); + virtual void handlePlayerInfo(shared_ptr packet); + virtual void handleKeepAlive(shared_ptr packet); + virtual void handlePlayerAbilities(shared_ptr playerAbilitiesPacket); + virtual void handleSoundEvent(shared_ptr packet); + virtual void handleCustomPayload(shared_ptr customPayloadPacket); + virtual Connection *getConnection(); + + // 4J Added + virtual void handleServerSettingsChanged(shared_ptr packet); + virtual void handleTexture(shared_ptr packet); + virtual void handleTextureAndGeometry(shared_ptr packet); + virtual void handleUpdateProgress(shared_ptr packet); + + // 4J Added + static int HostDisconnectReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + virtual void handleTextureChange(shared_ptr packet); + virtual void handleTextureAndGeometryChange(shared_ptr packet); + virtual void handleUpdateGameRuleProgressPacket(shared_ptr packet); + virtual void handleXZ(shared_ptr packet); + + void displayPrivilegeChanges(shared_ptr player, unsigned int oldPrivileges); +}; \ No newline at end of file diff --git a/Minecraft.Client/ClientConstants.cpp b/Minecraft.Client/ClientConstants.cpp new file mode 100644 index 0000000..1e4cead --- /dev/null +++ b/Minecraft.Client/ClientConstants.cpp @@ -0,0 +1,5 @@ +#include "stdafx.h" +#include "ClientConstants.h" + +const wstring ClientConstants::VERSION_STRING = wstring(L"Minecraft Xbox ") + VER_FILEVERSION_STR_W;//+ SharedConstants::VERSION_STRING; +bool ClientConstants::SHOW_LCEMP_WATERMARK = true; \ No newline at end of file diff --git a/Minecraft.Client/ClientConstants.h b/Minecraft.Client/ClientConstants.h new file mode 100644 index 0000000..28d16aa --- /dev/null +++ b/Minecraft.Client/ClientConstants.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +class ClientConstants +{ + + // This file holds global constants used by the client. + // The file should be replaced at compile-time with the + // proper settings for the given compilation. For example, + // release builds should replace this file with no-cheat + // settings. + + // INTERNAL DEVELOPMENT SETTINGS +public: + static const wstring VERSION_STRING; + + static const bool DEADMAU5_CAMERA_CHEATS = false; + static bool SHOW_LCEMP_WATERMARK; +}; \ No newline at end of file diff --git a/Minecraft.Client/ClockTexture.cpp b/Minecraft.Client/ClockTexture.cpp new file mode 100644 index 0000000..d8abb78 --- /dev/null +++ b/Minecraft.Client/ClockTexture.cpp @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include "Minecraft.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "MultiplayerLocalPlayer.h" +#include "..\Minecraft.World\JavaMath.h" +#include "Texture.h" +#include "ClockTexture.h" + +ClockTexture::ClockTexture() : StitchedTexture(L"compass") +{ + rot = rota = 0.0; + m_dataTexture = NULL; + m_iPad = XUSER_INDEX_ANY; +} + +ClockTexture::ClockTexture(int iPad, ClockTexture *dataTexture) : StitchedTexture(L"compass") +{ + rot = rota = 0.0; + m_dataTexture = dataTexture; + m_iPad = iPad; +} + +void ClockTexture::cycleFrames() +{ + + Minecraft *mc = Minecraft::GetInstance(); + + double rott = 0; + if (m_iPad >= 0 && m_iPad < XUSER_MAX_COUNT && mc->level != NULL && mc->localplayers[m_iPad] != NULL) + { + float time = mc->localplayers[m_iPad]->level->getTimeOfDay(1); + rott = time; + if (!mc->localplayers[m_iPad]->level->dimension->isNaturalDimension()) + { + rott = Math::random(); + } + } + else + { + // 4J Stu - For the static version, pretend we are already on a frame other than 0 + frame = 1; + } + + double rotd = rott - rot; + while (rotd < -.5) + rotd += 1.0; + while (rotd >= .5) + rotd -= 1.0; + if (rotd < -1) rotd = -1; + if (rotd > 1) rotd = 1; + rota += rotd * 0.1; + rota *= 0.8; + + rot += rota; + + // 4J Stu - We share data with another texture + if(m_dataTexture != NULL) + { + int newFrame = (int) ((rot + 1.0) * m_dataTexture->frames->size()) % m_dataTexture->frames->size(); + while (newFrame < 0) + { + newFrame = (newFrame + m_dataTexture->frames->size()) % m_dataTexture->frames->size(); + } + if (newFrame != frame) + { + frame = newFrame; + m_dataTexture->source->blit(x, y, m_dataTexture->frames->at(this->frame), rotated); + } + } + else + { + int newFrame = (int) ((rot + 1.0) * frames->size()) % frames->size(); + while (newFrame < 0) + { + newFrame = (newFrame + frames->size()) % frames->size(); + } + if (newFrame != frame) + { + frame = newFrame; + source->blit(x, y, frames->at(this->frame), rotated); + } + } +} + +int ClockTexture::getSourceWidth() const +{ + return source->getWidth(); +} + +int ClockTexture::getSourceHeight() const +{ + return source->getHeight(); +} + +int ClockTexture::getFrames() +{ + if(m_dataTexture == NULL) + { + return StitchedTexture::getFrames(); + } + else + { + return m_dataTexture->getFrames(); + } +} + +void ClockTexture::freeFrameTextures() +{ + if(m_dataTexture == NULL) + { + StitchedTexture::freeFrameTextures(); + } +} + +bool ClockTexture::hasOwnData() +{ + return m_dataTexture == NULL; +} \ No newline at end of file diff --git a/Minecraft.Client/ClockTexture.h b/Minecraft.Client/ClockTexture.h new file mode 100644 index 0000000..e042887 --- /dev/null +++ b/Minecraft.Client/ClockTexture.h @@ -0,0 +1,21 @@ +#pragma once +#include "StitchedTexture.h" + +class ClockTexture : public StitchedTexture +{ +private: + double rot, rota; + int m_iPad; + ClockTexture* m_dataTexture; + +public: + ClockTexture(); + ClockTexture(int iPad, ClockTexture *dataTexture); + void cycleFrames(); + + virtual int getSourceWidth() const; + virtual int getSourceHeight() const; + virtual int getFrames(); + virtual void freeFrameTextures(); // 4J added + virtual bool hasOwnData(); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h new file mode 100644 index 0000000..de1d1bd --- /dev/null +++ b/Minecraft.Client/Common/App_Defines.h @@ -0,0 +1,130 @@ +#pragma once + + +// 4J Stu - For non-splitscreen menus, default to this screen +#define DEFAULT_XUI_MENU_USER 0 +#define MULTITHREAD_ENABLE +#define MAX_CAPENAME_SIZE 32 +#define MAX_BANNERNAME_SIZE 32 +#define MAX_TMSFILENAME_SIZE 40 +#define MAX_TYPE_SIZE 32 +#define MAX_EXTENSION_TYPES 3 + +#ifdef __PSVITA__ +#define MAX_LOCAL_PLAYERS 1 +#else +#define MAX_LOCAL_PLAYERS 4 +#endif + +// 4J Stu - Required for sentient reporting of whether the volume level has been changed or not +#define DEFAULT_VOLUME_LEVEL 100 + +#define GAME_HOST_OPTION_BITMASK_DIFFICULTY 0x00000003 // 0 - 3 +#define GAME_HOST_OPTION_BITMASK_FRIENDSOFFRIENDS 0x00000004 +#define GAME_HOST_OPTION_BITMASK_GAMERTAGS 0x00000008 +#define GAME_HOST_OPTION_BITMASK_GAMETYPE 0x00000030 +#define GAME_HOST_OPTION_BITMASK_LEVELTYPE 0x00000040 +#define GAME_HOST_OPTION_BITMASK_STRUCTURES 0x00000080 +#define GAME_HOST_OPTION_BITMASK_BONUSCHEST 0x00000100 +#define GAME_HOST_OPTION_BITMASK_BEENINCREATIVE 0x00000200 +#define GAME_HOST_OPTION_BITMASK_PVP 0x00000400 +#define GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS 0x00000800 +#define GAME_HOST_OPTION_BITMASK_TNT 0x00001000 +#define GAME_HOST_OPTION_BITMASK_FIRESPREADS 0x00002000 +#define GAME_HOST_OPTION_BITMASK_HOSTFLY 0x00004000 +#define GAME_HOST_OPTION_BITMASK_HOSTHUNGER 0x00008000 +#define GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE 0x00010000 +#define GAME_HOST_OPTION_BITMASK_BEDROCKFOG 0x00020000 +#define GAME_HOST_OPTION_BITMASK_DISABLESAVE 0x00040000 +#define GAME_HOST_OPTION_BITMASK_ALL 0xFFFFFFFF + +#ifdef _XBOX +#define PROFILE_VERSION_1 1 +#define PROFILE_VERSION_2 2 +#define PROFILE_VERSION_3 3 +#define PROFILE_VERSION_4 4 +#define PROFILE_VERSION_5 6 +#define PROFILE_VERSION_6 7 +#define PROFILE_VERSION_7 8 +#endif +#define PROFILE_VERSION_8 10 +#define PROFILE_VERSION_9 11 + +#define PROFILE_VERSION_10 12 + +// 4J-JEV: New Statistics and Achievements for 'NexGen' platforms. +#define PROFILE_VERSION_BUILD_JUNE14 13 + +#define MAX_FAVORITE_SKINS 10 // these are stored in the profile data so keep it small + + + + + +// defines for game settings - uiBitmaskValues + +#define GAMESETTING_CLOUDS 0x00000001 +#define GAMESETTING_ONLINE 0x00000002 +#define GAMESETTING_INVITEONLY 0x00000004 +#define GAMESETTING_FRIENDSOFFRIENDS 0x00000008 +#define GAMESETTING_DISPLAYUPDATEMSG 0x00000030 +#define GAMESETTING_BEDROCKFOG 0x00000040 +#define GAMESETTING_DISPLAYHUD 0x00000080 +#define GAMESETTING_DISPLAYHAND 0x00000100 +#define GAMESETTING_CUSTOMSKINANIM 0x00000200 +#define GAMESETTING_DEATHMESSAGES 0x00000400 +#define GAMESETTING_UISIZE 0x00001800 +#define GAMESETTING_UISIZE_SPLITSCREEN 0x00006000 +#define GAMESETTING_ANIMATEDCHARACTER 0x00008000 +#define GAMESETTING_PS3EULAREAD 0x00010000 +#define GAMESETTING_PSVITANETWORKMODEADHOC 0x00020000 + + +// defines for languages + +#define MINECRAFT_LANGUAGE_DEFAULT 0x00 +#define MINECRAFT_LANGUAGE_ENGLISH 0x01 +#define MINECRAFT_LANGUAGE_JAPANESE 0x02 +#define MINECRAFT_LANGUAGE_GERMAN 0x03 +#define MINECRAFT_LANGUAGE_FRENCH 0x04 +#define MINECRAFT_LANGUAGE_SPANISH 0x05 +#define MINECRAFT_LANGUAGE_ITALIAN 0x06 +#define MINECRAFT_LANGUAGE_KOREAN 0x07 +#define MINECRAFT_LANGUAGE_TCHINESE 0x08 +#define MINECRAFT_LANGUAGE_PORTUGUESE 0x09 +#define MINECRAFT_LANGUAGE_BRAZILIAN 0x0A +#define MINECRAFT_LANGUAGE_RUSSIAN 0x0B +#define MINECRAFT_LANGUAGE_DUTCH 0x0C +#define MINECRAFT_LANGUAGE_FINISH 0x0D +#define MINECRAFT_LANGUAGE_SWEDISH 0x0E +#define MINECRAFT_LANGUAGE_DANISH 0x0F +#define MINECRAFT_LANGUAGE_NORWEGIAN 0x10 +#define MINECRAFT_LANGUAGE_POLISH 0x11 +#define MINECRAFT_LANGUAGE_TURKISH 0x12 +#define MINECRAFT_LANGUAGE_LATINAMERICANSPANISH 0x13 +#define MINECRAFT_LANGUAGE_GREEK 0x14 + + + /* Match these + + const int XC_LANGUAGE_ENGLISH =1; + const int XC_LANGUAGE_JAPANESE =2; + const int XC_LANGUAGE_GERMAN =3; + const int XC_LANGUAGE_FRENCH =4; + const int XC_LANGUAGE_SPANISH =5; + const int XC_LANGUAGE_ITALIAN =6; + const int XC_LANGUAGE_KOREAN =7; + const int XC_LANGUAGE_TCHINESE =8; + const int XC_LANGUAGE_PORTUGUESE =9; + const int XC_LANGUAGE_BRAZILIAN =10; + const int XC_LANGUAGE_RUSSIAN =11; + const int XC_LANGUAGE_DUTCH =12; + const int XC_LANGUAGE_FINISH =13; + const int XC_LANGUAGE_SWEDISH =14; + const int XC_LANGUAGE_DANISH =15; + const int XC_LANGUAGE_NORWEGIAN =16; + const int XC_LANGUAGE_POLISH =17; + const int XC_LANGUAGE_TURKISH =18; + const int XC_LANGUAGE_LATINAMERICANSPANISH =19; + const int XC_LANGUAGE_GREEK =20; + */ diff --git a/Minecraft.Client/Common/App_enums.h b/Minecraft.Client/Common/App_enums.h new file mode 100644 index 0000000..b6c484d --- /dev/null +++ b/Minecraft.Client/Common/App_enums.h @@ -0,0 +1,913 @@ +#pragma once + +enum eFileExtensionType +{ + eFileExtensionType_PNG=0, + eFileExtensionType_INF, + eFileExtensionType_DAT, +}; + +enum eTMSFileType +{ + eTMSFileType_MinecraftStore=0, + eTMSFileType_TexturePack, + eTMSFileType_All +}; + +enum eTPDFileType +{ + eTPDFileType_Loc=0, + eTPDFileType_Icon, +// eTPDFileType_Banner, + eTPDFileType_Comparison, +}; + +enum eFont +{ + eFont_European=0, + eFont_Korean, + eFont_Japanese, + eFont_Chinese, + eFont_None, // to fallback to nothing +}; + +enum eXuiAction +{ + eAppAction_Idle=0, + eAppAction_SaveGame, + eAppAction_SaveGameCapturedThumbnail, + eAppAction_ExitWorld, + eAppAction_ExitWorldCapturedThumbnail, + eAppAction_ExitWorldTrial, + //eAppAction_ExitGameFatalLoadError, + eAppAction_Respawn, + eAppAction_WaitForRespawnComplete, + eAppAction_PrimaryPlayerSignedOut, + eAppAction_PrimaryPlayerSignedOutReturned, + eAppAction_PrimaryPlayerSignedOutReturned_Menus, + eAppAction_ExitPlayer, // secondary player + eAppAction_ExitPlayerPreLogin, + eAppAction_TrialOver, + eAppAction_ExitTrial, + eAppAction_WaitForDimensionChangeComplete, + eAppAction_SocialPost, + eAppAction_SocialPostScreenshot, + eAppAction_EthernetDisconnected, + eAppAction_EthernetDisconnectedReturned, + eAppAction_EthernetDisconnectedReturned_Menus, + eAppAction_ExitAndJoinFromInvite, + eAppAction_DashboardTrialJoinFromInvite, + eAppAction_ExitAndJoinFromInviteConfirmed, + eAppAction_JoinFromInvite, + eAppAction_ChangeSessionType, + eAppAction_SetDefaultOptions, + eAppAction_LocalPlayerJoined, + eAppAction_RemoteServerSave, + eAppAction_WaitRemoteServerSaveComplete, + eAppAction_FailedToJoinNoPrivileges, + eAppAction_AutosaveSaveGame, + eAppAction_AutosaveSaveGameCapturedThumbnail, + eAppAction_ProfileReadError, + eAppAction_DisplayLavaMessage, + eAppAction_BanLevel, + eAppAction_LevelInBanLevelList, + + eAppAction_ReloadTexturePack, + eAppAction_TexturePackRequired, // when the user has joined from invite, but doesn't have the texture pack + +#ifdef __ORBIS__ + eAppAction_OptionsSaveNoSpace, +#endif + eAppAction_DebugText, + +}; + + + +enum eTMSAction +{ + eTMSAction_Idle=0, + eTMSAction_TMS_RetrieveFiles_Complete, + eTMSAction_TMSPP_RetrieveFiles_CreateLoad_SignInReturned, + eTMSAction_TMSPP_RetrieveFiles_RunPlayGame, + eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions, + eTMSAction_TMSPP_RetrieveFiles_DLCMain, + eTMSAction_TMSPP_GlobalFileList, + eTMSAction_TMSPP_GlobalFileList_Waiting, +// eTMSAction_TMSPP_ConfigFile, +// eTMSAction_TMSPP_ConfigFile_Waiting, + eTMSAction_TMSPP_UserFileList, + eTMSAction_TMSPP_UserFileList_Waiting, + eTMSAction_TMSPP_XUIDSFile, + eTMSAction_TMSPP_XUIDSFile_Waiting, + eTMSAction_TMSPP_DLCFile, + eTMSAction_TMSPP_DLCFile_Waiting, + eTMSAction_TMSPP_BannedListFile, + eTMSAction_TMSPP_BannedListFile_Waiting, + eTMSAction_TMSPP_RetrieveFiles_Complete, + eTMSAction_TMSPP_DLCFileOnly, + eTMSAction_TMSPP_RetrieveUserFilelist_DLCFileOnly, +}; + +// The server runs on its own thread, so we need to call its actions there rather than where all other Xui actions are performed +// In general these are debugging options +enum eXuiServerAction +{ + eXuiServerAction_Idle=0, + eXuiServerAction_DropItem, // Debug + eXuiServerAction_SaveGame, + eXuiServerAction_AutoSaveGame, + eXuiServerAction_SpawnMob, // Debug + eXuiServerAction_PauseServer, + eXuiServerAction_ToggleRain, // Debug + eXuiServerAction_ToggleThunder, // Debug + eXuiServerAction_ServerSettingChanged_Gamertags, + eXuiServerAction_ServerSettingChanged_Difficulty, + eXuiServerAction_ExportSchematic, //Debug + eXuiServerAction_ServerSettingChanged_BedrockFog, + eXuiServerAction_SetCameraLocation, //Debug +}; + +enum eGameSetting +{ + eGameSetting_MusicVolume=0, + eGameSetting_SoundFXVolume, + eGameSetting_Gamma, + eGameSetting_Difficulty, + eGameSetting_Sensitivity_InGame, + eGameSetting_Sensitivity_InMenu, + eGameSetting_ViewBob, + eGameSetting_ControlScheme, + eGameSetting_ControlInvertLook, + eGameSetting_ControlSouthPaw, + eGameSetting_SplitScreenVertical, + eGameSetting_GamertagsVisible, + // Interim TU 1.6.6 + eGameSetting_Autosave, + eGameSetting_DisplaySplitscreenGamertags, + eGameSetting_Hints, + eGameSetting_InterfaceOpacity, + eGameSetting_Tooltips, + // TU5 + eGameSetting_Clouds, + eGameSetting_Online, + eGameSetting_InviteOnly, + eGameSetting_FriendsOfFriends, + eGameSetting_DisplayUpdateMessage, + + // TU6 + eGameSetting_BedrockFog, + eGameSetting_DisplayHUD, + eGameSetting_DisplayHand, + + // TU7 + eGameSetting_CustomSkinAnim, + + // TU9 + eGameSetting_DeathMessages, + eGameSetting_UISize, + eGameSetting_UISizeSplitscreen, + eGameSetting_AnimatedCharacter, + + // PS3 + eGameSetting_PS3_EULA_Read, + + // PSVita + eGameSetting_PSVita_NetworkModeAdhoc, + + +}; + + + +enum eGameMode +{ + eMode_Singleplayer, + eMode_Multiplayer +}; + + +enum eMinecraftColour +{ + eMinecraftColour_NOT_SET, + + eMinecraftColour_Foliage_Evergreen, + eMinecraftColour_Foliage_Birch, + eMinecraftColour_Foliage_Default, + eMinecraftColour_Foliage_Common, + eMinecraftColour_Foliage_Ocean, + eMinecraftColour_Foliage_Plains, + eMinecraftColour_Foliage_Desert, + eMinecraftColour_Foliage_ExtremeHills, + eMinecraftColour_Foliage_Forest, + eMinecraftColour_Foliage_Taiga, + eMinecraftColour_Foliage_Swampland, + eMinecraftColour_Foliage_River, + eMinecraftColour_Foliage_Hell, + eMinecraftColour_Foliage_Sky, + eMinecraftColour_Foliage_FrozenOcean, + eMinecraftColour_Foliage_FrozenRiver, + eMinecraftColour_Foliage_IcePlains, + eMinecraftColour_Foliage_IceMountains, + eMinecraftColour_Foliage_MushroomIsland, + eMinecraftColour_Foliage_MushroomIslandShore, + eMinecraftColour_Foliage_Beach, + eMinecraftColour_Foliage_DesertHills, + eMinecraftColour_Foliage_ForestHills, + eMinecraftColour_Foliage_TaigaHills, + eMinecraftColour_Foliage_ExtremeHillsEdge, + eMinecraftColour_Foliage_Jungle, + eMinecraftColour_Foliage_JungleHills, + + eMinecraftColour_Grass_Common, + eMinecraftColour_Grass_Ocean, + eMinecraftColour_Grass_Plains, + eMinecraftColour_Grass_Desert, + eMinecraftColour_Grass_ExtremeHills, + eMinecraftColour_Grass_Forest, + eMinecraftColour_Grass_Taiga, + eMinecraftColour_Grass_Swampland, + eMinecraftColour_Grass_River, + eMinecraftColour_Grass_Hell, + eMinecraftColour_Grass_Sky, + eMinecraftColour_Grass_FrozenOcean, + eMinecraftColour_Grass_FrozenRiver, + eMinecraftColour_Grass_IcePlains, + eMinecraftColour_Grass_IceMountains, + eMinecraftColour_Grass_MushroomIsland, + eMinecraftColour_Grass_MushroomIslandShore, + eMinecraftColour_Grass_Beach, + eMinecraftColour_Grass_DesertHills, + eMinecraftColour_Grass_ForestHills, + eMinecraftColour_Grass_TaigaHills, + eMinecraftColour_Grass_ExtremeHillsEdge, + eMinecraftColour_Grass_Jungle, + eMinecraftColour_Grass_JungleHills, + + eMinecraftColour_Water_Ocean, + eMinecraftColour_Water_Plains, + eMinecraftColour_Water_Desert, + eMinecraftColour_Water_ExtremeHills, + eMinecraftColour_Water_Forest, + eMinecraftColour_Water_Taiga, + eMinecraftColour_Water_Swampland, + eMinecraftColour_Water_River, + eMinecraftColour_Water_Hell, + eMinecraftColour_Water_Sky, + eMinecraftColour_Water_FrozenOcean, + eMinecraftColour_Water_FrozenRiver, + eMinecraftColour_Water_IcePlains, + eMinecraftColour_Water_IceMountains, + eMinecraftColour_Water_MushroomIsland, + eMinecraftColour_Water_MushroomIslandShore, + eMinecraftColour_Water_Beach, + eMinecraftColour_Water_DesertHills, + eMinecraftColour_Water_ForestHills, + eMinecraftColour_Water_TaigaHills, + eMinecraftColour_Water_ExtremeHillsEdge, + eMinecraftColour_Water_Jungle, + eMinecraftColour_Water_JungleHills, + + eMinecraftColour_Sky_Ocean, + eMinecraftColour_Sky_Plains, + eMinecraftColour_Sky_Desert, + eMinecraftColour_Sky_ExtremeHills, + eMinecraftColour_Sky_Forest, + eMinecraftColour_Sky_Taiga, + eMinecraftColour_Sky_Swampland, + eMinecraftColour_Sky_River, + eMinecraftColour_Sky_Hell, + eMinecraftColour_Sky_Sky, + eMinecraftColour_Sky_FrozenOcean, + eMinecraftColour_Sky_FrozenRiver, + eMinecraftColour_Sky_IcePlains, + eMinecraftColour_Sky_IceMountains, + eMinecraftColour_Sky_MushroomIsland, + eMinecraftColour_Sky_MushroomIslandShore, + eMinecraftColour_Sky_Beach, + eMinecraftColour_Sky_DesertHills, + eMinecraftColour_Sky_ForestHills, + eMinecraftColour_Sky_TaigaHills, + eMinecraftColour_Sky_ExtremeHillsEdge, + eMinecraftColour_Sky_Jungle, + eMinecraftColour_Sky_JungleHills, + + eMinecraftColour_Tile_RedstoneDust, + eMinecraftColour_Tile_RedstoneDustUnlit, + eMinecraftColour_Tile_RedstoneDustLitMin, + eMinecraftColour_Tile_RedstoneDustLitMax, + eMinecraftColour_Tile_StemMin, + eMinecraftColour_Tile_StemMax, + eMinecraftColour_Tile_WaterLily, + + eMinecraftColour_Sky_Dawn_Dark, + eMinecraftColour_Sky_Dawn_Bright, + + eMinecraftColour_Material_None, + eMinecraftColour_Material_Grass, + eMinecraftColour_Material_Sand, + eMinecraftColour_Material_Cloth, + eMinecraftColour_Material_Fire, + eMinecraftColour_Material_Ice, + eMinecraftColour_Material_Metal, + eMinecraftColour_Material_Plant, + eMinecraftColour_Material_Snow, + eMinecraftColour_Material_Clay, + eMinecraftColour_Material_Dirt, + eMinecraftColour_Material_Stone, + eMinecraftColour_Material_Water, + eMinecraftColour_Material_Wood, + eMinecraftColour_Material_Emerald, + + eMinecraftColour_Particle_Note_00, + eMinecraftColour_Particle_Note_01, + eMinecraftColour_Particle_Note_02, + eMinecraftColour_Particle_Note_03, + eMinecraftColour_Particle_Note_04, + eMinecraftColour_Particle_Note_05, + eMinecraftColour_Particle_Note_06, + eMinecraftColour_Particle_Note_07, + eMinecraftColour_Particle_Note_08, + eMinecraftColour_Particle_Note_09, + eMinecraftColour_Particle_Note_10, + eMinecraftColour_Particle_Note_11, + eMinecraftColour_Particle_Note_12, + eMinecraftColour_Particle_Note_13, + eMinecraftColour_Particle_Note_14, + eMinecraftColour_Particle_Note_15, + eMinecraftColour_Particle_Note_16, + eMinecraftColour_Particle_Note_17, + eMinecraftColour_Particle_Note_18, + eMinecraftColour_Particle_Note_19, + eMinecraftColour_Particle_Note_20, + eMinecraftColour_Particle_Note_21, + eMinecraftColour_Particle_Note_22, + eMinecraftColour_Particle_Note_23, + eMinecraftColour_Particle_Note_24, + + eMinecraftColour_Particle_NetherPortal, + eMinecraftColour_Particle_EnderPortal, + eMinecraftColour_Particle_Smoke, + eMinecraftColour_Particle_Ender, + eMinecraftColour_Particle_Explode, + eMinecraftColour_Particle_HugeExplosion, + eMinecraftColour_Particle_DripWater, + eMinecraftColour_Particle_DripLavaStart, + eMinecraftColour_Particle_DripLavaEnd, + eMinecraftColour_Particle_EnchantmentTable, + eMinecraftColour_Particle_DragonBreathMin, + eMinecraftColour_Particle_DragonBreathMax, + eMinecraftColour_Particle_Suspend, + eMinecraftColour_Particle_CritStart, + eMinecraftColour_Particle_CritEnd, + + eMinecraftColour_Effect_MovementSpeed, + eMinecraftColour_Effect_MovementSlowDown, + eMinecraftColour_Effect_DigSpeed, + eMinecraftColour_Effect_DigSlowdown, + eMinecraftColour_Effect_DamageBoost, + eMinecraftColour_Effect_Heal, + eMinecraftColour_Effect_Harm, + eMinecraftColour_Effect_Jump, + eMinecraftColour_Effect_Confusion, + eMinecraftColour_Effect_Regeneration, + eMinecraftColour_Effect_DamageResistance, + eMinecraftColour_Effect_FireResistance, + eMinecraftColour_Effect_WaterBreathing, + eMinecraftColour_Effect_Invisiblity, + eMinecraftColour_Effect_Blindness, + eMinecraftColour_Effect_NightVision, + eMinecraftColour_Effect_Hunger, + eMinecraftColour_Effect_Weakness, + eMinecraftColour_Effect_Poison, + + eMinecraftColour_Potion_BaseColour, + + eMinecraftColour_Mob_Creeper_Colour1, + eMinecraftColour_Mob_Creeper_Colour2, + eMinecraftColour_Mob_Skeleton_Colour1, + eMinecraftColour_Mob_Skeleton_Colour2, + eMinecraftColour_Mob_Spider_Colour1, + eMinecraftColour_Mob_Spider_Colour2, + eMinecraftColour_Mob_Zombie_Colour1, + eMinecraftColour_Mob_Zombie_Colour2, + eMinecraftColour_Mob_Slime_Colour1, + eMinecraftColour_Mob_Slime_Colour2, + eMinecraftColour_Mob_Ghast_Colour1, + eMinecraftColour_Mob_Ghast_Colour2, + eMinecraftColour_Mob_PigZombie_Colour1, + eMinecraftColour_Mob_PigZombie_Colour2, + eMinecraftColour_Mob_Enderman_Colour1, + eMinecraftColour_Mob_Enderman_Colour2, + eMinecraftColour_Mob_CaveSpider_Colour1, + eMinecraftColour_Mob_CaveSpider_Colour2, + eMinecraftColour_Mob_Silverfish_Colour1, + eMinecraftColour_Mob_Silverfish_Colour2, + eMinecraftColour_Mob_Blaze_Colour1, + eMinecraftColour_Mob_Blaze_Colour2, + eMinecraftColour_Mob_LavaSlime_Colour1, + eMinecraftColour_Mob_LavaSlime_Colour2, + eMinecraftColour_Mob_Pig_Colour1, + eMinecraftColour_Mob_Pig_Colour2, + eMinecraftColour_Mob_Sheep_Colour1, + eMinecraftColour_Mob_Sheep_Colour2, + eMinecraftColour_Mob_Cow_Colour1, + eMinecraftColour_Mob_Cow_Colour2, + eMinecraftColour_Mob_Chicken_Colour1, + eMinecraftColour_Mob_Chicken_Colour2, + eMinecraftColour_Mob_Squid_Colour1, + eMinecraftColour_Mob_Squid_Colour2, + eMinecraftColour_Mob_Wolf_Colour1, + eMinecraftColour_Mob_Wolf_Colour2, + eMinecraftColour_Mob_MushroomCow_Colour1, + eMinecraftColour_Mob_MushroomCow_Colour2, + eMinecraftColour_Mob_Ocelot_Colour1, + eMinecraftColour_Mob_Ocelot_Colour2, + eMinecraftColour_Mob_Villager_Colour1, + eMinecraftColour_Mob_Villager_Colour2, + + eMinecraftColour_Armour_Default_Leather_Colour, + + eMinecraftColour_Under_Water_Clear_Colour, + eMinecraftColour_Under_Lava_Clear_Colour, + eMinecraftColour_In_Cloud_Base_Colour, + + eMinecraftColour_Under_Water_Fog_Colour, + eMinecraftColour_Under_Lava_Fog_Colour, + eMinecraftColour_In_Cloud_Fog_Colour, + + eMinecraftColour_Default_Fog_Colour, + eMinecraftColour_Nether_Fog_Colour, + eMinecraftColour_End_Fog_Colour, + + eMinecraftColour_Sign_Text, + eMinecraftColour_Map_Text, + + eHTMLColor_0, + eHTMLColor_1, + eHTMLColor_2, + eHTMLColor_3, + eHTMLColor_4, + eHTMLColor_5, + eHTMLColor_6, + eHTMLColor_7, + eHTMLColor_8, + eHTMLColor_9, + eHTMLColor_a, + eHTMLColor_b, + eHTMLColor_c, + eHTMLColor_d, + eHTMLColor_e, + eHTMLColor_f, + eHTMLColor_0_dark, + eHTMLColor_1_dark, + eHTMLColor_2_dark, + eHTMLColor_3_dark, + eHTMLColor_4_dark, + eHTMLColor_5_dark, + eHTMLColor_6_dark, + eHTMLColor_7_dark, + eHTMLColor_8_dark, + eHTMLColor_9_dark, + eHTMLColor_a_dark, + eHTMLColor_b_dark, + eHTMLColor_c_dark, + eHTMLColor_d_dark, + eHTMLColor_e_dark, + eHTMLColor_f_dark, + eHTMLColor_T1, + eHTMLColor_T2, + eHTMLColor_T3, + eHTMLColor_Black, + eHTMLColor_White, + + eTextColor_Enchant, + eTextColor_EnchantFocus, + eTextColor_EnchantDisabled, + eTextColor_RenamedItemTitle, + + //eHTMLColor_0 = 0x000000, //r:0 , g: 0, b: 0, i: 0 + //eHTMLColor_1 = 0x0000aa, //r:0 , g: 0, b: aa, i: 1 + //eHTMLColor_2 = 0x109e10, // Changed by request of Dave //0x00aa00, //r:0 , g: aa, b: 0, i: 2 + //eHTMLColor_3 = 0x109e9e, // Changed by request of Dave //0x00aaaa, //r:0 , g: aa, b: aa, i: 3 + //eHTMLColor_4 = 0xaa0000, //r:aa , g: 0, b: 0, i: 4 + //eHTMLColor_5 = 0xaa00aa, //r:aa , g: 0, b: aa, i: 5 + //eHTMLColor_6 = 0xffaa00, //r:ff , g: aa, b: 0, i: 6 + //eHTMLColor_7 = 0xaaaaaa, //r:aa , g: aa, b: aa, i: 7 + //eHTMLColor_8 = 0x555555, //r:55 , g: 55, b: 55, i: 8 + //eHTMLColor_9 = 0x5555ff, //r:55 , g: 55, b: ff, i: 9 + //eHTMLColor_a = 0x55ff55, //r:55 , g: ff, b: 55, i: a + //eHTMLColor_b = 0x55ffff, //r:55 , g: ff, b: ff, i: b + //eHTMLColor_c = 0xff5555, //r:ff , g: 55, b: 55, i: c + //eHTMLColor_d = 0xff55ff, //r:ff , g: 55, b: ff, i: d + //eHTMLColor_e = 0xffff55, //r:ff , g: ff, b: 55, i: e + //eHTMLColor_f = 0xffffff, //r:ff , g: ff, b: ff, i: f + //eHTMLColor_0_dark = 0x000000, //r:0 , g: 0, b: 0, i: 10 + //eHTMLColor_1_dark = 0x00002a, //r:0 , g: 0, b: 2a, i: 11 + //eHTMLColor_2_dark = 0x002a00, //r:0 , g: 2a, b: 0, i: 12 + //eHTMLColor_3_dark = 0x002a2a, //r:0 , g: 2a, b: 2a, i: 13 + //eHTMLColor_4_dark = 0x2a0000, //r:2a , g: 0, b: 0, i: 14 + //eHTMLColor_5_dark = 0x2a002a, //r:2a , g: 0, b: 2a, i: 15 + //eHTMLColor_6_dark = 0x2a2a00, //r:2a , g: 2a, b: 0, i: 16 + //eHTMLColor_7_dark = 0x2a2a2a, //r:2a , g: 2a, b: 2a, i: 17 + //eHTMLColor_8_dark = 0x151515, //r:15 , g: 15, b: 15, i: 18 + //eHTMLColor_9_dark = 0x15153f, //r:15 , g: 15, b: 3f, i: 19 + //eHTMLColor_a_dark = 0x153f15, //r:15 , g: 3f, b: 15, i: 1a + //eHTMLColor_b_dark = 0x153f3f, //r:15 , g: 3f, b: 3f, i: 1b + //eHTMLColor_c_dark = 0x3f1515, //r:3f , g: 15, b: 15, i: 1c + //eHTMLColor_d_dark = 0x3f153f, //r:3f , g: 15, b: 3f, i: 1d + //eHTMLColor_e_dark = 0x3f3f15, //r:3f , g: 3f, b: 15, i: 1e + //eHTMLColor_f_dark = 0x3f3f3f, //r:3f , g: 3f, b: 3f, i: 1f + + eMinecraftColour_COUNT, +}; + +enum eDLCContentType +{ + e_DLC_SkinPack=0, + e_DLC_TexturePacks, + e_DLC_MashupPacks, + e_DLC_Themes, + e_DLC_AvatarItems, + e_DLC_Gamerpics, + e_DLC_MAX_MinecraftStore, + e_DLC_TexturePackData, // for the icon, banner and text + e_DLC_MAX, + e_DLC_NotDefined, +}; + +enum eDLCMarketplaceType +{ + e_Marketplace_Content=0, // skins, texture packs and mashup packs + e_Marketplace_Themes, + e_Marketplace_AvatarItems, + e_Marketplace_Gamerpics, + e_Marketplace_MAX, + e_Marketplace_NotDefined, +}; + +enum eDLCContentState +{ + e_DLC_ContentState_Idle = 0, + e_DLC_ContentState_Retrieving, + e_DLC_ContentState_Retrieved +}; + +enum eTMSContentState +{ + e_TMS_ContentState_Idle = 0, + e_TMS_ContentState_Queued, + e_TMS_ContentState_Retrieving, + e_TMS_ContentState_Retrieved +}; + +enum eXUID +{ + eXUID_Undefined=0, + eXUID_NoName, // name not needed + eXUID_Notch, + eXUID_Carl, + eXUID_Daniel, + eXUID_Deadmau5, + eXUID_DannyBStyle, + eXUID_JulianClark, + eXUID_Millionth, + eXUID_4JPaddy, + eXUID_4JStuart, + eXUID_4JDavid, + eXUID_4JRichard, + eXUID_4JSteven, +}; + + +enum _eTerrainFeatureType +{ + eTerrainFeature_None=0, + eTerrainFeature_Stronghold, + eTerrainFeature_Mineshaft, + eTerrainFeature_Village, + eTerrainFeature_Ravine, + eTerrainFeature_NetherFortress, + eTerrainFeature_StrongholdEndPortal, + eTerrainFeature_Count +}; + +// 4J Stu - Whend adding new options you should consider whether having them on should disable achievements, and if so add them to the CanRecordStatsAndAchievements function +// 4J Stu - These options are now saved in save data, so new options can ONLY be added to the end +enum eGameHostOption +{ + eGameHostOption_Difficulty=0, + eGameHostOption_OnlineGame, // Unused + eGameHostOption_InviteOnly, // Unused + eGameHostOption_FriendsOfFriends, + eGameHostOption_Gamertags, + eGameHostOption_Tutorial, // special case + eGameHostOption_GameType, + eGameHostOption_LevelType, // flat or default + eGameHostOption_Structures, + eGameHostOption_BonusChest, + eGameHostOption_HasBeenInCreative, + eGameHostOption_PvP, + eGameHostOption_TrustPlayers, + eGameHostOption_TNT, + eGameHostOption_FireSpreads, + eGameHostOption_CheatsEnabled, // special case + eGameHostOption_HostCanFly, + eGameHostOption_HostCanChangeHunger, + eGameHostOption_HostCanBeInvisible, + eGameHostOption_BedrockFog, + eGameHostOption_NoHUD, + eGameHostOption_All, + + eGameHostOption_DisableSaving, +}; + +// 4J-PB - If any new DLC items are added to the TMSFiles, this array needs updated +#ifdef _XBOX +enum _TMSFILES +{ + TMS_SP1=0, + TMS_SP2, + TMS_SP3, + TMS_SP4, + TMS_SP5, + TMS_SP6, + TMS_SPF, + TMS_SPB, + TMS_SPC, + TMS_SPZ, + TMS_SPM, + TMS_SPI, + TMS_SPG, + + TMS_THST, + TMS_THIR, + TMS_THGO, + TMS_THDI, + TMS_THAW, + + TMS_GPAN, + TMS_GPCO, + TMS_GPEN, + TMS_GPFO, + TMS_GPTO, + TMS_GPBA, + TMS_GPFA, + TMS_GPME, + TMS_GPMF, + TMS_GPMM, + TMS_GPSE, + TMS_GPOr, + TMS_GPMi, + TMS_GPMB, + TMS_GPBr, + TMS_GPM1, + TMS_GPM2, + TMS_GPM3, + + TMS_AH_0001, + TMS_AH_0002, + TMS_AH_0003, + TMS_AH_0004, + TMS_AH_0005, + TMS_AH_0006, + TMS_AH_0007, + TMS_AH_0008, + TMS_AH_0009, + TMS_AH_0010, + TMS_AH_0011, + TMS_AH_0012, + TMS_AH_0013, + + TMS_AT_0001, + TMS_AT_0002, + TMS_AT_0003, + TMS_AT_0004, + TMS_AT_0005, + TMS_AT_0006, + TMS_AT_0007, + TMS_AT_0008, + TMS_AT_0009, + TMS_AT_0010, + TMS_AT_0011, + TMS_AT_0012, + TMS_AT_0013, + TMS_AT_0014, + TMS_AT_0015, + TMS_AT_0016, + TMS_AT_0017, + TMS_AT_0018, + TMS_AT_0019, + TMS_AT_0020, + TMS_AT_0021, + TMS_AT_0022, + TMS_AT_0023, + TMS_AT_0024, + TMS_AT_0025, + TMS_AT_0026, + + TMS_AP_0001, + TMS_AP_0002, + TMS_AP_0003, + TMS_AP_0004, + TMS_AP_0005, + TMS_AP_0006, + TMS_AP_0007, + TMS_AP_0009, + TMS_AP_0010, + TMS_AP_0011, + TMS_AP_0012, + TMS_AP_0013, + TMS_AP_0014, + TMS_AP_0015, + TMS_AP_0016, + TMS_AP_0017, + TMS_AP_0018, + + TMS_AP_0019, + TMS_AP_0020, + TMS_AP_0021, + TMS_AP_0022, + TMS_AP_0023, + TMS_AP_0024, + TMS_AP_0025, + TMS_AP_0026, + TMS_AP_0027, + TMS_AP_0028, + TMS_AP_0029, + TMS_AP_0030, + TMS_AP_0031, + TMS_AP_0032, + TMS_AP_0033, + + TMS_AA_0001, + + TMS_MPMA, + TMS_MPMA_DAT, + TMS_MPSR, + TMS_MPSR_DAT, + TMS_MPHA, + TMS_MPHA_DAT, + + TMS_TP01, + TMS_TP01_DAT, + TMS_TP02, + TMS_TP02_DAT, + TMS_TP04, + TMS_TP04_DAT, + TMS_TP05, + TMS_TP05_DAT, + TMS_TP06, + TMS_TP06_DAT, + TMS_TP07, + TMS_TP07_DAT, + + TMS_COUNT +}; +#endif + +enum EHTMLFontSize +{ + eHTMLSize_Normal, + eHTMLSize_Splitscreen, + eHTMLSize_Tutorial, + eHTMLSize_EndPoem, + + eHTMLSize_COUNT, +}; + +enum EControllerActions +{ + ACTION_MENU_A, + ACTION_MENU_B, + ACTION_MENU_X, + ACTION_MENU_Y, + ACTION_MENU_UP, + ACTION_MENU_DOWN, + ACTION_MENU_RIGHT, + ACTION_MENU_LEFT, + ACTION_MENU_PAGEUP, + ACTION_MENU_PAGEDOWN, + ACTION_MENU_RIGHT_SCROLL, + ACTION_MENU_LEFT_SCROLL, + ACTION_MENU_STICK_PRESS, + ACTION_MENU_OTHER_STICK_PRESS, + ACTION_MENU_OTHER_STICK_UP, + ACTION_MENU_OTHER_STICK_DOWN, + ACTION_MENU_OTHER_STICK_LEFT, + ACTION_MENU_OTHER_STICK_RIGHT, + ACTION_MENU_PAUSEMENU, + +#ifdef _DURANGO + ACTION_MENU_GTC_PAUSE, + ACTION_MENU_GTC_RESUME, +#endif + +#ifdef __ORBIS__ + ACTION_MENU_TOUCHPAD_PRESS, +#endif + + ACTION_MENU_OK, + ACTION_MENU_CANCEL, + ACTION_MAX_MENU = ACTION_MENU_CANCEL, + + MINECRAFT_ACTION_JUMP, + MINECRAFT_ACTION_FORWARD, + MINECRAFT_ACTION_BACKWARD, + MINECRAFT_ACTION_LEFT, + MINECRAFT_ACTION_RIGHT, + MINECRAFT_ACTION_LOOK_LEFT, + MINECRAFT_ACTION_LOOK_RIGHT, + MINECRAFT_ACTION_LOOK_UP, + MINECRAFT_ACTION_LOOK_DOWN, + MINECRAFT_ACTION_USE, + MINECRAFT_ACTION_ACTION, + MINECRAFT_ACTION_LEFT_SCROLL, + MINECRAFT_ACTION_RIGHT_SCROLL, + MINECRAFT_ACTION_INVENTORY, + MINECRAFT_ACTION_PAUSEMENU, + MINECRAFT_ACTION_DROP, + MINECRAFT_ACTION_SNEAK_TOGGLE, + MINECRAFT_ACTION_CRAFTING, + MINECRAFT_ACTION_RENDER_THIRD_PERSON, + MINECRAFT_ACTION_GAME_INFO, + MINECRAFT_ACTION_DPAD_LEFT, + MINECRAFT_ACTION_DPAD_RIGHT, + MINECRAFT_ACTION_DPAD_UP, + MINECRAFT_ACTION_DPAD_DOWN, + + MINECRAFT_ACTION_MAX, + + // These 4 aren't mapped to the input manager directly but are created from the dpad controls if required in Minecraft::run_middle + // Don't use them with the input manager directly, just through LocalPlayer::ullButtonsPressed + MINECRAFT_ACTION_SPAWN_CREEPER, + MINECRAFT_ACTION_CHANGE_SKIN, + MINECRAFT_ACTION_FLY_TOGGLE, + MINECRAFT_ACTION_RENDER_DEBUG +}; + +enum eMCLang +{ + eMCLang_null=0, + eMCLang_enUS, + eMCLang_enGB, + eMCLang_enIE, + eMCLang_enAU, + eMCLang_enNZ, + eMCLang_enCA, + eMCLang_jaJP, + eMCLang_deDE, + eMCLang_deAT, + eMCLang_frFR, + eMCLang_frCA, + eMCLang_esES, + eMCLang_esMX, + eMCLang_itIT, + eMCLang_koKR, + eMCLang_ptPT, + eMCLang_ptBR, + eMCLang_ruRU, + eMCLang_nlNL, + eMCLang_fiFI, + eMCLang_svSV, + eMCLang_daDA, + eMCLang_noNO, + eMCLang_plPL, + eMCLang_trTR, + eMCLang_elEL, + eMCLang_zhCHS, + eMCLang_zhCHT, + eMCLang_laLAS, + + eMCLang_zhSG, + eMCLang_zhCN, + eMCLang_zhHK, + eMCLang_zhTW, + eMCLang_nlBE, + eMCLang_daDK, + eMCLang_frBE, + eMCLang_frCH, + eMCLang_deCH, + eMCLang_nbNO, + eMCLang_enGR, + eMCLang_enHK, + eMCLang_enSA, + eMCLang_enHU, + eMCLang_enIN, + eMCLang_enIL, + eMCLang_enSG, + eMCLang_enSK, + eMCLang_enZA, + eMCLang_enCZ, + eMCLang_enAE, + eMCLang_esAR, + eMCLang_esCL, + eMCLang_esCO, + eMCLang_esUS, + eMCLang_svSE, + + eMCLang_csCZ, + eMCLang_elGR, + eMCLang_nnNO, + eMCLang_skSK, +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/App_structs.h b/Minecraft.Client/Common/App_structs.h new file mode 100644 index 0000000..ed321b5 --- /dev/null +++ b/Minecraft.Client/Common/App_structs.h @@ -0,0 +1,227 @@ +#pragma once + +typedef struct +{ + wchar_t *wchFilename; + eFileExtensionType eEXT; + eTMSFileType eTMSType; + PBYTE pbData; + UINT uiSize; + int iConfig; // used for texture pack data files +} +TMS_FILE; + +typedef struct +{ + PBYTE pbData; + DWORD dwBytes; + BYTE ucRefCount; +} +MEMDATA,*PMEMDATA; + +typedef struct +{ + DWORD dwNotification; + UINT uiParam; +} +NOTIFICATION,*PNOTIFICATION; + +typedef struct +{ + bool bSettingsChanged; + unsigned char ucMusicVolume; + unsigned char ucSoundFXVolume; + unsigned char ucSensitivity; + unsigned char ucGamma; + unsigned char ucPad01; // 1 byte of padding inserted here + unsigned short usBitmaskValues; // bit 0,1 - difficulty + // bit 2 - view bob + // bit 3 - player visible in a map + // bit 4,5 - control scheme + // bit 6 - invert look + // bit 7 - southpaw + // bit 8 - splitscreen vertical + + // 4J-PB - Adding new values for interim TU for 1.6.6 + // bit 9 - Display gamertags in splitscreen + // bit 10 - Disable/Enable hints + // bit 11,12,13,14 - Autosave frequency - 0 = Off, 8 = (8*15 minutes) = 2 hours + // bit 15 Tooltips + + // debug values + unsigned int uiDebugBitmask; + + // block off space to use for whatever we want (e.g bitflags for storing things the player has done in the game, so we can flag the first time they do things, such as sleep) + union + { + struct + { + unsigned char ucTutorialCompletion[TUTORIAL_PROFILE_STORAGE_BYTES]; + // adding new flags for interim TU to 1.6.6 + + // A value that encodes the skin that the player has set as their default + DWORD dwSelectedSkin; + + // In-Menu sensitivity + unsigned char ucMenuSensitivity; + unsigned char ucInterfaceOpacity; + unsigned char ucPad02;//2 bytes of padding added here + unsigned char usPad03; + + // Adding another bitmask flag for more settings for 1.8.2 + unsigned int uiBitmaskValues; // 0x00000001 - eGameSetting_Clouds - on + // 0x00000002 - eGameSetting_GameSetting_Online - on + // 0x00000004 - eGameSetting_GameSetting_Invite - off + // 0x00000008 - eGameSetting_GameSetting_FriendsOfFriends - on + // 0x00000010 - eGameSetting_PSVita_NetworkModeAdhoc - on + + // TU 5 + // 0x00000030 - eGameSetting_DisplayUpdateMessage - 3 - counts down to zero + // TU 6 + // 0x00000040 - eGameSetting_BedrockFog - off + // 0x00000080 - eGameSetting_DisplayHUD - on + // 0x00000100 - eGameSetting_DisplayHand - on + // TU 7 + // 0x00000200 - eGameSetting_CustomSkinAnim - on + + // TU9 // 0x00000400 - eGameSetting_DeathMessages - on + + // Adding another bitmask to store "special" completion tasks for the tutorial + unsigned int uiSpecialTutorialBitmask; + + // A value that encodes the cape that the player has set + DWORD dwSelectedCape; + + unsigned int uiFavoriteSkinA[MAX_FAVORITE_SKINS]; + unsigned char ucCurrentFavoriteSkinPos; + + // TU13 + unsigned int uiMashUpPackWorldsDisplay; // bitmask to enable/disable the display of the individual mash-up pack worlds + + // PS3 1.05 - Adding Greek, so need a language + unsigned char ucLanguage; + // 4J Stu - See comment for GAME_SETTINGS_PROFILE_DATA_BYTES below + // was 192 + //unsigned char ucUnused[192-TUTORIAL_PROFILE_STORAGE_BYTES-sizeof(DWORD)-sizeof(char)-sizeof(char)-sizeof(char)-sizeof(char)-sizeof(LONG)-sizeof(LONG)-sizeof(DWORD)]; + // 4J-PB - don't need to define the padded space, the union with ucReservedSpace will make the sizeof GAME_SETTINGS correct + }; + + unsigned char ucReservedSpace[192]; + + + }; +} +GAME_SETTINGS; + +#ifdef _XBOX_ONE +typedef struct +{ + WCHAR wchPlayerUID[64]; + char pszLevelName[14]; +} +BANNEDLISTDATA,*PBANNEDLISTDATA; +#else +typedef struct +{ + PlayerUID xuid; + char pszLevelName[14]; +} +BANNEDLISTDATA,*PBANNEDLISTDATA; +#endif + +typedef std::vector VBANNEDLIST; + +typedef struct +{ + int iPad; + eXuiAction action; +} +XuiActionParam; + +// tips +typedef struct +{ + int iSortValue; + UINT uiStringID; +} +TIPSTRUCT; + + +typedef struct +{ + eXUID eXuid; + WCHAR wchCape[MAX_CAPENAME_SIZE]; + WCHAR wchSkin[MAX_CAPENAME_SIZE]; +} +MOJANG_DATA; + +typedef struct +{ + eDLCContentType eDLCType; +#if defined( __PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + char chImageURL[256];//SCE_NP_COMMERCE2_URL_LEN +#else + +#ifdef _XBOX_ONE + + wstring wsProductId; + wstring wsDisplayName; + + // add a store for the local DLC image + PBYTE pbImageData; + DWORD dwImageBytes; +#else + ULONGLONG ullOfferID_Full; + ULONGLONG ullOfferID_Trial; +#endif + WCHAR wchBanner[MAX_BANNERNAME_SIZE]; + WCHAR wchDataFile[MAX_BANNERNAME_SIZE]; + int iGender; +#endif + int iConfig; + unsigned int uiSortIndex; +} +DLC_INFO; + + +typedef struct +{ + int x,z; + _eTerrainFeatureType eTerrainFeature; +} +FEATURE_DATA; + +// banned list +typedef struct +{ + BYTE *pBannedList; + DWORD dwBytes; +} +BANNEDLIST; + +typedef struct _DLCRequest +{ + DWORD dwType; + eDLCContentState eState; +} +DLCRequest; + +typedef struct _TMSPPRequest +{ + eTMSContentState eState; + eDLCContentType eType; + C4JStorage::eGlobalStorage eStorageFacility; + C4JStorage::eTMS_FILETYPEVAL eFileTypeVal; + //char szFilename[MAX_TMSFILENAME_SIZE]; +#ifdef _XBOX_ONE + int( *CallbackFunc)(LPVOID,int,int,LPVOID, WCHAR *); +#else + int( *CallbackFunc)(LPVOID,int,int,C4JStorage::PTMSPP_FILEDATA, LPCSTR szFilename); +#endif + WCHAR wchFilename[MAX_TMSFILENAME_SIZE]; + + LPVOID lpCallbackParam; +} +TMSPPRequest; + +typedef pair SceneStackPair; diff --git a/Minecraft.Client/Common/Audio/Consoles_SoundEngine.cpp b/Minecraft.Client/Common/Audio/Consoles_SoundEngine.cpp new file mode 100644 index 0000000..269b605 --- /dev/null +++ b/Minecraft.Client/Common/Audio/Consoles_SoundEngine.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "Consoles_SoundEngine.h" + + +bool ConsoleSoundEngine::GetIsPlayingStreamingCDMusic() +{ + return m_bIsPlayingStreamingCDMusic; +} +bool ConsoleSoundEngine::GetIsPlayingStreamingGameMusic() +{ + return m_bIsPlayingStreamingGameMusic; +} +void ConsoleSoundEngine::SetIsPlayingStreamingCDMusic(bool bVal) +{ + m_bIsPlayingStreamingCDMusic=bVal; +} +void ConsoleSoundEngine::SetIsPlayingStreamingGameMusic(bool bVal) +{ + m_bIsPlayingStreamingGameMusic=bVal; +} +bool ConsoleSoundEngine::GetIsPlayingEndMusic() +{ + return m_bIsPlayingEndMusic; +} +bool ConsoleSoundEngine::GetIsPlayingNetherMusic() +{ + return m_bIsPlayingNetherMusic; +} +void ConsoleSoundEngine::SetIsPlayingEndMusic(bool bVal) +{ + m_bIsPlayingEndMusic=bVal; +} +void ConsoleSoundEngine::SetIsPlayingNetherMusic(bool bVal) +{ + m_bIsPlayingNetherMusic=bVal; +} + + diff --git a/Minecraft.Client/Common/Audio/Consoles_SoundEngine.h b/Minecraft.Client/Common/Audio/Consoles_SoundEngine.h new file mode 100644 index 0000000..4ec7603 --- /dev/null +++ b/Minecraft.Client/Common/Audio/Consoles_SoundEngine.h @@ -0,0 +1,81 @@ +#pragma once + +#include "..\..\..\Minecraft.World\SoundTypes.h" + +#ifdef _XBOX + +#elif defined (__PS3__) +#undef __in +#undef __out +#include "..\..\PS3\Miles\include\mss.h" +#elif defined (__PSVITA__) +#include "..\..\PSVITA\Miles\include\mss.h" +#elif defined _DURANGO +// 4J Stu - Temp define to get Miles to link, can likely be removed when we get a new version of Miles +#define _SEKRIT +#include "..\..\Durango\Miles\include\mss.h" +#elif defined _WINDOWS64 +#include "..\..\windows64\Miles\include\mss.h" +#else // PS4 +// 4J Stu - Temp define to get Miles to link, can likely be removed when we get a new version of Miles +#define _SEKRIT2 +#include "..\..\Orbis\Miles\include\mss.h" +#endif + +typedef struct +{ + float x,y,z; +} +AUDIO_VECTOR; + +typedef struct +{ + bool bValid; + AUDIO_VECTOR vPosition; + AUDIO_VECTOR vOrientFront; +} +AUDIO_LISTENER; + +class Options; + +class ConsoleSoundEngine +{ +public: + + ConsoleSoundEngine() : m_bIsPlayingStreamingCDMusic(false),m_bIsPlayingStreamingGameMusic(false), m_bIsPlayingEndMusic(false),m_bIsPlayingNetherMusic(false){}; + virtual void tick(shared_ptr *players, float a) =0; + virtual void destroy()=0; + virtual void play(int iSound, float x, float y, float z, float volume, float pitch) =0; + virtual void playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay=true) =0; + virtual void playUI(int iSound, float volume, float pitch) =0; + virtual void updateMusicVolume(float fVal) =0; + virtual void updateSystemMusicPlaying(bool isPlaying) = 0; + virtual void updateSoundEffectVolume(float fVal) =0; + virtual void init(Options *) =0 ; + virtual void add(const wstring& name, File *file) =0; + virtual void addMusic(const wstring& name, File *file) =0; + virtual void addStreaming(const wstring& name, File *file) =0; + virtual char *ConvertSoundPathToName(const wstring& name, bool bConvertSpaces) =0; + virtual void playMusicTick() =0; + + virtual bool GetIsPlayingStreamingCDMusic() ; + virtual bool GetIsPlayingStreamingGameMusic() ; + virtual void SetIsPlayingStreamingCDMusic(bool bVal) ; + virtual void SetIsPlayingStreamingGameMusic(bool bVal) ; + virtual bool GetIsPlayingEndMusic() ; + virtual bool GetIsPlayingNetherMusic() ; + virtual void SetIsPlayingEndMusic(bool bVal) ; + virtual void SetIsPlayingNetherMusic(bool bVal) ; + static const WCHAR *wchSoundNames[eSoundType_MAX]; + static const WCHAR *wchUISoundNames[eSFX_MAX]; + +private: + // platform specific functions + + virtual int initAudioHardware(int iMinSpeakers)=0; + + bool m_bIsPlayingStreamingCDMusic; + bool m_bIsPlayingStreamingGameMusic; + bool m_bIsPlayingEndMusic; + bool m_bIsPlayingNetherMusic; +}; diff --git a/Minecraft.Client/Common/Audio/SoundEngine.cpp b/Minecraft.Client/Common/Audio/SoundEngine.cpp new file mode 100644 index 0000000..4eaf7df --- /dev/null +++ b/Minecraft.Client/Common/Audio/SoundEngine.cpp @@ -0,0 +1,1668 @@ +#include "stdafx.h" + +#include "SoundEngine.h" +#include "..\Consoles_App.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\Minecraft.World\leveldata.h" +#include "..\..\Minecraft.World\mth.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\DLCTexturePack.h" +#include "Common\DLC\DLCAudioFile.h" + +#ifdef __PSVITA__ +#include +#endif + +#ifdef _WINDOWS64 +#include "..\..\Minecraft.Client\Windows64\Windows64_App.h" +#include "..\..\Minecraft.Client\Windows64\Miles\include\imssapi.h" +#endif + +#ifdef __ORBIS__ +#include +//#define __DISABLE_MILES__ // MGH disabled for now as it crashes if we call sceNpMatching2Initialize +#endif + +// take out Orbis until they are done +#if defined _XBOX + +SoundEngine::SoundEngine() {} +void SoundEngine::init(Options *pOptions) +{ +} + +void SoundEngine::tick(shared_ptr *players, float a) +{ +} +void SoundEngine::destroy() {} +void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch) +{ + app.DebugPrintf("PlaySound - %d\n",iSound); +} +void SoundEngine::playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay) {} +void SoundEngine::playUI(int iSound, float volume, float pitch) {} + +void SoundEngine::updateMusicVolume(float fVal) {} +void SoundEngine::updateSoundEffectVolume(float fVal) {} + +void SoundEngine::add(const wstring& name, File *file) {} +void SoundEngine::addMusic(const wstring& name, File *file) {} +void SoundEngine::addStreaming(const wstring& name, File *file) {} +char *SoundEngine::ConvertSoundPathToName(const wstring& name, bool bConvertSpaces) { return NULL; } +bool SoundEngine::isStreamingWavebankReady() { return true; } +void SoundEngine::playMusicTick() {}; + +#else + +#ifdef _WINDOWS64 +char SoundEngine::m_szSoundPath[]={"Durango\\Sound\\"}; +char SoundEngine::m_szMusicPath[]={"music\\"}; +char SoundEngine::m_szRedistName[]={"redist64"}; +#elif defined _DURANGO +char SoundEngine::m_szSoundPath[]={"Sound\\"}; +char SoundEngine::m_szMusicPath[]={"music\\"}; +char SoundEngine::m_szRedistName[]={"redist64"}; +#elif defined __ORBIS__ + +#ifdef _CONTENT_PACKAGE +char SoundEngine::m_szSoundPath[]={"Sound/"}; +#elif defined _ART_BUILD +char SoundEngine::m_szSoundPath[]={"Sound/"}; +#else +// just use the host Durango folder for the sound. In the content package, we'll have moved this in the .gp4 file +char SoundEngine::m_szSoundPath[]={"Durango/Sound/"}; +#endif +char SoundEngine::m_szMusicPath[]={"music/"}; +char SoundEngine::m_szRedistName[]={"redist64"}; +#elif defined __PSVITA__ +char SoundEngine::m_szSoundPath[]={"PSVita/Sound/"}; +char SoundEngine::m_szMusicPath[]={"music/"}; +char SoundEngine::m_szRedistName[]={"redist"}; +#elif defined __PS3__ +//extern const char* getPS3HomePath(); +char SoundEngine::m_szSoundPath[]={"PS3/Sound/"}; +char SoundEngine::m_szMusicPath[]={"music/"}; +char SoundEngine::m_szRedistName[]={"redist"}; + +#define USE_SPURS + +#ifdef USE_SPURS +#include +#else +#include +#endif + +#endif + +F32 AILCALLBACK custom_falloff_function (HSAMPLE S, + F32 distance, + F32 rolloff_factor, + F32 min_dist, + F32 max_dist); + +char *SoundEngine::m_szStreamFileA[eStream_Max]= +{ + "calm1", + "calm2", + "calm3", + "hal1", + "hal2", + "hal3", + "hal4", + "nuance1", + "nuance2", +#ifndef _XBOX + // add the new music tracks + "creative1", + "creative2", + "creative3", + "creative4", + "creative5", + "creative6", + "menu1", + "menu2", + "menu3", + "menu4", +#endif + "piano1", + "piano2", + "piano3", + + // Nether + "nether1", + "nether2", + "nether3", + "nether4", + // The End + "the_end_dragon_alive", + "the_end_end", + // CDs + "11", + "13", + "blocks", + "cat", + "chirp", + "far", + "mall", + "mellohi", + "stal", + "strad", + "ward", + "where_are_we_now" +}; + +///////////////////////////////////////////// +// +// ErrorCallback +// +///////////////////////////////////////////// +void AILCALL ErrorCallback(S64 i_Id, char const* i_Details) +{ + char *pchLastError=AIL_last_error(); + + if(pchLastError[0]!=0) + { + app.DebugPrintf("\rErrorCallback Error Category: %s\n", pchLastError); + } + + if (i_Details) + { + app.DebugPrintf("ErrorCallback - Details: %s\n", i_Details); + } +} + +#ifdef __PSVITA__ +// AP - this is the callback when the driver is about to mix. At this point the mutex is locked by Miles so we can now call all Miles functions without +// the possibility of incurring a stall. +static bool SoundEngine_Change = false; // has tick been called? +static CRITICAL_SECTION SoundEngine_MixerMutex; + +void AILCALL MilesMixerCB(HDIGDRIVER dig) +{ + // has the tick function been called since the last callback + if( SoundEngine_Change ) + { + SoundEngine_Change = false; + + EnterCriticalSection(&SoundEngine_MixerMutex); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->soundEngine->updateMiles(); + pMinecraft->soundEngine->playMusicUpdate(); + + LeaveCriticalSection(&SoundEngine_MixerMutex); + } +} +#endif + +///////////////////////////////////////////// +// +// init +// +///////////////////////////////////////////// +void SoundEngine::init(Options *pOptions) +{ + app.DebugPrintf("---SoundEngine::init\n"); +#ifdef __DISABLE_MILES__ + return; +#endif +#ifdef __ORBIS__ + C4JThread::PushAffinityAllCores(); +#endif +#if defined _DURANGO || defined __ORBIS__ || defined __PS3__ || defined __PSVITA__ + Register_RIB(BinkADec); +#endif + + char *redistpath; + +#if (defined _WINDOWS64 || defined __PSVITA__)// || defined _DURANGO || defined __ORBIS__ ) + redistpath=AIL_set_redist_directory(m_szRedistName); +#endif + + app.DebugPrintf("---SoundEngine::init - AIL_startup\n"); + S32 ret = AIL_startup(); + + int iNumberOfChannels=initAudioHardware(8); + + // Create a driver to render our audio - 44khz, 16 bit, +#ifdef __PS3__ + // On the Sony PS3, the driver is always opened in 48 kHz, 32-bit floating point. The only meaningful configurations are MSS_MC_STEREO, MSS_MC_51_DISCRETE, and MSS_MC_71_DISCRETE. + m_hDriver = AIL_open_digital_driver( 48000, 16, iNumberOfChannels, AIL_OPEN_DIGITAL_USE_SPU0 ); +#elif defined __PSVITA__ + + // maximum of 16 samples + AIL_set_preference(DIG_MIXER_CHANNELS, 16); + + m_hDriver = AIL_open_digital_driver( 48000, 16, MSS_MC_STEREO, 0 ); + + // AP - For some reason the submit thread defaults to a priority of zero (invalid). Make sure it has the highest priority to avoid audio breakup. + SceUID threadID; + AIL_platform_property( m_hDriver, PSP2_SUBMIT_THREAD, &threadID, 0, 0); + S32 g_DefaultCPU = sceKernelGetThreadCpuAffinityMask(threadID); + S32 Old = sceKernelChangeThreadPriority(threadID, 64); + + // AP - register a callback when the mixer starts + AILMIXERCB temp = AIL_register_mix_callback(m_hDriver, MilesMixerCB); + + InitializeCriticalSection(&SoundEngine_MixerMutex); + +#elif defined(__ORBIS__) + m_hDriver = AIL_open_digital_driver( 48000, 16, 2, 0 ); + app.DebugPrintf("---SoundEngine::init - AIL_open_digital_driver\n"); + +#else + m_hDriver = AIL_open_digital_driver(44100, 16, MSS_MC_USE_SYSTEM_CONFIG, 0); +#endif + if (m_hDriver == 0) + { + app.DebugPrintf("Couldn't open digital sound driver. (%s)\n", AIL_last_error()); + AIL_shutdown(); +#ifdef __ORBIS__ + C4JThread::PopAffinity(); +#endif + return; + } + app.DebugPrintf("---SoundEngine::init - driver opened\n"); + +#ifdef __PSVITA__ + + // set high falloff power for maximum spatial effect in software mode + AIL_set_speaker_configuration( m_hDriver, 0, 0, 4.0F ); + +#endif + + AIL_set_event_error_callback(ErrorCallback); + + AIL_set_3D_rolloff_factor(m_hDriver,1.0); + + // Create an event system tied to that driver - let Miles choose memory defaults. + //if (AIL_startup_event_system(m_hDriver, 0, 0, 0) == 0) + // 4J-PB - Durango complains that the default memory (64k)isn't enough + // Error: MilesEvent: Out of event system memory (pool passed to event system startup exhausted). + // AP - increased command buffer from the default 5K to 20K for Vita + + if (AIL_startup_event_system(m_hDriver, 1024*20, 0, 1024*128) == 0) + { + app.DebugPrintf("Couldn't init event system (%s).\n", AIL_last_error()); + AIL_close_digital_driver(m_hDriver); + AIL_shutdown(); +#ifdef __ORBIS__ + C4JThread::PopAffinity(); +#endif + app.DebugPrintf("---SoundEngine::init - AIL_startup_event_system failed\n"); + return; + } + char szBankName[255]; +#if defined __PS3__ + if(app.GetBootedFromDiscPatch()) + { + char szTempSoundFilename[255]; + sprintf(szTempSoundFilename,"%s%s",m_szSoundPath, "Minecraft.msscmp" ); + + app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) looking for %s\n",szTempSoundFilename); + sprintf(szBankName,"%s/%s",app.GetBDUsrDirPath(szTempSoundFilename), m_szSoundPath ); + app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s\n",szBankName); + } + else + { + sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath ); + } + +#elif defined __PSVITA__ + sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath ); +#elif defined __ORBIS__ + sprintf(szBankName,"%s/%s",getUsrDirPath(), m_szSoundPath ); +#else + strcpy((char *)szBankName,m_szSoundPath); +#endif + + strcat((char *)szBankName,"Minecraft.msscmp"); + + m_hBank=AIL_add_soundbank(szBankName, 0); + + if(m_hBank == NULL) + { + char *Error=AIL_last_error(); + app.DebugPrintf("Couldn't open soundbank: %s (%s)\n", szBankName, Error); + AIL_close_digital_driver(m_hDriver); + AIL_shutdown(); +#ifdef __ORBIS__ + C4JThread::PopAffinity(); +#endif + return; + } + + //#ifdef _DEBUG + HMSSENUM token = MSS_FIRST; + char const* Events[1] = {0}; + S32 EventCount = 0; + while (AIL_enumerate_events(m_hBank, &token, 0, &Events[0])) + { + app.DebugPrintf(4,"%d - %s\n", EventCount, Events[0]); + + EventCount++; + } + //#endif + + U64 u64Result; + u64Result=AIL_enqueue_event_by_name("Minecraft/CacheSounds"); + + m_MasterMusicVolume=1.0f; + m_MasterEffectsVolume=1.0f; + + //AIL_set_variable_float(0,"UserEffectVol",1); + + m_bSystemMusicPlaying = false; + + m_openStreamThread = NULL; + +#ifdef __ORBIS__ + C4JThread::PopAffinity(); +#endif + +#ifdef __PSVITA__ + // AP - By default the mixer won't start up and nothing will process. Kick off a blank sample to force the mixer to start up. + HSAMPLE Sample = AIL_allocate_sample_handle(m_hDriver); + AIL_init_sample(Sample, DIG_F_STEREO_16); + static U64 silence = 0; + AIL_set_sample_address(Sample, &silence, sizeof(U64)); + AIL_start_sample(Sample); + + // wait for 1 mix... + AIL_release_sample_handle(Sample); +#endif +} + +#ifdef __ORBIS__ +// void SoundEngine::SetHandle(int32_t hAudio) +// { +// //m_hAudio=hAudio; +// } +#endif + +void SoundEngine::SetStreamingSounds(int iOverworldMin, int iOverWorldMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCD1) +{ + m_iStream_Overworld_Min=iOverworldMin; + m_iStream_Overworld_Max=iOverWorldMax; + m_iStream_Nether_Min=iNetherMin; + m_iStream_Nether_Max=iNetherMax; + m_iStream_End_Min=iEndMin; + m_iStream_End_Max=iEndMax; + m_iStream_CD_1=iCD1; + + // array to monitor recently played tracks + if(m_bHeardTrackA) + { + delete [] m_bHeardTrackA; + } + m_bHeardTrackA = new bool[iEndMax+1]; + memset(m_bHeardTrackA,0,sizeof(bool)*iEndMax+1); +} + +// AP - moved to a separate function so it can be called from the mixer callback on Vita +void SoundEngine::updateMiles() +{ +#ifdef __PSVITA__ + //CD - We must check for Background Music [BGM] at any point + //If it's playing disable our audio, otherwise enable + int NoBGMPlaying = sceAudioOutGetAdopt(SCE_AUDIO_OUT_PORT_TYPE_BGM); + updateSystemMusicPlaying( !NoBGMPlaying ); +#elif defined __ORBIS__ + // is the system playing background music? + SceAudioOutPortState outPortState; + sceAudioOutGetPortState(m_hBGMAudio,&outPortState); + updateSystemMusicPlaying( outPortState.output==SCE_AUDIO_OUT_STATE_OUTPUT_UNKNOWN ); +#endif + + if( m_validListenerCount == 1 ) + { + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + // set the listener as the first player we find + if( m_ListenerA[i].bValid ) + { + AIL_set_listener_3D_position(m_hDriver,m_ListenerA[i].vPosition.x,m_ListenerA[i].vPosition.y,-m_ListenerA[i].vPosition.z); // Flipped sign of z as Miles is expecting left handed coord system + AIL_set_listener_3D_orientation(m_hDriver,-m_ListenerA[i].vOrientFront.x,m_ListenerA[i].vOrientFront.y,m_ListenerA[i].vOrientFront.z,0,1,0); // Flipped sign of z as Miles is expecting left handed coord system + break; + } + } + } + else + { + // 4J-PB - special case for splitscreen + // the shortest distance between any listener and a sound will be used to play a sound a set distance away down the z axis. + // The listener position will be set to 0,0,0, and the orientation will be facing down the z axis + + AIL_set_listener_3D_position(m_hDriver,0,0,0); + AIL_set_listener_3D_orientation(m_hDriver,0,0,1,0,1,0); + } + + AIL_begin_event_queue_processing(); + + // Iterate over the sounds + S32 StartedCount = 0, CompletedCount = 0, TotalCount = 0; + HMSSENUM token = MSS_FIRST; + MILESEVENTSOUNDINFO SoundInfo; + int Playing = 0; + while (AIL_enumerate_sound_instances(0, &token, 0, 0, 0, &SoundInfo)) + { + AUDIO_INFO* game_data= (AUDIO_INFO*)( SoundInfo.UserBuffer ); + + if( SoundInfo.Status == MILESEVENT_SOUND_STATUS_PLAYING ) + { + Playing += 1; + } + + if ( SoundInfo.Status != MILESEVENT_SOUND_STATUS_COMPLETE ) + { + // apply the master volume + // watch for the 'special' volume levels + bool isThunder = false; + if( game_data->volume == 10000.0f ) + { + isThunder = true; + } + if(game_data->volume>1) + { + game_data->volume=1; + } + AIL_set_sample_volume_levels( SoundInfo.Sample, game_data->volume*m_MasterEffectsVolume, game_data->volume*m_MasterEffectsVolume); + + float distanceScaler = 16.0f; + switch(SoundInfo.Status) + { + case MILESEVENT_SOUND_STATUS_PENDING: + // 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue + AIL_register_falloff_function_callback(SoundInfo.Sample,&custom_falloff_function); + + if(game_data->bIs3D) + { + AIL_set_sample_is_3D( SoundInfo.Sample, 1 ); + + int iSound = game_data->iSound - eSFX_MAX; + switch(iSound) + { + // Is this the Dragon? + case eSoundType_MOB_ENDERDRAGON_GROWL: + case eSoundType_MOB_ENDERDRAGON_MOVE: + case eSoundType_MOB_ENDERDRAGON_END: + case eSoundType_MOB_ENDERDRAGON_HIT: + distanceScaler=100.0f; + break; + case eSoundType_MOB_GHAST_MOAN: + case eSoundType_MOB_GHAST_SCREAM: + case eSoundType_MOB_GHAST_DEATH: + case eSoundType_MOB_GHAST_CHARGE: + case eSoundType_MOB_GHAST_FIREBALL: + distanceScaler=30.0f; + break; + } + + // Set a special distance scaler for thunder, which we respond to by having no attenutation + if( isThunder ) + { + distanceScaler = 10000.0f; + } + } + else + { + AIL_set_sample_is_3D( SoundInfo.Sample, 0 ); + } + + AIL_set_sample_3D_distances(SoundInfo.Sample,distanceScaler,1,0); + // set the pitch + if(!game_data->bUseSoundsPitchVal) + { + AIL_set_sample_playback_rate_factor(SoundInfo.Sample,game_data->pitch); + } + + if(game_data->bIs3D) + { + if(m_validListenerCount>1) + { + float fClosest=10000.0f; + int iClosestListener=0; + float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist; + // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_ListenerA[i].bValid ) + { + float x,y,z; + + x=fabs(m_ListenerA[i].vPosition.x-game_data->x); + y=fabs(m_ListenerA[i].vPosition.y-game_data->y); + z=fabs(m_ListenerA[i].vPosition.z-game_data->z); + fDist=x+y+z; + + if(fDistx, game_data->y, -game_data->z ); // Flipped sign of z as Miles is expecting left handed coord system + } + } + break; + + default: + if(game_data->bIs3D) + { + if(m_validListenerCount>1) + { + float fClosest=10000.0f; + int iClosestListener=0; + float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist; + // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_ListenerA[i].bValid ) + { + float x,y,z; + + x=fabs(m_ListenerA[i].vPosition.x-game_data->x); + y=fabs(m_ListenerA[i].vPosition.y-game_data->y); + z=fabs(m_ListenerA[i].vPosition.z-game_data->z); + fDist=x+y+z; + + if(fDistx, game_data->y, -game_data->z ); // Flipped sign of z as Miles is expecting left handed coord system + } + } + break; + } + } + } + AIL_complete_event_queue_processing(); +} + +//#define DISTORTION_TEST +#ifdef DISTORTION_TEST +static float fVal=0.0f; +#endif +///////////////////////////////////////////// +// +// tick +// +///////////////////////////////////////////// + +#ifdef __PSVITA__ +static S32 running = AIL_ms_count(); +#endif + +void SoundEngine::tick(shared_ptr *players, float a) +{ +#ifdef __DISABLE_MILES__ + return; +#endif + +#ifdef __PSVITA__ + EnterCriticalSection(&SoundEngine_MixerMutex); +#endif + + // update the listener positions + int listenerCount = 0; +#ifdef DISTORTION_TEST + float fX,fY,fZ; +#endif + if( players ) + { + bool bListenerPostionSet=false; + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( players[i] != NULL ) + { + m_ListenerA[i].bValid=true; + F32 x,y,z; + x=players[i]->xo + (players[i]->x - players[i]->xo) * a; + y=players[i]->yo + (players[i]->y - players[i]->yo) * a; + z=players[i]->zo + (players[i]->z - players[i]->zo) * a; + + float yRot = players[i]->yRotO + (players[i]->yRot - players[i]->yRotO) * a; + float yCos = (float)cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = (float)sin(-yRot * Mth::RAD_TO_GRAD - PI); + + // store the listener positions for splitscreen + m_ListenerA[i].vPosition.x = x; + m_ListenerA[i].vPosition.y = y; + m_ListenerA[i].vPosition.z = z; + + m_ListenerA[i].vOrientFront.x = ySin; + m_ListenerA[i].vOrientFront.y = 0; + m_ListenerA[i].vOrientFront.z = yCos; + + listenerCount++; + } + else + { + m_ListenerA[i].bValid=false; + } + } + } + + + // If there were no valid players set, make up a default listener + if( listenerCount == 0 ) + { + m_ListenerA[0].vPosition.x = 0; + m_ListenerA[0].vPosition.y = 0; + m_ListenerA[0].vPosition.z = 0; + m_ListenerA[0].vOrientFront.x = 0; + m_ListenerA[0].vOrientFront.y = 0; + m_ListenerA[0].vOrientFront.z = 1.0f; + listenerCount++; + } + m_validListenerCount = listenerCount; + +#ifdef __PSVITA__ + // AP - Show that a change has occurred so we know to update the values at the next Mixer callback + SoundEngine_Change = true; + + LeaveCriticalSection(&SoundEngine_MixerMutex); +#else + updateMiles(); +#endif +} + +///////////////////////////////////////////// +// +// SoundEngine +// +///////////////////////////////////////////// +SoundEngine::SoundEngine() +{ + random = new Random(); + m_hStream=0; + m_StreamState=eMusicStreamState_Idle; + m_iMusicDelay=0; + m_validListenerCount=0; + + m_bHeardTrackA=NULL; + + // Start the streaming music playing some music from the overworld + SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3, + eStream_Nether1,eStream_Nether4, + eStream_end_dragon,eStream_end_end, + eStream_CD_1); + + m_musicID=getMusicID(LevelData::DIMENSION_OVERWORLD); + + m_StreamingAudioInfo.bIs3D=false; + m_StreamingAudioInfo.x=0; + m_StreamingAudioInfo.y=0; + m_StreamingAudioInfo.z=0; + m_StreamingAudioInfo.volume=1; + m_StreamingAudioInfo.pitch=1; + + memset(CurrentSoundsPlaying,0,sizeof(int)*(eSoundType_MAX+eSFX_MAX)); + memset(m_ListenerA,0,sizeof(AUDIO_LISTENER)*XUSER_MAX_COUNT); + +#ifdef __ORBIS__ + m_hBGMAudio=GetAudioBGMHandle(); +#endif +} + +void SoundEngine::destroy() {} + +#ifdef _DEBUG +void SoundEngine::GetSoundName(char *szSoundName,int iSound) +{ + strcpy((char *)szSoundName,"Minecraft/"); + wstring name = wchSoundNames[iSound]; + char *SoundName = (char *)ConvertSoundPathToName(name); + strcat((char *)szSoundName,SoundName); +} +#endif + +///////////////////////////////////////////// +// +// play +// +///////////////////////////////////////////// +void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch) +{ + U8 szSoundName[256]; + + if(iSound==-1) + { + app.DebugPrintf(6,"PlaySound with sound of -1 !!!!!!!!!!!!!!!\n"); + return; + } + + // AP removed old counting system. Now relying on Miles' Play Count Limit + /* // if we are already playing loads of this sounds ignore this one + if(CurrentSoundsPlaying[iSound+eSFX_MAX]>MAX_SAME_SOUNDS_PLAYING) + { + // wstring name = wchSoundNames[iSound]; + // char *SoundName = (char *)ConvertSoundPathToName(name); + // app.DebugPrintf("Too many %s sounds playing!\n",SoundName); + return; + }*/ + + //if (iSound != eSoundType_MOB_IRONGOLEM_WALK) return; + + // build the name + strcpy((char *)szSoundName,"Minecraft/"); + +#ifdef DISTORTION_TEST + wstring name = wchSoundNames[eSoundType_MOB_ENDERDRAGON_GROWL]; +#else + wstring name = wchSoundNames[iSound]; +#endif + + char *SoundName = (char *)ConvertSoundPathToName(name); + strcat((char *)szSoundName,SoundName); + +// app.DebugPrintf(6,"PlaySound - %d - %s - %s (%f %f %f, vol %f, pitch %f)\n",iSound, SoundName, szSoundName,x,y,z,volume,pitch); + + AUDIO_INFO AudioInfo; + AudioInfo.x=x; + AudioInfo.y=y; + AudioInfo.z=z; + AudioInfo.volume=volume; + AudioInfo.pitch=pitch; + AudioInfo.bIs3D=true; + AudioInfo.bUseSoundsPitchVal=false; + AudioInfo.iSound=iSound+eSFX_MAX; +#ifdef _DEBUG + strncpy(AudioInfo.chName,(char *)szSoundName,64); +#endif + + S32 token = AIL_enqueue_event_start(); + AIL_enqueue_event_buffer(&token, &AudioInfo, sizeof(AUDIO_INFO), 0); + AIL_enqueue_event_end_named(token, (char *)szSoundName); +} + +///////////////////////////////////////////// +// +// playUI +// +///////////////////////////////////////////// +void SoundEngine::playUI(int iSound, float volume, float pitch) +{ + U8 szSoundName[256]; + wstring name; + // we have some game sounds played as UI sounds... + // Not the best way to do this, but it seems to only be the portal sounds + + if(iSound>=eSFX_MAX) + { + // AP removed old counting system. Now relying on Miles' Play Count Limit + /* // if we are already playing loads of this sounds ignore this one + if(CurrentSoundsPlaying[iSound+eSFX_MAX]>MAX_SAME_SOUNDS_PLAYING) return;*/ + + // build the name + strcpy((char *)szSoundName,"Minecraft/"); + name = wchSoundNames[iSound]; + } + else + { + // AP removed old counting system. Now relying on Miles' Play Count Limit + /* // if we are already playing loads of this sounds ignore this one + if(CurrentSoundsPlaying[iSound]>MAX_SAME_SOUNDS_PLAYING) return;*/ + + // build the name + strcpy((char *)szSoundName,"Minecraft/UI/"); + name = wchUISoundNames[iSound]; + } + + char *SoundName = (char *)ConvertSoundPathToName(name); + strcat((char *)szSoundName,SoundName); +// app.DebugPrintf("UI: Playing %s, volume %f, pitch %f\n",SoundName,volume,pitch); + + //app.DebugPrintf("PlaySound - %d - %s\n",iSound, SoundName); + + AUDIO_INFO AudioInfo; + memset(&AudioInfo,0,sizeof(AUDIO_INFO)); + AudioInfo.volume=volume; // will be multiplied by the master volume + AudioInfo.pitch=pitch; + AudioInfo.bUseSoundsPitchVal=true; + if(iSound>=eSFX_MAX) + { + AudioInfo.iSound=iSound+eSFX_MAX; + } + else + { + AudioInfo.iSound=iSound; + } +#ifdef _DEBUG + strncpy(AudioInfo.chName,(char *)szSoundName,64); +#endif + + // 4J-PB - not going to stop UI events happening based on the number of currently playing sounds + S32 token = AIL_enqueue_event_start(); + AIL_enqueue_event_buffer(&token, &AudioInfo, sizeof(AUDIO_INFO), 0); + AIL_enqueue_event_end_named(token, (char *)szSoundName); +} + +///////////////////////////////////////////// +// +// playStreaming +// +///////////////////////////////////////////// +void SoundEngine::playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay) +{ + // This function doesn't actually play a streaming sound, just sets states and an id for the music tick to play it + // Level audio will be played when a play with an empty name comes in + // CD audio will be played when a named stream comes in + + m_StreamingAudioInfo.x=x; + m_StreamingAudioInfo.y=y; + m_StreamingAudioInfo.z=z; + m_StreamingAudioInfo.volume=volume; + m_StreamingAudioInfo.pitch=pitch; + + if(m_StreamState==eMusicStreamState_Playing) + { + m_StreamState=eMusicStreamState_Stop; + } + else if(m_StreamState==eMusicStreamState_Opening) + { + m_StreamState=eMusicStreamState_OpeningCancel; + } + + if(name.empty()) + { + // music, or stop CD + m_StreamingAudioInfo.bIs3D=false; + + // we need a music id + // random delay of up to 3 minutes for music + m_iMusicDelay = random->nextInt(20 * 60 * 3);//random->nextInt(20 * 60 * 10) + 20 * 60 * 10; + +#ifdef _DEBUG + m_iMusicDelay=0; +#endif + Minecraft *pMinecraft=Minecraft::GetInstance(); + + bool playerInEnd=false; + bool playerInNether=false; + + for(unsigned int i=0;ilocalplayers[i]!=NULL) + { + if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END) + { + playerInEnd=true; + } + else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER) + { + playerInNether=true; + } + } + } + if(playerInEnd) + { + m_musicID = getMusicID(LevelData::DIMENSION_END); + } + else if(playerInNether) + { + m_musicID = getMusicID(LevelData::DIMENSION_NETHER); + } + else + { + m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD); + } + } + else + { + // jukebox + m_StreamingAudioInfo.bIs3D=true; + m_musicID=getMusicID(name); + m_iMusicDelay=0; + } +} + + +int SoundEngine::GetRandomishTrack(int iStart,int iEnd) +{ + // 4J-PB - make it more likely that we'll get a track we've not heard for a while, although repeating tracks sometimes is fine + + // if all tracks have been heard, clear the flags + bool bAllTracksHeard=true; + int iVal=iStart; + for(int i=iStart;i<=iEnd;i++) + { + if(m_bHeardTrackA[i]==false) + { + bAllTracksHeard=false; + app.DebugPrintf("Not heard all tracks yet\n"); + break; + } + } + + if(bAllTracksHeard) + { + app.DebugPrintf("Heard all tracks - resetting the tracking array\n"); + + for(int i=iStart;i<=iEnd;i++) + { + m_bHeardTrackA[i]=false; + } + } + + // trying to get a track we haven't heard, but not too hard + for(int i=0;i<=((iEnd-iStart)/2);i++) + { + // random->nextInt(1) will always return 0 + iVal=random->nextInt((iEnd-iStart)+1)+iStart; + if(m_bHeardTrackA[iVal]==false) + { + // not heard this + app.DebugPrintf("(%d) Not heard track %d yet, so playing it now\n",i,iVal); + m_bHeardTrackA[iVal]=true; + break; + } + else + { + app.DebugPrintf("(%d) Skipping track %d already heard it recently\n",i,iVal); + } + } + + app.DebugPrintf("Select track %d\n",iVal); + return iVal; +} +///////////////////////////////////////////// +// +// getMusicID +// +///////////////////////////////////////////// +int SoundEngine::getMusicID(int iDomain) +{ + int iRandomVal=0; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // Before the game has started? + if(pMinecraft==NULL) + { + // any track from the overworld + return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max); + } + + if(pMinecraft->skins->isUsingDefaultSkin()) + { + switch(iDomain) + { + case LevelData::DIMENSION_END: + // the end isn't random - it has different music depending on whether the dragon is alive or not, but we've not added the dead dragon music yet + return m_iStream_End_Min; + case LevelData::DIMENSION_NETHER: + return GetRandomishTrack(m_iStream_Nether_Min,m_iStream_Nether_Max); + //return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min); + default: //overworld + //return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min); + return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max); + } + } + else + { + // using a texture pack - may have multiple End music tracks + switch(iDomain) + { + case LevelData::DIMENSION_END: + return GetRandomishTrack(m_iStream_End_Min,m_iStream_End_Max); + case LevelData::DIMENSION_NETHER: + //return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min); + return GetRandomishTrack(m_iStream_Nether_Min,m_iStream_Nether_Max); + default: //overworld + //return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min); + return GetRandomishTrack(m_iStream_Overworld_Min,m_iStream_Overworld_Max); + } + } +} + +///////////////////////////////////////////// +// +// getMusicID +// +///////////////////////////////////////////// +// check what the CD is +int SoundEngine::getMusicID(const wstring& name) +{ + int iCD=0; + char *SoundName = (char *)ConvertSoundPathToName(name,true); + + // 4J-PB - these will always be the game cds, so use the m_szStreamFileA for this + for(int i=0;i<12;i++) + { + if(strcmp(SoundName,m_szStreamFileA[i+eStream_CD_1])==0) + { + iCD=i; + break; + } + } + + // adjust for cd start position on normal or mash-up pack + return iCD+m_iStream_CD_1; +} + +///////////////////////////////////////////// +// +// getMasterMusicVolume +// +///////////////////////////////////////////// +float SoundEngine::getMasterMusicVolume() +{ + if( m_bSystemMusicPlaying ) + { + return 0.0f; + } + else + { + return m_MasterMusicVolume; + } +} + +///////////////////////////////////////////// +// +// updateMusicVolume +// +///////////////////////////////////////////// +void SoundEngine::updateMusicVolume(float fVal) +{ + m_MasterMusicVolume=fVal; +} + +///////////////////////////////////////////// +// +// updateSystemMusicPlaying +// +///////////////////////////////////////////// +void SoundEngine::updateSystemMusicPlaying(bool isPlaying) +{ + m_bSystemMusicPlaying = isPlaying; +} + +///////////////////////////////////////////// +// +// updateSoundEffectVolume +// +///////////////////////////////////////////// +void SoundEngine::updateSoundEffectVolume(float fVal) +{ + m_MasterEffectsVolume=fVal; + //AIL_set_variable_float(0,"UserEffectVol",fVal); +} + +void SoundEngine::add(const wstring& name, File *file) {} +void SoundEngine::addMusic(const wstring& name, File *file) {} +void SoundEngine::addStreaming(const wstring& name, File *file) {} +bool SoundEngine::isStreamingWavebankReady() { return true; } + +int SoundEngine::OpenStreamThreadProc( void* lpParameter ) +{ +#ifdef __DISABLE_MILES__ + return 0; +#endif + SoundEngine *soundEngine = (SoundEngine *)lpParameter; + soundEngine->m_hStream = AIL_open_stream(soundEngine->m_hDriver,soundEngine->m_szStreamName,0); + return 0; +} + +///////////////////////////////////////////// +// +// playMusicTick +// +///////////////////////////////////////////// +void SoundEngine::playMusicTick() +{ +// AP - vita will update the music during the mixer callback +#ifndef __PSVITA__ + playMusicUpdate(); +#endif +} + +// AP - moved to a separate function so it can be called from the mixer callback on Vita +void SoundEngine::playMusicUpdate() +{ + //return; + static bool firstCall = true; + static float fMusicVol = 0.0f; + if( firstCall ) + { + fMusicVol = getMasterMusicVolume(); + firstCall = false; + } + + switch(m_StreamState) + { + case eMusicStreamState_Idle: + + // start a stream playing + if (m_iMusicDelay > 0) + { + m_iMusicDelay--; + return; + } + + if(m_musicID!=-1) + { + // start playing it + + +#if ( defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ ) + +#ifdef __PS3__ + // 4J-PB - Need to check if we are a patched BD build + if(app.GetBootedFromDiscPatch()) + { + sprintf(m_szStreamName,"%s/%s",app.GetBDUsrDirPath(m_szMusicPath), m_szMusicPath ); + app.DebugPrintf("SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s",m_szStreamName); + } + else + { + sprintf(m_szStreamName,"%s/%s",getUsrDirPath(), m_szMusicPath ); + } +#else + sprintf(m_szStreamName,"%s/%s",getUsrDirPath(), m_szMusicPath ); +#endif + +#else + strcpy((char *)m_szStreamName,m_szMusicPath); +#endif + // are we using a mash-up pack? + //if(pMinecraft && !pMinecraft->skins->isUsingDefaultSkin() && pMinecraft->skins->getSelected()->hasAudio()) + if(Minecraft::GetInstance()->skins->getSelected()->hasAudio()) + { + // It's a mash-up - need to use the DLC path for the music + TexturePack *pTexPack=Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)pTexPack; + DLCPack *pack = pDLCTexPack->getDLCInfoParentPack(); + DLCAudioFile *dlcAudioFile = (DLCAudioFile *) pack->getFile(DLCManager::e_DLCType_Audio, 0); + + app.DebugPrintf("Mashup pack \n"); + + // build the name + + // if the music ID is beyond the end of the texture pack music files, then it's a CD + if(m_musicIDGetSoundName(m_musicID); + wstring wstrFile=L"TPACK:\\Data\\" + wstrSoundName +L".binka"; + std::wstring mountedPath = StorageManager.GetMountedPath(wstrFile); + wcstombs(m_szStreamName,mountedPath.c_str(),255); +#else + wstring &wstrSoundName=dlcAudioFile->GetSoundName(m_musicID); + char szName[255]; + wcstombs(szName,wstrSoundName.c_str(),255); + + string strFile="TPACK:\\Data\\" + string(szName) + ".binka"; + std::string mountedPath = StorageManager.GetMountedPath(strFile); + strcpy(m_szStreamName,mountedPath.c_str()); +#endif + } + else + { + SetIsPlayingStreamingGameMusic(false); + SetIsPlayingStreamingCDMusic(true); + m_MusicType=eMusicType_CD; + m_StreamingAudioInfo.bIs3D=true; + + // Need to adjust to index into the cds in the game's m_szStreamFileA + strcat((char *)m_szStreamName,"cds/"); + strcat((char *)m_szStreamName,m_szStreamFileA[m_musicID-m_iStream_CD_1+eStream_CD_1]); + strcat((char *)m_szStreamName,".binka"); + } + } + else + { + // 4J-PB - if this is a PS3 disc patch, we have to check if the music file is in the patch data +#ifdef __PS3__ + if(app.GetBootedFromDiscPatch() && (m_musicIDRun(); + m_StreamState = eMusicStreamState_Opening; + } + break; + + case eMusicStreamState_Opening: + // If the open stream thread is complete, then we are ready to proceed to actually playing + if( !m_openStreamThread->isRunning() ) + { + delete m_openStreamThread; + m_openStreamThread = NULL; + + HSAMPLE hSample = AIL_stream_sample_handle( m_hStream); + + // 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue + AIL_register_falloff_function_callback(hSample,&custom_falloff_function); + + if(m_StreamingAudioInfo.bIs3D) + { + AIL_set_sample_3D_distances(hSample,64.0f,1,0); // Larger distance scaler for music discs + if(m_validListenerCount>1) + { + float fClosest=10000.0f; + int iClosestListener=0; + float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist; + // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_ListenerA[i].bValid ) + { + float x,y,z; + + x=fabs(m_ListenerA[i].vPosition.x-m_StreamingAudioInfo.x); + y=fabs(m_ListenerA[i].vPosition.y-m_StreamingAudioInfo.y); + z=fabs(m_ListenerA[i].vPosition.z-m_StreamingAudioInfo.z); + fDist=x+y+z; + + if(fDistisRunning() ) + { + delete m_openStreamThread; + m_openStreamThread = NULL; + m_StreamState = eMusicStreamState_Stop; + } + break; + case eMusicStreamState_Stop: + // should gradually take the volume down in steps + AIL_pause_stream(m_hStream,1); + AIL_close_stream(m_hStream); + m_hStream=0; + SetIsPlayingStreamingCDMusic(false); + SetIsPlayingStreamingGameMusic(false); + m_StreamState=eMusicStreamState_Idle; + break; + case eMusicStreamState_Stopping: + break; + case eMusicStreamState_Play: + break; + case eMusicStreamState_Playing: + if(GetIsPlayingStreamingGameMusic()) + { + //if(m_MusicInfo.pCue!=NULL) + { + bool playerInEnd = false; + bool playerInNether=false; + Minecraft *pMinecraft = Minecraft::GetInstance(); + for(unsigned int i = 0; i < MAX_LOCAL_PLAYERS; ++i) + { + if(pMinecraft->localplayers[i]!=NULL) + { + if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END) + { + playerInEnd=true; + } + else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER) + { + playerInNether=true; + } + } + } + + if(playerInEnd && !GetIsPlayingEndMusic()) + { + m_StreamState=eMusicStreamState_Stop; + + // Set the end track + m_musicID = getMusicID(LevelData::DIMENSION_END); + SetIsPlayingEndMusic(true); + SetIsPlayingNetherMusic(false); + } + else if(!playerInEnd && GetIsPlayingEndMusic()) + { + if(playerInNether) + { + m_StreamState=eMusicStreamState_Stop; + + // Set the end track + m_musicID = getMusicID(LevelData::DIMENSION_NETHER); + SetIsPlayingEndMusic(false); + SetIsPlayingNetherMusic(true); + } + else + { + m_StreamState=eMusicStreamState_Stop; + + // Set the end track + m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD); + SetIsPlayingEndMusic(false); + SetIsPlayingNetherMusic(false); + } + } + else if (playerInNether && !GetIsPlayingNetherMusic()) + { + m_StreamState=eMusicStreamState_Stop; + // set the Nether track + m_musicID = getMusicID(LevelData::DIMENSION_NETHER); + SetIsPlayingNetherMusic(true); + SetIsPlayingEndMusic(false); + } + else if(!playerInNether && GetIsPlayingNetherMusic()) + { + if(playerInEnd) + { + m_StreamState=eMusicStreamState_Stop; + // set the Nether track + m_musicID = getMusicID(LevelData::DIMENSION_END); + SetIsPlayingNetherMusic(false); + SetIsPlayingEndMusic(true); + } + else + { + m_StreamState=eMusicStreamState_Stop; + // set the Nether track + m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD); + SetIsPlayingNetherMusic(false); + SetIsPlayingEndMusic(false); + } + } + + // volume change required? + if(fMusicVol!=getMasterMusicVolume()) + { + fMusicVol=getMasterMusicVolume(); + HSAMPLE hSample = AIL_stream_sample_handle( m_hStream); + //AIL_set_sample_3D_position( hSample, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, m_StreamingAudioInfo.z ); + AIL_set_sample_volume_levels( hSample, fMusicVol, fMusicVol); + } + } + } + else + { + // Music disc playing - if it's a 3D stream, then set the position - we don't have any streaming audio in the world that moves, so this isn't + // required unless we have more than one listener, and are setting the listening position to the origin and setting a fake position + // for the sound down the z axis + if(m_StreamingAudioInfo.bIs3D) + { + if(m_validListenerCount>1) + { + float fClosest=10000.0f; + int iClosestListener=0; + float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist; + + // need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_ListenerA[i].bValid ) + { + float x,y,z; + + x=fabs(m_ListenerA[i].vPosition.x-m_StreamingAudioInfo.x); + y=fabs(m_ListenerA[i].vPosition.y-m_StreamingAudioInfo.y); + z=fabs(m_ListenerA[i].vPosition.z-m_StreamingAudioInfo.z); + fDist=x+y+z; + + if(fDistnextInt(20 * 60 * 3);//random->nextInt(20 * 60 * 10) + 20 * 60 * 10; + // Check if we have a local player in The Nether or in The End, and play that music if they are + Minecraft *pMinecraft=Minecraft::GetInstance(); + bool playerInEnd=false; + bool playerInNether=false; + + for(unsigned int i=0;ilocalplayers[i]!=NULL) + { + if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END) + { + playerInEnd=true; + } + else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER) + { + playerInNether=true; + } + } + } + if(playerInEnd) + { + m_musicID = getMusicID(LevelData::DIMENSION_END); + SetIsPlayingEndMusic(true); + SetIsPlayingNetherMusic(false); + } + else if(playerInNether) + { + m_musicID = getMusicID(LevelData::DIMENSION_NETHER); + SetIsPlayingNetherMusic(true); + SetIsPlayingEndMusic(false); + } + else + { + m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD); + SetIsPlayingNetherMusic(false); + SetIsPlayingEndMusic(false); + } + + m_StreamState=eMusicStreamState_Idle; + } + break; + } + + // check the status of the stream - this is for when a track completes rather than is stopped by the user action + + if(m_hStream!=0) + { + if(AIL_stream_status(m_hStream)==SMP_DONE ) // SMP_DONE + { + AIL_close_stream(m_hStream); + m_hStream=0; + SetIsPlayingStreamingCDMusic(false); + SetIsPlayingStreamingGameMusic(false); + + m_StreamState=eMusicStreamState_Completed; + } + } +} + + +///////////////////////////////////////////// +// +// ConvertSoundPathToName +// +///////////////////////////////////////////// +char *SoundEngine::ConvertSoundPathToName(const wstring& name, bool bConvertSpaces) +{ + static char buf[256]; + assert(name.length()<256); + for(unsigned int i = 0; i < name.length(); i++ ) + { + wchar_t c = name[i]; + if(c=='.') c='/'; + if(bConvertSpaces) + { + if(c==' ') c='_'; + } + buf[i] = (char)c; + } + buf[name.length()] = 0; + return buf; +} + +#endif + + +F32 AILCALLBACK custom_falloff_function (HSAMPLE S, + F32 distance, + F32 rolloff_factor, + F32 min_dist, + F32 max_dist) +{ + F32 result; + + // This is now emulating the linear fall-off function that we used on the Xbox 360. The parameter which is passed as "max_dist" is the only one actually used, + // and is generally used as CurveDistanceScaler is used on XACT on the Xbox. A special value of 10000.0f is passed for thunder, which has no attenuation + + if( max_dist == 10000.0f ) + { + return 1.0f; + } + + result = 1.0f - ( distance / max_dist ); + if( result < 0.0f ) result = 0.0f; + if( result > 1.0f ) result = 1.0f; + + return result; +} diff --git a/Minecraft.Client/Common/Audio/SoundEngine.h b/Minecraft.Client/Common/Audio/SoundEngine.h new file mode 100644 index 0000000..92c99d2 --- /dev/null +++ b/Minecraft.Client/Common/Audio/SoundEngine.h @@ -0,0 +1,168 @@ +#pragma once +class Mob; +class Options; +using namespace std; +#include "..\..\Minecraft.World\SoundTypes.h" + +enum eMUSICFILES +{ + eStream_Overworld_Calm1 = 0, + eStream_Overworld_Calm2, + eStream_Overworld_Calm3, + eStream_Overworld_hal1, + eStream_Overworld_hal2, + eStream_Overworld_hal3, + eStream_Overworld_hal4, + eStream_Overworld_nuance1, + eStream_Overworld_nuance2, +#ifndef _XBOX + // Add the new music tracks + eStream_Overworld_Creative1, + eStream_Overworld_Creative2, + eStream_Overworld_Creative3, + eStream_Overworld_Creative4, + eStream_Overworld_Creative5, + eStream_Overworld_Creative6, + eStream_Overworld_Menu1, + eStream_Overworld_Menu2, + eStream_Overworld_Menu3, + eStream_Overworld_Menu4, +#endif + eStream_Overworld_piano1, + eStream_Overworld_piano2, + eStream_Overworld_piano3, // <-- make piano3 the last overworld one + // Nether + eStream_Nether1, + eStream_Nether2, + eStream_Nether3, + eStream_Nether4, + // The End + eStream_end_dragon, + eStream_end_end, + eStream_CD_1, + eStream_CD_2, + eStream_CD_3, + eStream_CD_4, + eStream_CD_5, + eStream_CD_6, + eStream_CD_7, + eStream_CD_8, + eStream_CD_9, + eStream_CD_10, + eStream_CD_11, + eStream_CD_12, + eStream_Max, +}; + +enum eMUSICTYPE +{ + eMusicType_None, + eMusicType_Game, + eMusicType_CD, +}; + + +enum MUSIC_STREAMSTATE +{ + eMusicStreamState_Idle=0, + eMusicStreamState_Stop, + eMusicStreamState_Stopping, + eMusicStreamState_Opening, + eMusicStreamState_OpeningCancel, + eMusicStreamState_Play, + eMusicStreamState_Playing, + eMusicStreamState_Completed +}; + +typedef struct +{ + F32 x,y,z,volume,pitch; + int iSound; + bool bIs3D; + bool bUseSoundsPitchVal; +#ifdef _DEBUG + char chName[64]; +#endif +} +AUDIO_INFO; + +class SoundEngine : public ConsoleSoundEngine +{ + static const int MAX_SAME_SOUNDS_PLAYING = 8; // 4J added +public: + SoundEngine(); + virtual void destroy(); +#ifdef _DEBUG + void GetSoundName(char *szSoundName,int iSound); +#endif + virtual void play(int iSound, float x, float y, float z, float volume, float pitch); + virtual void playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay=true); + virtual void playUI(int iSound, float volume, float pitch); + virtual void playMusicTick(); + virtual void updateMusicVolume(float fVal); + virtual void updateSystemMusicPlaying(bool isPlaying); + virtual void updateSoundEffectVolume(float fVal); + virtual void init(Options *); + virtual void tick(shared_ptr *players, float a); // 4J - updated to take array of local players rather than single one + virtual void add(const wstring& name, File *file); + virtual void addMusic(const wstring& name, File *file); + virtual void addStreaming(const wstring& name, File *file); + virtual char *ConvertSoundPathToName(const wstring& name, bool bConvertSpaces=false); + bool isStreamingWavebankReady(); // 4J Added + int getMusicID(int iDomain); + int getMusicID(const wstring& name); + void SetStreamingSounds(int iOverworldMin, int iOverWorldMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCD1); + void updateMiles(); // AP added so Vita can update all the Miles functions during the mixer callback + void playMusicUpdate(); + +private: + float getMasterMusicVolume(); + // platform specific functions +#ifdef __PS3__ + int initAudioHardware(int iMinSpeakers); +#else + int initAudioHardware(int iMinSpeakers) { return iMinSpeakers;} +#endif + + int GetRandomishTrack(int iStart,int iEnd); + + HMSOUNDBANK m_hBank; + HDIGDRIVER m_hDriver; + HSTREAM m_hStream; + + static char m_szSoundPath[]; + static char m_szMusicPath[]; + static char m_szRedistName[]; + static char *m_szStreamFileA[eStream_Max]; + + AUDIO_LISTENER m_ListenerA[MAX_LOCAL_PLAYERS]; + int m_validListenerCount; + + + Random *random; + int m_musicID; + int m_iMusicDelay; + int m_StreamState; + int m_MusicType; + AUDIO_INFO m_StreamingAudioInfo; + wstring m_CDMusic; + BOOL m_bSystemMusicPlaying; + float m_MasterMusicVolume; + float m_MasterEffectsVolume; + + C4JThread *m_openStreamThread; + static int OpenStreamThreadProc( void* lpParameter ); + char m_szStreamName[255]; + int CurrentSoundsPlaying[eSoundType_MAX+eSFX_MAX]; + + // streaming music files - will be different for mash-up packs + int m_iStream_Overworld_Min,m_iStream_Overworld_Max; + int m_iStream_Nether_Min,m_iStream_Nether_Max; + int m_iStream_End_Min,m_iStream_End_Max; + int m_iStream_CD_1; + bool *m_bHeardTrackA; + +#ifdef __ORBIS__ + int32_t m_hBGMAudio; +#endif +}; diff --git a/Minecraft.Client/Common/Audio/SoundNames.cpp b/Minecraft.Client/Common/Audio/SoundNames.cpp new file mode 100644 index 0000000..170c87a --- /dev/null +++ b/Minecraft.Client/Common/Audio/SoundNames.cpp @@ -0,0 +1,165 @@ +#include "stdafx.h" + +#include "Consoles_SoundEngine.h" + + + +const WCHAR *ConsoleSoundEngine::wchSoundNames[eSoundType_MAX]= +{ + L"mob.chicken", // eSoundType_MOB_CHICKEN_AMBIENT + L"mob.chickenhurt", // eSoundType_MOB_CHICKEN_HURT + L"mob.chickenplop", // eSoundType_MOB_CHICKENPLOP + L"mob.cow", // eSoundType_MOB_COW_AMBIENT + L"mob.cowhurt", // eSoundType_MOB_COW_HURT + L"mob.pig", // eSoundType_MOB_PIG_AMBIENT + L"mob.pigdeath", // eSoundType_MOB_PIG_DEATH + L"mob.sheep", // eSoundType_MOB_SHEEP_AMBIENT + L"mob.wolf.growl", // eSoundType_MOB_WOLF_GROWL + L"mob.wolf.whine", // eSoundType_MOB_WOLF_WHINE + L"mob.wolf.panting", // eSoundType_MOB_WOLF_PANTING + L"mob.wolf.bark", // eSoundType_MOB_WOLF_BARK + L"mob.wolf.hurt", // eSoundType_MOB_WOLF_HURT + L"mob.wolf.death", // eSoundType_MOB_WOLF_DEATH + L"mob.wolf.shake", // eSoundType_MOB_WOLF_SHAKE + L"mob.blaze.breathe", // eSoundType_MOB_BLAZE_BREATHE + L"mob.blaze.hit", // eSoundType_MOB_BLAZE_HURT + L"mob.blaze.death", // eSoundType_MOB_BLAZE_DEATH + L"mob.ghast.moan", // eSoundType_MOB_GHAST_MOAN + L"mob.ghast.scream", // eSoundType_MOB_GHAST_SCREAM + L"mob.ghast.death", // eSoundType_MOB_GHAST_DEATH + L"mob.ghast.fireball", // eSoundType_MOB_GHAST_FIREBALL + L"mob.ghast.charge", // eSoundType_MOB_GHAST_CHARGE + L"mob.endermen.idle", // eSoundType_MOB_ENDERMEN_IDLE + L"mob.endermen.hit", // eSoundType_MOB_ENDERMEN_HIT + L"mob.endermen.death", // eSoundType_MOB_ENDERMEN_DEATH + L"mob.endermen.portal", // eSoundType_MOB_ENDERMEN_PORTAL + L"mob.zombiepig.zpig", // eSoundType_MOB_ZOMBIEPIG_AMBIENT + L"mob.zombiepig.zpighurt", // eSoundType_MOB_ZOMBIEPIG_HURT + L"mob.zombiepig.zpigdeath", // eSoundType_MOB_ZOMBIEPIG_DEATH + L"mob.zombiepig.zpigangry", // eSoundType_MOB_ZOMBIEPIG_ZPIGANGRY + L"mob.silverfish.say", // eSoundType_MOB_SILVERFISH_AMBIENT, + L"mob.silverfish.hit", // eSoundType_MOB_SILVERFISH_HURT + L"mob.silverfish.kill", // eSoundType_MOB_SILVERFISH_DEATH, + L"mob.silverfish.step", // eSoundType_MOB_SILVERFISH_STEP, + L"mob.skeleton", // eSoundType_MOB_SKELETON_AMBIENT, + L"mob.skeletonhurt", // eSoundType_MOB_SKELETON_HURT, + L"mob.spider", // eSoundType_MOB_SPIDER_AMBIENT, + L"mob.spiderdeath", // eSoundType_MOB_SPIDER_DEATH, + L"mob.slime", // eSoundType_MOB_SLIME, + L"mob.slimeattack", // eSoundType_MOB_SLIME_ATTACK, + L"mob.creeper", // eSoundType_MOB_CREEPER_HURT, + L"mob.creeperdeath", // eSoundType_MOB_CREEPER_DEATH, + L"mob.zombie", // eSoundType_MOB_ZOMBIE_AMBIENT, + L"mob.zombiehurt", // eSoundType_MOB_ZOMBIE_HURT, + L"mob.zombiedeath", // eSoundType_MOB_ZOMBIE_DEATH, + L"mob.zombie.wood", // eSoundType_MOB_ZOMBIE_WOOD, + L"mob.zombie.woodbreak", // eSoundType_MOB_ZOMBIE_WOOD_BREAK, + L"mob.zombie.metal", // eSoundType_MOB_ZOMBIE_METAL, + L"mob.magmacube.big", // eSoundType_MOB_MAGMACUBE_BIG, + L"mob.magmacube.small", // eSoundType_MOB_MAGMACUBE_SMALL, + L"mob.cat.purr", // eSoundType_MOB_CAT_PURR + L"mob.cat.purreow", // eSoundType_MOB_CAT_PURREOW + L"mob.cat.meow", // eSoundType_MOB_CAT_MEOW + // 4J-PB - correct the name of the event for hitting ocelots + L"mob.cat.hit", // eSoundType_MOB_CAT_HITT +// L"mob.irongolem.throw", // eSoundType_MOB_IRONGOLEM_THROW +// L"mob.irongolem.hit", // eSoundType_MOB_IRONGOLEM_HIT +// L"mob.irongolem.death", // eSoundType_MOB_IRONGOLEM_DEATH +// L"mob.irongolem.walk", // eSoundType_MOB_IRONGOLEM_WALK + L"random.bow", // eSoundType_RANDOM_BOW, + L"random.bowhit", // eSoundType_RANDOM_BOW_HIT, + L"random.explode", // eSoundType_RANDOM_EXPLODE, + L"random.fizz", // eSoundType_RANDOM_FIZZ, + L"random.pop", // eSoundType_RANDOM_POP, + L"random.fuse", // eSoundType_RANDOM_FUSE, + L"random.drink", // eSoundType_RANDOM_DRINK, + L"random.eat", // eSoundType_RANDOM_EAT, + L"random.burp", // eSoundType_RANDOM_BURP, + L"random.splash", // eSoundType_RANDOM_SPLASH, + L"random.click", // eSoundType_RANDOM_CLICK, + L"random.glass", // eSoundType_RANDOM_GLASS, + L"random.orb", // eSoundType_RANDOM_ORB, + L"random.break", // eSoundType_RANDOM_BREAK, + L"random.chestopen", // eSoundType_RANDOM_CHEST_OPEN, + L"random.chestclosed", // eSoundType_RANDOM_CHEST_CLOSE, + L"random.door_open", // eSoundType_RANDOM_DOOR_OPEN, + L"random.door_close", // eSoundType_RANDOM_DOOR_CLOSE, + L"ambient.weather.rain", // eSoundType_AMBIENT_WEATHER_RAIN, + L"ambient.weather.thunder", // eSoundType_AMBIENT_WEATHER_THUNDER, + L"ambient.cave.cave", // eSoundType_CAVE_CAVE, DON'T USE FOR XBOX 360!!! +#ifdef _XBOX + L"ambient.cave.cave2", // eSoundType_CAVE_CAVE2 - removed the two sounds that were at 192k in the first ambient cave event +#endif + L"portal.portal", // eSoundType_PORTAL_PORTAL, + // 4J-PB - added a couple that were still using wstring + L"portal.trigger", // eSoundType_PORTAL_TRIGGER + L"portal.travel", // eSoundType_PORTAL_TRAVEL + + L"fire.ignite", // eSoundType_FIRE_IGNITE, + L"fire.fire", // eSoundType_FIRE_FIRE, + L"damage.hurtflesh", // eSoundType_DAMAGE_HURT, + L"damage.fallsmall", // eSoundType_DAMAGE_FALL_SMALL, + L"damage.fallbig", // eSoundType_DAMAGE_FALL_BIG, + L"note.harp", // eSoundType_NOTE_HARP, + L"note.bd", // eSoundType_NOTE_BD, + L"note.snare", // eSoundType_NOTE_SNARE, + L"note.hat", // eSoundType_NOTE_HAT, + L"note.bassattack", // eSoundType_NOTE_BASSATTACK, + L"tile.piston.in", // eSoundType_TILE_PISTON_IN, + L"tile.piston.out", // eSoundType_TILE_PISTON_OUT, + L"liquid.water", // eSoundType_LIQUID_WATER, + L"liquid.lavapop", // eSoundType_LIQUID_LAVA_POP, + L"liquid.lava", // eSoundType_LIQUID_LAVA, + L"step.stone", // eSoundType_STEP_STONE, + L"step.wood", // eSoundType_STEP_WOOD, + L"step.gravel", // eSoundType_STEP_GRAVEL, + L"step.grass", // eSoundType_STEP_GRASS, + L"step.metal", // eSoundType_STEP_METAL, + L"step.cloth", // eSoundType_STEP_CLOTH, + L"step.sand", // eSoundType_STEP_SAND, + + // below this are the additional sounds from the second soundbank + L"mob.enderdragon.end", // eSoundType_MOB_ENDERDRAGON_END + L"mob.enderdragon.growl", // eSoundType_MOB_ENDERDRAGON_GROWL + L"mob.enderdragon.hit", // eSoundType_MOB_ENDERDRAGON_HIT + L"mob.enderdragon.wings", // eSoundType_MOB_ENDERDRAGON_MOVE + L"mob.irongolem.throw", // eSoundType_MOB_IRONGOLEM_THROW + L"mob.irongolem.hit", // eSoundType_MOB_IRONGOLEM_HIT + L"mob.irongolem.death", // eSoundType_MOB_IRONGOLEM_DEATH + L"mob.irongolem.walk", // eSoundType_MOB_IRONGOLEM_WALK + + // TU14 + L"damage.thorns", // eSoundType_DAMAGE_THORNS + L"random.anvil_break", // eSoundType_RANDOM_ANVIL_BREAK + L"random.anvil_land", // eSoundType_RANDOM_ANVIL_LAND + L"random.anvil_use", // eSoundType_RANDOM_ANVIL_USE + L"mob.villager.haggle", // eSoundType_MOB_VILLAGER_HAGGLE + L"mob.villager.idle", // eSoundType_MOB_VILLAGER_IDLE + L"mob.villager.hit", // eSoundType_MOB_VILLAGER_HIT + L"mob.villager.death", // eSoundType_MOB_VILLAGER_DEATH + L"mob.villager.yes", // eSoundType_MOB_VILLAGER_YES + L"mob.villager.no", // eSoundType_MOB_VILLAGER_NO + L"mob.zombie.infect", // eSoundType_MOB_ZOMBIE_INFECT + L"mob.zombie.unfect", // eSoundType_MOB_ZOMBIE_UNFECT + L"mob.zombie.remedy", // eSoundType_MOB_ZOMBIE_REMEDY + L"step.snow", // eSoundType_STEP_SNOW + L"step.ladder", // eSoundType_STEP_LADDER + L"dig.cloth", // eSoundType_DIG_CLOTH + L"dig.grass", // eSoundType_DIG_GRASS + L"dig.gravel", // eSoundType_DIG_GRAVEL + L"dig.sand", // eSoundType_DIG_SAND + L"dig.snow", // eSoundType_DIG_SNOW + L"dig.stone", // eSoundType_DIG_STONE + L"dig.wood", // eSoundType_DIG_WOOD +}; + + +const WCHAR *ConsoleSoundEngine::wchUISoundNames[eSFX_MAX]= +{ + L"back", + L"craft", + L"craftfail", + L"focus", + L"press", + L"scroll", +}; diff --git a/Minecraft.Client/Common/BuildVer.h b/Minecraft.Client/Common/BuildVer.h new file mode 100644 index 0000000..ee558b0 --- /dev/null +++ b/Minecraft.Client/Common/BuildVer.h @@ -0,0 +1,57 @@ + +#pragma once + + +#define VER_PRODUCTMAJORVERSION 0 +#define VER_PRODUCTMINORVERSION 0 + +// This goes up with each build +// 4J-JEV: This value is extracted with a regex so it can be placed as the version in the AppX manifest on Durango. +#define VER_PRODUCTBUILD 495 +// This goes up if there is any change to network traffic or code in a build +#define VER_NETWORK 495 +#define VER_PRODUCTBUILD_QFE 0 + +#define VER_FILEVERSION_STRING "1.3" +#define VER_PRODUCTVERSION_STRING VER_FILEVERSION_STRING +#define VER_FILEVERSION_STRING_W L"1.3" +#define VER_PRODUCTVERSION_STRING_W VER_FILEVERSION_STRING_W +#define VER_FILEBETA_STR "" +#undef VER_FILEVERSION +#define VER_FILEVERSION VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION, VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE +#define VER_PRODUCTVERSION VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION, VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE + +#if (VER_PRODUCTBUILD < 10) +#define VER_FILEBPAD "000" +#define VER_FILEBPAD_W L"000" +#elif (VER_PRODUCTBUILD < 100) +#define VER_FILEBPAD "00" +#define VER_FILEBPAD_W L"00" +#elif (VER_PRODUCTBUILD < 1000) +#define VER_FILEBPAD "0" +#define VER_FILEBPAD_W L"0" +#else +#define VER_FILEBPAD +#define VER_FILEBPAD_W +#endif + +#define VER_WIDE_PREFIX(x) L##x + +#define VER_FILEVERSION_STR2(x,y) VER_FILEVERSION_STRING "." VER_FILEBPAD #x "." #y +#define VER_FILEVERSION_STR2_W(x,y) VER_FILEVERSION_STRING_W L"." VER_FILEBPAD_W VER_WIDE_PREFIX(#x) L"." VER_WIDE_PREFIX(#y) +#define VER_FILEVERSION_STR1(x,y) VER_FILEVERSION_STR2(x, y) +#define VER_FILEVERSION_STR1_W(x,y) VER_FILEVERSION_STR2_W(x, y) + +#undef VER_FILEVERSION_STR +#define VER_FILEVERSION_STR VER_FILEVERSION_STR1(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR1(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) + +#define VER_FILEVERSION_STR_W VER_FILEVERSION_STR1_W(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) +#define VER_PRODUCTVERSION_STR_W VER_FILEVERSION_STR1_W(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) + +#if (VER_PRODUCTBUILD_QFE >= 256) +#error "QFE number cannot exceed 255" +#endif + + + diff --git a/Minecraft.Client/Common/C4JMemoryPool.h b/Minecraft.Client/Common/C4JMemoryPool.h new file mode 100644 index 0000000..e1e795e --- /dev/null +++ b/Minecraft.Client/Common/C4JMemoryPool.h @@ -0,0 +1,176 @@ + +#pragma once + + +#include + +class C4JMemoryPool +{ +public: + unsigned int Align(unsigned int val, unsigned int align) { return int((val+(align-1))/align) * align; } + virtual void* Alloc(size_t size) = 0; + virtual void Free(void* ptr) = 0; +}; + + + +// Fast Efficient Fixed-Size Memory Pool : No Loops and No Overhead +// http://www.alogicalmind.com/memory_pools/index.htm +class C4JMemoryPoolFixed : public C4JMemoryPool +{ + // Basic type define + typedef unsigned int uint; + typedef unsigned char uchar; + uint m_numOfBlocks; // Num of blocks + uint m_sizeOfEachBlock; // Size of each block + uint m_numFreeBlocks; // Num of remaining blocks + uint m_numInitialized; // Num of initialized blocks + uchar* m_memStart; // Beginning of memory pool + uchar* m_memEnd; // End of memory pool + uchar* m_next; // Num of next free block +// CRITICAL_SECTION m_CS; +public: + C4JMemoryPoolFixed() + { + m_numOfBlocks = 0; + m_sizeOfEachBlock = 0; + m_numFreeBlocks = 0; + m_numInitialized = 0; + m_memStart = NULL; + m_memEnd = NULL; + m_next = 0; + } + + C4JMemoryPoolFixed(uint sizeOfEachBlock, uint numOfBlocks) + { + CreatePool(sizeOfEachBlock, numOfBlocks); + } + + ~C4JMemoryPoolFixed() { DestroyPool(); } + + void CreatePool(uint sizeOfEachBlock, uint numOfBlocks) + { + assert(sizeOfEachBlock >= 4); // has to be at least the size of an int, for book keeping + m_numOfBlocks = numOfBlocks; + m_sizeOfEachBlock = sizeOfEachBlock; + m_numFreeBlocks = numOfBlocks; + m_numInitialized = 0; + m_memStart = new uchar[ m_sizeOfEachBlock * + m_numOfBlocks ]; + m_memEnd = m_memStart + (m_sizeOfEachBlock * m_numOfBlocks); + m_next = m_memStart; +// InitializeCriticalSection(&m_CS); + } + + void DestroyPool() + { + delete[] m_memStart; + m_memStart = NULL; + } + + uchar* AddrFromIndex(uint i) const + { + return m_memStart + ( i * m_sizeOfEachBlock ); + } + + uint IndexFromAddr(const uchar* p) const + { + return (((uint)(p - m_memStart)) / m_sizeOfEachBlock); + } + + virtual void* Alloc(size_t size) + { + if(size > m_sizeOfEachBlock) + return ::malloc(size); +// EnterCriticalSection(&m_CS); + if (m_numInitialized < m_numOfBlocks ) + { + uint* p = (uint*)AddrFromIndex( m_numInitialized ); + *p = m_numInitialized + 1; + m_numInitialized++; + } + void* ret = NULL; + if ( m_numFreeBlocks > 0 ) + { + ret = (void*)m_next; + --m_numFreeBlocks; + if (m_numFreeBlocks!=0) + { + m_next = AddrFromIndex( *((uint*)m_next) ); + } + else + { + m_next = NULL; + } + } +// LeaveCriticalSection(&m_CS); + return ret; + } + + virtual void Free(void* ptr) + { + if(ptr < m_memStart || ptr > m_memEnd) + { + ::free(ptr); + return; + } +// EnterCriticalSection(&m_CS); + if (m_next != NULL) + { + (*(uint*)ptr) = IndexFromAddr( m_next ); + m_next = (uchar*)ptr; + } + else + { + *((uint*)ptr) = m_numOfBlocks; + m_next = (uchar*)ptr; + } + ++m_numFreeBlocks; +// LeaveCriticalSection(&m_CS); + } +}; // End pool class + + +// this pool will constantly grow until it is reset (automatically when all allocs have been "freed") +class C4JMemoryPoolGrow : public C4JMemoryPool +{ + uint32_t m_totalSize; + uint32_t m_memUsed; + uint32_t m_numAllocations; + uint8_t* m_pMemory; + uint32_t m_currentOffset; + +public: + C4JMemoryPoolGrow(uint32_t size = 64*1024) + { + size = Align(size, 4); + m_totalSize = size; + m_pMemory = new uint8_t[size]; + m_currentOffset = 0; + m_memUsed = 0; + m_numAllocations = 0; + } + + virtual void* Alloc(size_t size) + { + size = Align(size, 4); // 4 byte align the memory + assert((m_currentOffset + size) < m_totalSize); // make sure we haven't ran out of space + void* returnMem = &m_pMemory[m_currentOffset]; // grab the return memory + m_currentOffset += size; + m_numAllocations++; + return returnMem; + } + virtual void Free(void* ptr) + { + m_numAllocations--; + if(m_numAllocations == 0) + m_currentOffset = 0; // reset the pool when we reach zero allocations + } +}; + + + + + + + diff --git a/Minecraft.Client/Common/C4JMemoryPoolAllocator.h b/Minecraft.Client/Common/C4JMemoryPoolAllocator.h new file mode 100644 index 0000000..a46cc76 --- /dev/null +++ b/Minecraft.Client/Common/C4JMemoryPoolAllocator.h @@ -0,0 +1,113 @@ + + +#pragma once +#include "..\Minecraft.Client\Common\C4JMemoryPool.h" + +// Custom allocator, takes a C4JMemoryPool class, which can be one of a number of pool implementations. + +template +class C4JPoolAllocator +{ +public: + typedef T value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + typedef T* pointer; + typedef const T* const_pointer; + + typedef T& reference; + typedef const T& const_reference; + + //! A struct to construct an allocator for a different type. + template + struct rebind { typedef C4JPoolAllocator other; }; + + + C4JMemoryPool* m_pPool; + bool m_selfAllocated; + + C4JPoolAllocator( C4JMemoryPool* pool = new C4JMemoryPoolFixed(32, 4096 )) : m_pPool( pool ), m_selfAllocated(true) + { + printf("allocated mempool\n"); + } + + template + C4JPoolAllocator(C4JPoolAllocator const& obj) : m_pPool( obj.m_pPool ), m_selfAllocated(false) // copy constructor + { + printf("C4JPoolAllocator constructed from 0x%08x\n", &obj); + assert(obj.m_pPool); + } +private: + +public: + + ~C4JPoolAllocator() + { + if(m_selfAllocated) + delete m_pPool; + } + + pointer address( reference r ) const { return &r; } + const_pointer address( const_reference r ) const { return &r; } + + pointer allocate( size_type n, const void* /*hint*/=0 ) + { + assert(m_pPool); + return (pointer)m_pPool->Alloc(n * sizeof(T)); + } + + void deallocate( pointer p, size_type /*n*/ ) + { + assert(m_pPool); + m_pPool->Free(p); + } + + void construct( pointer p, const T& val ) + { + new (p) T(val); + } + + void destroy( pointer p ) + { + p->~T(); + } + + size_type max_size() const + { + return ULONG_MAX / sizeof(T); + } + +}; + + +template +bool +operator==( const C4JPoolAllocator& left, const C4JPoolAllocator& right ) +{ + if (left.m_pPool == right.m_pPool) + { + return true; + } + return false; +} + +template +bool +operator!=( const C4JPoolAllocator& left, const C4JPoolAllocator& right) +{ + if (left.m_pPool != right.m_pPool) + { + return true; + } + return false; +} + + + + + + + + + diff --git a/Minecraft.Client/Common/Colours/ColourTable.cpp b/Minecraft.Client/Common/Colours/ColourTable.cpp new file mode 100644 index 0000000..dc58cfb --- /dev/null +++ b/Minecraft.Client/Common/Colours/ColourTable.cpp @@ -0,0 +1,366 @@ +#include "stdafx.h" +#include "ColourTable.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +unordered_map ColourTable::s_colourNamesMap; + +wchar_t *ColourTable::ColourTableElements[eMinecraftColour_COUNT] = +{ + L"NOTSET", + + L"Foliage_Evergreen", + L"Foliage_Birch", + L"Foliage_Default", + L"Foliage_Common", + L"Foliage_Ocean", + L"Foliage_Plains", + L"Foliage_Desert", + L"Foliage_ExtremeHills", + L"Foliage_Forest", + L"Foliage_Taiga", + L"Foliage_Swampland", + L"Foliage_River", + L"Foliage_Hell", + L"Foliage_Sky", + L"Foliage_FrozenOcean", + L"Foliage_FrozenRiver", + L"Foliage_IcePlains", + L"Foliage_IceMountains", + L"Foliage_MushroomIsland", + L"Foliage_MushroomIslandShore", + L"Foliage_Beach", + L"Foliage_DesertHills", + L"Foliage_ForestHills", + L"Foliage_TaigaHills", + L"Foliage_ExtremeHillsEdge", + L"Foliage_Jungle", + L"Foliage_JungleHills", + + L"Grass_Common", + L"Grass_Ocean", + L"Grass_Plains", + L"Grass_Desert", + L"Grass_ExtremeHills", + L"Grass_Forest", + L"Grass_Taiga", + L"Grass_Swampland", + L"Grass_River", + L"Grass_Hell", + L"Grass_Sky", + L"Grass_FrozenOcean", + L"Grass_FrozenRiver", + L"Grass_IcePlains", + L"Grass_IceMountains", + L"Grass_MushroomIsland", + L"Grass_MushroomIslandShore", + L"Grass_Beach", + L"Grass_DesertHills", + L"Grass_ForestHills", + L"Grass_TaigaHills", + L"Grass_ExtremeHillsEdge", + L"Grass_Jungle", + L"Grass_JungleHills", + + L"Water_Ocean", + L"Water_Plains", + L"Water_Desert", + L"Water_ExtremeHills", + L"Water_Forest", + L"Water_Taiga", + L"Water_Swampland", + L"Water_River", + L"Water_Hell", + L"Water_Sky", + L"Water_FrozenOcean", + L"Water_FrozenRiver", + L"Water_IcePlains", + L"Water_IceMountains", + L"Water_MushroomIsland", + L"Water_MushroomIslandShore", + L"Water_Beach", + L"Water_DesertHills", + L"Water_ForestHills", + L"Water_TaigaHills", + L"Water_ExtremeHillsEdge", + L"Water_Jungle", + L"Water_JungleHills", + + L"Sky_Ocean", + L"Sky_Plains", + L"Sky_Desert", + L"Sky_ExtremeHills", + L"Sky_Forest", + L"Sky_Taiga", + L"Sky_Swampland", + L"Sky_River", + L"Sky_Hell", + L"Sky_Sky", + L"Sky_FrozenOcean", + L"Sky_FrozenRiver", + L"Sky_IcePlains", + L"Sky_IceMountains", + L"Sky_MushroomIsland", + L"Sky_MushroomIslandShore", + L"Sky_Beach", + L"Sky_DesertHills", + L"Sky_ForestHills", + L"Sky_TaigaHills", + L"Sky_ExtremeHillsEdge", + L"Sky_Jungle", + L"Sky_JungleHills", + + L"Tile_RedstoneDust", + L"Tile_RedstoneDustUnlit", + L"Tile_RedstoneDustLitMin", + L"Tile_RedstoneDustLitMax", + L"Tile_StemMin", + L"Tile_StemMax", + L"Tile_WaterLily", + + L"Sky_Dawn_Dark", + L"Sky_Dawn_Bright", + + L"Material_None", + L"Material_Grass", + L"Material_Sand", + L"Material_Cloth", + L"Material_Fire", + L"Material_Ice", + L"Material_Metal", + L"Material_Plant", + L"Material_Snow", + L"Material_Clay", + L"Material_Dirt", + L"Material_Stone", + L"Material_Water", + L"Material_Wood", + L"Material_Emerald", + + L"Particle_Note_00", + L"Particle_Note_01", + L"Particle_Note_02", + L"Particle_Note_03", + L"Particle_Note_04", + L"Particle_Note_05", + L"Particle_Note_06", + L"Particle_Note_07", + L"Particle_Note_08", + L"Particle_Note_09", + L"Particle_Note_10", + L"Particle_Note_11", + L"Particle_Note_12", + L"Particle_Note_13", + L"Particle_Note_14", + L"Particle_Note_15", + L"Particle_Note_16", + L"Particle_Note_17", + L"Particle_Note_18", + L"Particle_Note_19", + L"Particle_Note_20", + L"Particle_Note_21", + L"Particle_Note_22", + L"Particle_Note_23", + L"Particle_Note_24", + + L"Particle_NetherPortal", + L"Particle_EnderPortal", + L"Particle_Smoke", + L"Particle_Ender", + + L"Particle_Explode", + L"Particle_HugeExplosion", + + L"Particle_DripWater", + L"Particle_DripLavaStart", + L"Particle_DripLavaEnd", + + L"Particle_EnchantmentTable", + L"Particle_DragonBreathMin", + L"Particle_DragonBreathMax", + L"Particle_Suspend", + + L"Particle_CritStart", // arrow in air + L"Particle_CritEnd", // arrow in air + + L"Effect_MovementSpeed", + L"Effect_MovementSlowDown", + L"Effect_DigSpeed", + L"Effect_DigSlowdown", + L"Effect_DamageBoost", + L"Effect_Heal", + L"Effect_Harm", + L"Effect_Jump", + L"Effect_Confusion", + L"Effect_Regeneration", + L"Effect_DamageResistance", + L"Effect_FireResistance", + L"Effect_WaterBreathing", + L"Effect_Invisiblity", + L"Effect_Blindness", + L"Effect_NightVision", + L"Effect_Hunger", + L"Effect_Weakness", + L"Effect_Poison", + + L"Potion_BaseColour", + + L"Mob_Creeper_Colour1", + L"Mob_Creeper_Colour2", + L"Mob_Skeleton_Colour1", + L"Mob_Skeleton_Colour2", + L"Mob_Spider_Colour1", + L"Mob_Spider_Colour2", + L"Mob_Zombie_Colour1", + L"Mob_Zombie_Colour2", + L"Mob_Slime_Colour1", + L"Mob_Slime_Colour2", + L"Mob_Ghast_Colour1", + L"Mob_Ghast_Colour2", + L"Mob_PigZombie_Colour1", + L"Mob_PigZombie_Colour2", + L"Mob_Enderman_Colour1", + L"Mob_Enderman_Colour2", + L"Mob_CaveSpider_Colour1", + L"Mob_CaveSpider_Colour2", + L"Mob_Silverfish_Colour1", + L"Mob_Silverfish_Colour2", + L"Mob_Blaze_Colour1", + L"Mob_Blaze_Colour2", + L"Mob_LavaSlime_Colour1", + L"Mob_LavaSlime_Colour2", + L"Mob_Pig_Colour1", + L"Mob_Pig_Colour2", + L"Mob_Sheep_Colour1", + L"Mob_Sheep_Colour2", + L"Mob_Cow_Colour1", + L"Mob_Cow_Colour2", + L"Mob_Chicken_Colour1", + L"Mob_Chicken_Colour2", + L"Mob_Squid_Colour1", + L"Mob_Squid_Colour2", + L"Mob_Wolf_Colour1", + L"Mob_Wolf_Colour2", + L"Mob_MushroomCow_Colour1", + L"Mob_MushroomCow_Colour2", + L"Mob_Ocelot_Colour1", + L"Mob_Ocelot_Colour2", + L"Mob_Villager_Colour1", + L"Mob_Villager_Colour2", + + L"Armour_Default_Leather_Colour", + L"Under_Water_Clear_Colour", + L"Under_Lava_Clear_Colour", + L"In_Cloud_Base_Colour", + + L"Under_Water_Fog_Colour", + L"Under_Lava_Fog_Colour", + L"In_Cloud_Fog_Colour", + + L"Default_Fog_Colour", + L"Nether_Fog_Colour", + L"End_Fog_Colour", + + L"Sign_Text", + L"Map_Text", + + L"HTMLColor_0", + L"HTMLColor_1", + L"HTMLColor_2", + L"HTMLColor_3", + L"HTMLColor_4", + L"HTMLColor_5", + L"HTMLColor_6", + L"HTMLColor_7", + L"HTMLColor_8", + L"HTMLColor_9", + L"HTMLColor_a", + L"HTMLColor_b", + L"HTMLColor_c", + L"HTMLColor_d", + L"HTMLColor_e", + L"HTMLColor_f", + L"HTMLColor_dark_0", + L"HTMLColor_dark_1", + L"HTMLColor_dark_2", + L"HTMLColor_dark_3", + L"HTMLColor_dark_4", + L"HTMLColor_dark_5", + L"HTMLColor_dark_6", + L"HTMLColor_dark_7", + L"HTMLColor_dark_8", + L"HTMLColor_dark_9", + L"HTMLColor_dark_a", + L"HTMLColor_dark_b", + L"HTMLColor_dark_c", + L"HTMLColor_dark_d", + L"HTMLColor_dark_e", + L"HTMLColor_dark_f", + L"HTMLColor_T1", + L"HTMLColor_T2", + L"HTMLColor_T3", + L"HTMLColor_Black", + L"HTMLColor_White", + L"Color_EnchantText", + L"Color_EnchantTextFocus", + L"Color_EnchantTextDisabled", + L"Color_RenamedItemTitle", +}; + +void ColourTable::staticCtor() +{ + for(unsigned int i = eMinecraftColour_NOT_SET; i < eMinecraftColour_COUNT; ++i) + { + s_colourNamesMap.insert( unordered_map::value_type( ColourTableElements[i], (eMinecraftColour)i) ); + } +} + +ColourTable::ColourTable(PBYTE pbData, DWORD dwLength) +{ + loadColoursFromData(pbData, dwLength); +} + +ColourTable::ColourTable(ColourTable *defaultColours, PBYTE pbData, DWORD dwLength) +{ + // 4J Stu - Default the colours that of the table passed in + XMemCpy( (void *)m_colourValues, (void *)defaultColours->m_colourValues, sizeof(int) * eMinecraftColour_COUNT); + loadColoursFromData(pbData, dwLength); +} +void ColourTable::loadColoursFromData(PBYTE pbData, DWORD dwLength) +{ + byteArray src(pbData, dwLength); + + ByteArrayInputStream bais(src); + DataInputStream dis(&bais); + + int versionNumber = dis.readInt(); + int coloursCount = dis.readInt(); + + for(int i = 0; i < coloursCount; ++i) + { + wstring colourId = dis.readUTF(); + int colourValue = dis.readInt(); + setColour(colourId, colourValue); + AUTO_VAR(it,s_colourNamesMap.find(colourId)); + } + + bais.reset(); +} + +void ColourTable::setColour(const wstring &colourName, int value) +{ + AUTO_VAR(it,s_colourNamesMap.find(colourName)); + if(it != s_colourNamesMap.end()) + { + m_colourValues[(int)it->second] = value; + } +} + +void ColourTable::setColour(const wstring &colourName, const wstring &value) +{ + setColour(colourName, _fromHEXString(value)); +} + +unsigned int ColourTable::getColour(eMinecraftColour id) +{ + return m_colourValues[(int)id]; +} diff --git a/Minecraft.Client/Common/Colours/ColourTable.h b/Minecraft.Client/Common/Colours/ColourTable.h new file mode 100644 index 0000000..8e0a348 --- /dev/null +++ b/Minecraft.Client/Common/Colours/ColourTable.h @@ -0,0 +1,23 @@ +#pragma once + +class ColourTable +{ +private: + unsigned int m_colourValues[eMinecraftColour_COUNT]; + + static wchar_t *ColourTableElements[eMinecraftColour_COUNT]; + static unordered_map s_colourNamesMap; + +public: + static void staticCtor(); + + ColourTable(PBYTE pbData, DWORD dwLength); + ColourTable(ColourTable *defaultColours, PBYTE pbData, DWORD dwLength); + + unsigned int getColour(eMinecraftColour id); + unsigned int getColor(eMinecraftColour id) { return getColour(id); } + + void loadColoursFromData(PBYTE pbData, DWORD dwLength); + void setColour(const wstring &colourName, int value); + void setColour(const wstring &colourName, const wstring &value); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/CommonMedia.sln b/Minecraft.Client/Common/CommonMedia.sln new file mode 100644 index 0000000..9f83988 --- /dev/null +++ b/Minecraft.Client/Common/CommonMedia.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonMedia", "CommonMedia.vcxproj", "{21BBD32C-AF5E-4741-8B80-3B73FC0D0F27}" +EndProject +Global + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 2 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = http://tfs_server:8080/tfs/storiespark + SccProjectUniqueName0 = CommonMedia.vcxproj + SccLocalPath0 = . + SccLocalPath1 = . + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {21BBD32C-AF5E-4741-8B80-3B73FC0D0F27}.Debug|Win32.ActiveCfg = Debug|Win32 + {21BBD32C-AF5E-4741-8B80-3B73FC0D0F27}.Debug|Win32.Build.0 = Debug|Win32 + {21BBD32C-AF5E-4741-8B80-3B73FC0D0F27}.Release|Win32.ActiveCfg = Release|Win32 + {21BBD32C-AF5E-4741-8B80-3B73FC0D0F27}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Minecraft.Client/Common/CommonMedia.vcxproj b/Minecraft.Client/Common/CommonMedia.vcxproj new file mode 100644 index 0000000..5a472e0 --- /dev/null +++ b/Minecraft.Client/Common/CommonMedia.vcxproj @@ -0,0 +1,115 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {21BBD32C-AF5E-4741-8B80-3B73FC0D0F27} + MakeFileProj + SAK + SAK + SAK + SAK + + + + Makefile + true + v110 + + + Makefile + false + v110 + + + + + + + + + + + + + WIN32;_DEBUG;$(NMakePreprocessorDefinitions) + echo Creating languages.loc +copy .\Media\strings.resx .\Media\en-EN.lang +copy .\Media\fr-FR\strings.resx .\Media\fr-FR\fr-FR.lang +copy .\Media\ja-JP\strings.resx .\Media\ja-JP\ja-JP.lang +..\..\..\Tools\NewLocalisationPacker.exe --static .\Media .\Media\languages.loc + +echo Making archive +..\..\..\Tools\ArchiveFilePacker.exe -cd $(ProjectDir)\Media media.arc media.txt + +echo Copying Durango strings.h +copy .\Media\strings.h ..\Durango\strings.h + +echo Copying PS3 strings.h +copy .\Media\strings.h ..\PS3\strings.h + +echo Copying PS4 strings.h +copy .\Media\strings.h ..\Orbis\strings.h + +echo Copying Win strings.h +copy .\Media\strings.h ..\Windows64\strings.h + + + WIN32;NDEBUG;$(NMakePreprocessorDefinitions) + + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Common/CommonMedia.vcxproj.filters b/Minecraft.Client/Common/CommonMedia.vcxproj.filters new file mode 100644 index 0000000..9fb0927 --- /dev/null +++ b/Minecraft.Client/Common/CommonMedia.vcxproj.filters @@ -0,0 +1,136 @@ + + + + + {55c7ab2e-b3e5-4aed-9ffe-3308591d9c34} + + + {eaa0eb72-0b27-4080-ad53-f68e42f37ba8} + + + {711ad95b-eb56-4e18-b001-34ad7b8075a3} + + + {1432ec3d-c5d0-46da-91b6-e7737095a97e} + + + {4b2aeaf1-04d7-454d-b2d9-08364799831c} + + + {4b0eaef6-fa2f-4605-b0da-a81ffb5659bc} + + + {bf1c74da-21f1-4bdd-98ed-83457946e4cc} + + + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + Archive + + + Archive + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + IggyMedia + + + + + Strings + + + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Strings + + + Archive + + + + + Archive\Durango + + + Archive\PS3 + + + Archive\PS4 + + + Archive\Win64 + + + \ No newline at end of file diff --git a/Minecraft.Client/Common/ConsoleGameMode.cpp b/Minecraft.Client/Common/ConsoleGameMode.cpp new file mode 100644 index 0000000..b080e62 --- /dev/null +++ b/Minecraft.Client/Common/ConsoleGameMode.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "ConsoleGameMode.h" +#include "..\Common\Tutorial\Tutorial.h" + +ConsoleGameMode::ConsoleGameMode(int iPad, Minecraft *minecraft, ClientConnection *connection) + : TutorialMode(iPad, minecraft, connection) +{ + tutorial = new Tutorial(iPad); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/ConsoleGameMode.h b/Minecraft.Client/Common/ConsoleGameMode.h new file mode 100644 index 0000000..3e486cb --- /dev/null +++ b/Minecraft.Client/Common/ConsoleGameMode.h @@ -0,0 +1,10 @@ +#pragma once +#include "..\Common\Tutorial\TutorialMode.h" + +class ConsoleGameMode : public TutorialMode +{ +public: + ConsoleGameMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + + virtual bool isImplemented() { return true; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Console_Awards_enum.h b/Minecraft.Client/Common/Console_Awards_enum.h new file mode 100644 index 0000000..9597c71 --- /dev/null +++ b/Minecraft.Client/Common/Console_Awards_enum.h @@ -0,0 +1,72 @@ +#pragma once + +enum eAward +{ + eAward_TakingInventory=0, + eAward_GettingWood, + eAward_Benchmarking, + eAward_TimeToMine, + eAward_HotTopic, + eAward_AquireHardware, + eAward_TimeToFarm, + eAward_BakeBread, + eAward_TheLie, + eAward_GettingAnUpgrade, + eAward_DeliciousFish, + eAward_OnARail, + eAward_TimeToStrike, + eAward_MonsterHunter, + eAward_CowTipper, + eAward_WhenPigsFly, + eAward_LeaderOfThePack, + eAward_MOARTools, + eAward_DispenseWithThis, + eAward_InToTheNether, + + eAward_mine100Blocks, + eAward_kill10Creepers, + eAward_eatPorkChop, + eAward_play100Days, + eAward_arrowKillCreeper, + eAward_socialPost, + +#ifndef _XBOX + // 4J Stu - Does not map to any Xbox achievements + eAward_snipeSkeleton, + eAward_diamonds, + eAward_portal, + eAward_ghast, + eAward_blazeRod, + eAward_potion, + eAward_theEnd, + eAward_winGame, + eAward_enchantments, + eAward_overkill, + eAward_bookcase, +#endif + +#ifdef _EXTENDED_ACHIEVEMENTS + eAward_adventuringTime, + eAward_repopulation, + //eAward_porkChop, + eAward_diamondsToYou, + //eAward_passingTheTime, + //eAward_archer, + eAward_theHaggler, + eAward_potPlanter, + eAward_itsASign, + eAward_ironBelly, + eAward_haveAShearfulDay, + eAward_rainbowCollection, + eAward_stayinFrosty, + eAward_chestfulOfCobblestone, + eAward_renewableEnergy, + eAward_musicToMyEars, + eAward_bodyGuard, + eAward_ironMan, + eAward_zombieDoctor, + eAward_lionTamer, +#endif + + eAward_Max, +}; diff --git a/Minecraft.Client/Common/Console_Debug_enum.h b/Minecraft.Client/Common/Console_Debug_enum.h new file mode 100644 index 0000000..4e6c2b1 --- /dev/null +++ b/Minecraft.Client/Common/Console_Debug_enum.h @@ -0,0 +1,42 @@ +#pragma once + +enum eDebugSetting +{ + eDebugSetting_LoadSavesFromDisk, + eDebugSetting_WriteSavesToDisk, + eDebugSetting_FreezePlayers, //eDebugSetting_InterfaceOff, + eDebugSetting_Safearea, + eDebugSetting_MobsDontAttack, + eDebugSetting_FreezeTime, + eDebugSetting_DisableWeather, + eDebugSetting_CraftAnything, + eDebugSetting_UseDpadForDebug, + eDebugSetting_MobsDontTick, + eDebugSetting_InstantDestroy, + eDebugSetting_ShowUIConsole, + eDebugSetting_DistributableSave, + eDebugSetting_DebugLeaderboards, + eDebugSetting_EnableHeightWaterBiomeOverride, //eDebugSetting_TipsAlwaysOn, + eDebugSetting_SuperflatNether, + //eDebugSetting_LightDarkBackground, + eDebugSetting_RegularLightning, + eDebugSetting_GoToNether, + //eDebugSetting_GoToEnd, + eDebugSetting_GoToOverworld, + eDebugSetting_UnlockAllDLC, // eDebugSetting_ToggleFont, + eDebugSetting_ShowUIMarketingGuide, + eDebugSetting_Max, +}; + +enum eDebugButton +{ + eDebugButton_Theme=0, + eDebugButton_Avatar_Item_1, + eDebugButton_Avatar_Item_2, + eDebugButton_Avatar_Item_3, + eDebugButton_Gamerpic_1, + eDebugButton_Gamerpic_2, + eDebugButton_CheckTips, + eDebugButton_WipeLeaderboards, + eDebugButton_Max, +}; diff --git a/Minecraft.Client/Common/Console_Utils.cpp b/Minecraft.Client/Common/Console_Utils.cpp new file mode 100644 index 0000000..cb0f1b5 --- /dev/null +++ b/Minecraft.Client/Common/Console_Utils.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" + +//-------------------------------------------------------------------------------------- +// Name: DebugSpewV() +// Desc: Internal helper function +//-------------------------------------------------------------------------------------- +#ifndef _CONTENT_PACKAGE +static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList ) +{ +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + assert(0); +#else + CHAR str[2048]; + // Use the secure CRT to avoid buffer overruns. Specify a count of + // _TRUNCATE so that too long strings will be silently truncated + // rather than triggering an error. + _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList ); + OutputDebugStringA( str ); +#endif +} +#endif + +//-------------------------------------------------------------------------------------- +// Name: DebugSpew() +// Desc: Prints formatted debug spew +//-------------------------------------------------------------------------------------- +#ifdef _Printf_format_string_ // VC++ 2008 and later support this annotation +VOID CDECL DebugSpew( _In_z_ _Printf_format_string_ const CHAR* strFormat, ... ) +#else +VOID CDECL DebugPrintf( const CHAR* strFormat, ... ) +#endif +{ +#ifndef _CONTENT_PACKAGE + va_list pArgList; + va_start( pArgList, strFormat ); + DebugSpewV( strFormat, pArgList ); + va_end( pArgList ); +#endif +} + diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp new file mode 100644 index 0000000..cf855ca --- /dev/null +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -0,0 +1,9539 @@ + +#include "stdafx.h" + +#include "..\..\Minecraft.World\Recipy.h" +#include "..\..\Minecraft.Client\Options.h" +#include "..\..\Minecraft.World\AABB.h" +#include "..\..\Minecraft.World\Vec3.h" +#include "..\MinecraftServer.h" +#include "..\MultiPlayerLevel.h" +#include "..\GameRenderer.h" +#include "..\ProgressRenderer.h" +#include "..\..\Minecraft.Client\LevelRenderer.h" +#include "..\..\Minecraft.Client\MobSkinMemTextureProcessor.h" +#include "..\..\Minecraft.Client\Minecraft.h" +#include "..\ClientConnection.h" +#include "..\MultiPlayerLocalPlayer.h" +#include "..\..\Minecraft.Client\LocalPlayer.h" +#include "..\..\Minecraft.World\Player.h" +#include "..\..\Minecraft.World\Inventory.h" +#include "..\..\Minecraft.World\Level.h" +#include "..\..\Minecraft.World\FurnaceTileEntity.h" +#include "..\..\Minecraft.World\Container.h" +#include "..\..\Minecraft.World\DispenserTileEntity.h" +#include "..\..\Minecraft.World\SignTileEntity.h" +#include "..\..\Minecraft.Client\StatsCounter.h" +#include "..\GameMode.h" +#include "..\Xbox\Social\SocialManager.h" +#include "Tutorial\TutorialMode.h" +#if defined _XBOX || defined _WINDOWS64 +#include "..\..\Minecraft.Client\Xbox\XML\ATGXmlParser.h" +#include "..\..\Minecraft.Client\Xbox\XML\xmlFilesCallback.h" +#endif +#include "Minecraft_Macros.h" +#include "..\..\Minecraft.Client\PlayerList.h" +#include "..\..\Minecraft.Client\ServerPlayer.h" +#include "GameRules\ConsoleGameRules.h" +#include "GameRules\ConsoleSchematicFile.h" +#include "..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\Minecraft.World\LevelSettings.h" +#include "..\User.h" +#include "..\..\Minecraft.World\LevelData.h" +#include "..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\Minecraft.Client\EntityRenderDispatcher.h" +#include "..\..\Minecraft.World\compression.h" +#include "..\TexturePackRepository.h" +#include "..\DLCTexturePack.h" +#include "DLC\DLCPack.h" +#include "..\StringTable.h" +#ifndef _XBOX +#include "..\ArchiveFile.h" +#endif +#include "..\Minecraft.h" +#ifdef _XBOX +#include "..\Xbox\GameConfig\Minecraft.spa.h" +#include "..\Xbox\Network\NetworkPlayerXbox.h" +#include "XUI\XUI_TextEntry.h" +#include "XUI\XUI_XZP_Icons.h" +#include "XUI\XUI_PauseMenu.h" +#else +#include "UI\UI.h" +#include "UI\UIScene_PauseMenu.h" +#endif +#ifdef __PS3__ +#include +#endif +#ifdef __ORBIS__ +#include +#endif + +#include "..\Common\Leaderboards\LeaderboardManager.h" + +//CMinecraftApp app; +unsigned int CMinecraftApp::m_uiLastSignInData = 0; + +const float CMinecraftApp::fSafeZoneX = 64.0f; // 5% of 1280 +const float CMinecraftApp::fSafeZoneY = 36.0f; // 5% of 720 + +int CMinecraftApp::s_iHTMLFontSizesA[eHTMLSize_COUNT] = +{ +#ifdef _XBOX + 14,12,14,24 +#else + //20,15,20,24 + 20,13,20,26 +#endif +}; + + +CMinecraftApp::CMinecraftApp() +{ + if(GAME_SETTINGS_PROFILE_DATA_BYTES != sizeof(GAME_SETTINGS)) + { + // 4J Stu - See comment for GAME_SETTINGS_PROFILE_DATA_BYTES in Xbox_App.h + DebugPrintf("WARNING: The size of the profile GAME_SETTINGS struct has changed, so all stat data is likely incorrect. Is: %d, Should be: %d\n",sizeof(GAME_SETTINGS),GAME_SETTINGS_PROFILE_DATA_BYTES); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + } + + for(int i=0;i; + } + + LocaleAndLanguageInit(); + +#ifdef _XBOX_ONE + m_hasReachedMainMenu = false; +#endif +} + + + +void CMinecraftApp::DebugPrintf(const char *szFormat, ...) +{ + +#ifndef _FINAL_BUILD + char buf[1024]; + va_list ap; + va_start(ap, szFormat); + vsnprintf(buf, sizeof(buf), szFormat, ap); + va_end(ap); + OutputDebugStringA(buf); +#endif + +} + +void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...) +{ +#ifndef _FINAL_BUILD + if(user == USER_NONE) + return; + char buf[1024]; + va_list ap; + va_start(ap, szFormat); + vsnprintf(buf, sizeof(buf), szFormat, ap); + va_end(ap); +#ifdef __PS3__ + unsigned int writelen; + sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen ); +#elif defined __PSVITA__ + switch(user) + { + case 0: + { + SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0); + if(tty2>=0) + { + std::string string1(buf); + sceIoWrite(tty2, string1.c_str(), string1.length()); + sceIoClose(tty2); + } + } + break; + case 1: + { + SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0); + if(tty3>=0) + { + std::string string1(buf); + sceIoWrite(tty3, string1.c_str(), string1.length()); + sceIoClose(tty3); + } + } + break; + default: + OutputDebugStringA(buf); + break; + } +#else + OutputDebugStringA(buf); +#endif +#ifndef _XBOX + if(user == USER_UI) + { + ui.logDebugString(buf); + } +#endif +#endif +} + +LPCWSTR CMinecraftApp::GetString(int iID) +{ + //return L"Değişiklikler ve Yenilikler"; + //return L"ÕÕÕÕÖÖÖÖ"; + return app.m_stringTable->getString(iID); +} + +void CMinecraftApp::SetAction(int iPad, eXuiAction action, LPVOID param) +{ + if(m_eXuiAction[iPad] == eAppAction_ExitWorldCapturedThumbnail && action != eAppAction_Idle) + { + app.DebugPrintf("Invalid change of App action for pad %d from %d to %d, ignoring\n", iPad, m_eXuiAction[iPad], action); + } + else + { + app.DebugPrintf("Changing App action for pad %d from %d to %d\n", iPad, m_eXuiAction[iPad], action); + m_eXuiAction[iPad]=action; + m_eXuiActionParam[iPad] = param; + } +} + +bool CMinecraftApp::IsAppPaused() +{ +#if defined(_XBOX_ONE) || defined(__ORBIS__) + bool paused = m_bIsAppPaused; + EnterCriticalSection(&m_saveNotificationCriticalSection); + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + { + paused |= m_saveNotificationDepth > 0; + } + LeaveCriticalSection(&m_saveNotificationCriticalSection); + return paused; +#else + return m_bIsAppPaused; +#endif +} + +void CMinecraftApp::SetAppPaused(bool val) +{ + m_bIsAppPaused = val; +} + +void CMinecraftApp::HandleButtonPresses() +{ + for(int i=0;i<4;i++) + { + HandleButtonPresses(i); + } +} + +void CMinecraftApp::HandleButtonPresses(int iPad) +{ + +// // test an update of the profile data +// void *pData=ProfileManager.GetGameDefinedProfileData(iPad); +// +// unsigned char *pchData= (unsigned char *)pData; +// int iCount=0; +// for(int i=0;i player,bool bNavigateBack) +{ + bool success = true; + + InventoryScreenInput* initData = new InventoryScreenInput(); + initData->player = player; + initData->bNavigateBack=bNavigateBack; + initData->iPad = iPad; + + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_InventoryMenu,initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_InventoryMenu,initData); + } + + return success; +} + +bool CMinecraftApp::LoadCreativeMenu(int iPad,shared_ptr player,bool bNavigateBack) +{ + bool success = true; + + InventoryScreenInput* initData = new InventoryScreenInput(); + initData->player = player; + initData->bNavigateBack=bNavigateBack; + initData->iPad = iPad; + + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_CreativeMenu,initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_CreativeMenu,initData); + } + + return success; +} + +bool CMinecraftApp::LoadCrafting2x2Menu(int iPad,shared_ptr player) +{ + bool success = true; + + CraftingPanelScreenInput* initData = new CraftingPanelScreenInput(); + initData->player = player; + initData->iContainerType=RECIPE_TYPE_2x2; + initData->iPad = iPad; + initData->x = 0; + initData->y = 0; + initData->z = 0; + + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_Crafting2x2Menu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_Crafting2x2Menu, initData); + } + + return success; +} + +bool CMinecraftApp::LoadCrafting3x3Menu(int iPad,shared_ptr player, int x, int y, int z) +{ + bool success = true; + + CraftingPanelScreenInput* initData = new CraftingPanelScreenInput(); + initData->player = player; + initData->iContainerType=RECIPE_TYPE_3x3; + initData->iPad = iPad; + initData->x = x; + initData->y = y; + initData->z = z; + + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_Crafting3x3Menu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_Crafting3x3Menu, initData); + } + + return success; +} + +bool CMinecraftApp::LoadEnchantingMenu(int iPad,shared_ptr inventory, int x, int y, int z, Level *level) +{ + bool success = true; + + EnchantingScreenInput* initData = new EnchantingScreenInput(); + initData->inventory = inventory; + initData->level = level; + initData->x = x; + initData->y = y; + initData->z = z; + initData->iPad = iPad; + + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_EnchantingMenu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_EnchantingMenu, initData); + } + + return success; +} + +bool CMinecraftApp::LoadFurnaceMenu(int iPad,shared_ptr inventory, shared_ptr furnace) +{ + bool success = true; + + FurnaceScreenInput* initData = new FurnaceScreenInput(); + + initData->furnace = furnace; + initData->inventory = inventory; + initData->iPad = iPad; + + // Load the scene. + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_FurnaceMenu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_FurnaceMenu, initData); + } + + return success; +} + +bool CMinecraftApp::LoadBrewingStandMenu(int iPad,shared_ptr inventory, shared_ptr brewingStand) +{ + bool success = true; + + BrewingScreenInput* initData = new BrewingScreenInput(); + + initData->brewingStand = brewingStand; + initData->inventory = inventory; + initData->iPad = iPad; + + // Load the scene. + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_BrewingStandMenu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_BrewingStandMenu, initData); + } + + return success; +} + + +bool CMinecraftApp::LoadContainerMenu(int iPad,shared_ptr inventory, shared_ptr container) +{ + bool success = true; + + ContainerScreenInput* initData = new ContainerScreenInput(); + + initData->inventory = inventory; + initData->container = container; + initData->iPad = iPad; + + // Load the scene. + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + + bool bLargeChest = (initData->container->getContainerSize() > 3*9)?true:false; + if(bLargeChest) + { + success = ui.NavigateToScene(iPad,eUIScene_LargeContainerMenu,initData); + } + else + { + success = ui.NavigateToScene(iPad,eUIScene_ContainerMenu,initData); + } + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_ContainerMenu,initData); + } + + return success; +} + +bool CMinecraftApp::LoadTrapMenu(int iPad,shared_ptr inventory, shared_ptr trap) +{ + bool success = true; + + TrapScreenInput* initData = new TrapScreenInput(); + + initData->inventory = inventory; + initData->trap = trap; + initData->iPad = iPad; + + // Load the scene. + if(app.GetLocalPlayerCount()>1) + { + initData->bSplitscreen=true; + success = ui.NavigateToScene(iPad,eUIScene_DispenserMenu, initData); + } + else + { + initData->bSplitscreen=false; + success = ui.NavigateToScene(iPad,eUIScene_DispenserMenu, initData); + } + + return success; +} + +bool CMinecraftApp::LoadSignEntryMenu(int iPad,shared_ptr sign) +{ + bool success = true; + + SignEntryScreenInput* initData = new SignEntryScreenInput(); + + initData->sign = sign; + initData->iPad = iPad; + + success = ui.NavigateToScene(iPad,eUIScene_SignEntryMenu, initData); + + delete initData; + + return success; +} + +bool CMinecraftApp::LoadRepairingMenu(int iPad,shared_ptr inventory, Level *level, int x, int y, int z) +{ + bool success = true; + + AnvilScreenInput *initData = new AnvilScreenInput(); + initData->inventory = inventory; + initData->level = level; + initData->x = x; + initData->y = y; + initData->z = z; + initData->iPad = iPad; + if(app.GetLocalPlayerCount()>1) initData->bSplitscreen=true; + else initData->bSplitscreen=false; + + success = ui.NavigateToScene(iPad,eUIScene_AnvilMenu, initData); + + return success; +} + +bool CMinecraftApp::LoadTradingMenu(int iPad, shared_ptr inventory, shared_ptr trader, Level *level) +{ + bool success = true; + + TradingScreenInput *initData = new TradingScreenInput(); + initData->inventory = inventory; + initData->trader = trader; + initData->level = level; + initData->iPad = iPad; + if(app.GetLocalPlayerCount()>1) initData->bSplitscreen=true; + else initData->bSplitscreen=false; + + success = ui.NavigateToScene(iPad,eUIScene_TradingMenu, initData); + + return success; +} + + +////////////////////////////////////////////// +// GAME SETTINGS +////////////////////////////////////////////// +void CMinecraftApp::InitGameSettings() +{ + for(int i=0;ibSettingsChanged=false; + + //SetDefaultGameSettings(i); - done on a callback from the profile manager + + // 4J-PB - adding in for Windows & PS3 to set the defaults for the joypad +#if defined _WINDOWS64// || defined __PSVITA__ + C_4JProfile::PROFILESETTINGS *pProfileSettings=ProfileManager.GetDashboardProfileSettings(i); + // clear this for now - it will come from reading the system values + memset(pProfileSettings,0,sizeof(C_4JProfile::PROFILESETTINGS)); + SetDefaultOptions(pProfileSettings,i); +#elif defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__ + C4JStorage::PROFILESETTINGS *pProfileSettings=StorageManager.GetDashboardProfileSettings(i); + // 4J-PB - don't cause an options write to happen here + SetDefaultOptions(pProfileSettings,i,false); + +#endif + } +} + +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) +int CMinecraftApp::SetDefaultOptions(C4JStorage::PROFILESETTINGS *pSettings,const int iPad,bool bWriteProfile) +#else +int CMinecraftApp::SetDefaultOptions(C_4JProfile::PROFILESETTINGS *pSettings,const int iPad) +#endif +{ + SetGameSettings(iPad,eGameSetting_MusicVolume,DEFAULT_VOLUME_LEVEL); + SetGameSettings(iPad,eGameSetting_SoundFXVolume,DEFAULT_VOLUME_LEVEL); + SetGameSettings(iPad,eGameSetting_Gamma,100); + + // 4J-PB - Don't reset the difficult level if we're in-game + if(Minecraft::GetInstance()->level==NULL) + { + app.DebugPrintf("SetDefaultOptions - Difficulty = 1\n"); + SetGameSettings(iPad,eGameSetting_Difficulty,1); + } + SetGameSettings(iPad,eGameSetting_Sensitivity_InGame,100); + SetGameSettings(iPad,eGameSetting_ViewBob,1); + SetGameSettings(iPad,eGameSetting_ControlScheme,0); + SetGameSettings(iPad,eGameSetting_ControlInvertLook,(pSettings->iYAxisInversion!=0)?1:0); + SetGameSettings(iPad,eGameSetting_ControlSouthPaw,pSettings->bSwapSticks?1:0); + SetGameSettings(iPad,eGameSetting_SplitScreenVertical,0); + SetGameSettings(iPad,eGameSetting_GamertagsVisible,1); + + // Interim TU 1.6.6 + SetGameSettings(iPad,eGameSetting_Sensitivity_InMenu,100); + SetGameSettings(iPad,eGameSetting_DisplaySplitscreenGamertags,1); + SetGameSettings(iPad,eGameSetting_Hints,1); + SetGameSettings(iPad,eGameSetting_Autosave,2); + SetGameSettings(iPad,eGameSetting_Tooltips,1); + SetGameSettings(iPad,eGameSetting_InterfaceOpacity,80); + + // TU 5 + SetGameSettings(iPad,eGameSetting_Clouds,1); + SetGameSettings(iPad,eGameSetting_Online,1); + SetGameSettings(iPad,eGameSetting_InviteOnly,0); + SetGameSettings(iPad,eGameSetting_FriendsOfFriends,1); + + // default the update changes message to zero + // 4J-PB - We'll only display the message if the profile is pre-TU5 + //SetGameSettings(iPad,eGameSetting_DisplayUpdateMessage,0); + + // TU 6 + SetGameSettings(iPad,eGameSetting_BedrockFog,0); + SetGameSettings(iPad,eGameSetting_DisplayHUD,1); + SetGameSettings(iPad,eGameSetting_DisplayHand,1); + + // TU 7 + SetGameSettings(iPad,eGameSetting_CustomSkinAnim,1); + + // TU 9 + SetGameSettings(iPad,eGameSetting_DeathMessages,1); + SetGameSettings(iPad,eGameSetting_UISize,1); + SetGameSettings(iPad,eGameSetting_UISizeSplitscreen,2); + SetGameSettings(iPad,eGameSetting_AnimatedCharacter,1); + + // TU 12 + GameSettingsA[iPad]->ucCurrentFavoriteSkinPos=0; + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + + // TU 13 + GameSettingsA[iPad]->uiMashUpPackWorldsDisplay=0xFFFFFFFF; + + // 4J-PB - leave these in, or remove from everywhere they are referenced! + // Although probably best to leave in unless we split the profile settings into platform specific classes - having different meaning per platform for the same bitmask could get confusing +//#ifdef __PS3__ + // PS3DEC13 + SetGameSettings(iPad,eGameSetting_PS3_EULA_Read,0); // EULA not read + + // PS3 1.05 - added Greek + GameSettingsA[iPad]->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language +//#endif + +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GameSettingsA[iPad]->bSettingsChanged=bWriteProfile; +#endif + + return 0; +} + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) +int CMinecraftApp::DefaultOptionsCallback(LPVOID pParam,C4JStorage::PROFILESETTINGS *pSettings, const int iPad) +#else +int CMinecraftApp::DefaultOptionsCallback(LPVOID pParam,C_4JProfile::PROFILESETTINGS *pSettings, const int iPad) +#endif +{ + CMinecraftApp *pApp=(CMinecraftApp *)pParam; + + // flag the default options to be set + + pApp->DebugPrintf("Setting default options for player %d", iPad); + pApp->SetAction(iPad,eAppAction_SetDefaultOptions, (LPVOID)pSettings); + //pApp->SetDefaultOptions(pSettings,iPad); + + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + //pApp->CheckGameSettingsChanged(); + + return 0; +} + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + +#ifdef __ORBIS__ +int CMinecraftApp::OptionsDataCallback(LPVOID pParam,int iPad,unsigned short usVersion,C4JStorage::eOptionsCallback eStatus,int iBlocksRequired) +{ + CMinecraftApp *pApp=(CMinecraftApp *)pParam; + pApp->m_eOptionsStatusA[iPad]=eStatus; + pApp->m_eOptionsBlocksRequiredA[iPad]=iBlocksRequired; + return 0; +} + +int CMinecraftApp::GetOptionsBlocksRequired(int iPad) +{ + return m_eOptionsBlocksRequiredA[iPad]; +} + +#else +int CMinecraftApp::OptionsDataCallback(LPVOID pParam,int iPad,unsigned short usVersion,C4JStorage::eOptionsCallback eStatus) +{ + CMinecraftApp *pApp=(CMinecraftApp *)pParam; + pApp->m_eOptionsStatusA[iPad]=eStatus; + return 0; +} +#endif + +C4JStorage::eOptionsCallback CMinecraftApp::GetOptionsCallbackStatus(int iPad) +{ + return m_eOptionsStatusA[iPad]; +} + +void CMinecraftApp::SetOptionsCallbackStatus(int iPad, C4JStorage::eOptionsCallback eStatus) +{ + m_eOptionsStatusA[iPad]=eStatus; +} +#endif + +int CMinecraftApp::OldProfileVersionCallback(LPVOID pParam,unsigned char *pucData, const unsigned short usVersion, const int iPad) +{ + // check what needs to be done with this version to update to the current one + + switch(usVersion) + { +#ifdef _XBOX + case PROFILE_VERSION_1: + case PROFILE_VERSION_2: + // need to fill in values for the new profile data. No need to save the profile - that'll happen if they get changed, or if the auto save for the profile kicks in + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + pGameSettings->ucMenuSensitivity=100; //eGameSetting_Sensitivity_InMenu + pGameSettings->ucInterfaceOpacity=80; //eGameSetting_Sensitivity_InMenu + pGameSettings->usBitmaskValues|=0x0200; //eGameSetting_DisplaySplitscreenGamertags - on + pGameSettings->usBitmaskValues|=0x0400; //eGameSetting_Hints - on + pGameSettings->usBitmaskValues|=0x1000; //eGameSetting_Autosave - 2 + pGameSettings->usBitmaskValues|=0x8000; //eGameSetting_Tooltips - on + + // 4J-PB - Let's also award all the achievements they have again because of the profile bug that seemed to stop the awards of some + // Changing this to check the system achievements at sign-in and award any that the game says we have and the system says we haven't + //ProfileManager.ReAwardAchievements(iPad); + + pGameSettings->uiBitmaskValues=0L; // reset + pGameSettings->uiBitmaskValues|=GAMESETTING_CLOUDS; //eGameSetting_Clouds - on + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + //eGameSetting_GameSetting_Invite - off + pGameSettings->uiBitmaskValues|=GAMESETTING_FRIENDSOFFRIENDS; //eGameSetting_GameSetting_FriendsOfFriends - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + // TU6 + pGameSettings->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; //eGameSetting_BedrockFog - off + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; //eGameSetting_DisplayHUD - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; //eGameSetting_DisplayHand - on + // TU7 + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + // TU9 + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + } + break; + case PROFILE_VERSION_3: + + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + pGameSettings->uiBitmaskValues=0L; // reset + pGameSettings->uiBitmaskValues|=GAMESETTING_CLOUDS; //eGameSetting_Clouds - on + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + //eGameSetting_GameSetting_Invite - off + pGameSettings->uiBitmaskValues|=GAMESETTING_FRIENDSOFFRIENDS; //eGameSetting_GameSetting_FriendsOfFriends - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + // TU6 + pGameSettings->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; //eGameSetting_BedrockFog - off + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; //eGameSetting_DisplayHUD - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; //eGameSetting_DisplayHand - on + // TU7 + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + // TU9 + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + } + break; + case PROFILE_VERSION_4: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + + pGameSettings->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; //eGameSetting_BedrockFog - off + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; //eGameSetting_DisplayHUD - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; //eGameSetting_DisplayHand - on + // TU7 + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + // TU9 + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + + // Set the online flag to on, so it's not saved if a game starts offline when the user didn't change it to be offline (xbox disconnected from LIVE) + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + } + + break; + case PROFILE_VERSION_5: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + + // reset the display new message counter + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + // TU7 + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + // TU9 + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + // Set the online flag to on, so it's not saved if a game starts offline when the user didn't change it to be offline (xbox disconnected from LIVE) + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + + } + + break; + case PROFILE_VERSION_6: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + + // Added gui size for splitscreen and fullscreen + // Added death messages toggle + + // reset the display new message counter + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + // TU9 + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + // Set the online flag to on, so it's not saved if a game starts offline when the user didn't change it to be offline (xbox disconnected from LIVE) + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + + } + + break; + + case PROFILE_VERSION_7: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + // reset the display new message counter + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + + } + break; +#endif + case PROFILE_VERSION_8: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + // reset the display new message counter + //pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3DEC13 + pGameSettings->uiBitmaskValues&=~GAMESETTING_PS3EULAREAD; //eGameSetting_PS3_EULA_Read - off + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + } + break; + case PROFILE_VERSION_9: + // PS3DEC13 + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + pGameSettings->uiBitmaskValues&=~GAMESETTING_PS3EULAREAD; //eGameSetting_PS3_EULA_Read - off + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + } + break; + case PROFILE_VERSION_10: + { + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + } + break; + + default: + { + // This might be from a version during testing of new profile updates + app.DebugPrintf("Don't know what to do with this profile version!\n"); + #ifndef _CONTENT_PACKAGE + // __debugbreak(); + #endif + + GAME_SETTINGS *pGameSettings=(GAME_SETTINGS *)pucData; + pGameSettings->ucMenuSensitivity=100; //eGameSetting_Sensitivity_InMenu + pGameSettings->ucInterfaceOpacity=80; //eGameSetting_Sensitivity_InMenu + pGameSettings->usBitmaskValues|=0x0200; //eGameSetting_DisplaySplitscreenGamertags - on + pGameSettings->usBitmaskValues|=0x0400; //eGameSetting_Hints - on + pGameSettings->usBitmaskValues|=0x1000; //eGameSetting_Autosave - 2 + pGameSettings->usBitmaskValues|=0x8000; //eGameSetting_Tooltips - on + + pGameSettings->uiBitmaskValues=0L; // reset + pGameSettings->uiBitmaskValues|=GAMESETTING_CLOUDS; //eGameSetting_Clouds - on + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + //eGameSetting_GameSetting_Invite - off + pGameSettings->uiBitmaskValues|=GAMESETTING_FRIENDSOFFRIENDS; //eGameSetting_GameSetting_FriendsOfFriends - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + pGameSettings->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; //eGameSetting_BedrockFog - off + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; //eGameSetting_DisplayHUD - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; //eGameSetting_DisplayHand - on + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3DEC13 + pGameSettings->uiBitmaskValues&=~GAMESETTING_PS3EULAREAD; //eGameSetting_PS3_EULA_Read - off + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + } + break; + } + + return 0; +} + +void CMinecraftApp::ApplyGameSettingsChanged(int iPad) +{ + ActionGameSettings(iPad,eGameSetting_MusicVolume ); + ActionGameSettings(iPad,eGameSetting_SoundFXVolume ); + ActionGameSettings(iPad,eGameSetting_Gamma ); + ActionGameSettings(iPad,eGameSetting_Difficulty ); + ActionGameSettings(iPad,eGameSetting_Sensitivity_InGame ); + ActionGameSettings(iPad,eGameSetting_ViewBob ); + ActionGameSettings(iPad,eGameSetting_ControlScheme ); + ActionGameSettings(iPad,eGameSetting_ControlInvertLook); + ActionGameSettings(iPad,eGameSetting_ControlSouthPaw); + ActionGameSettings(iPad,eGameSetting_SplitScreenVertical); + ActionGameSettings(iPad,eGameSetting_GamertagsVisible); + + // Interim TU 1.6.6 + ActionGameSettings(iPad,eGameSetting_Sensitivity_InMenu ); + ActionGameSettings(iPad,eGameSetting_DisplaySplitscreenGamertags); + ActionGameSettings(iPad,eGameSetting_Hints); + ActionGameSettings(iPad,eGameSetting_InterfaceOpacity); + ActionGameSettings(iPad,eGameSetting_Tooltips); + + ActionGameSettings(iPad,eGameSetting_Clouds); + ActionGameSettings(iPad,eGameSetting_BedrockFog); + ActionGameSettings(iPad,eGameSetting_DisplayHUD); + ActionGameSettings(iPad,eGameSetting_DisplayHand); + ActionGameSettings(iPad,eGameSetting_CustomSkinAnim); + ActionGameSettings(iPad,eGameSetting_DeathMessages); + ActionGameSettings(iPad,eGameSetting_UISize); + ActionGameSettings(iPad,eGameSetting_UISizeSplitscreen); + ActionGameSettings(iPad,eGameSetting_AnimatedCharacter); + + ActionGameSettings(iPad,eGameSetting_PS3_EULA_Read); + +} + +void CMinecraftApp::ActionGameSettings(int iPad,eGameSetting eVal) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + switch(eVal) + { + case eGameSetting_MusicVolume: + if(iPad==ProfileManager.GetPrimaryPad()) + { + pMinecraft->options->set(Options::Option::MUSIC,((float)GameSettingsA[iPad]->ucMusicVolume)/100.0f); + } + break; + case eGameSetting_SoundFXVolume: + if(iPad==ProfileManager.GetPrimaryPad()) + { + pMinecraft->options->set(Options::Option::SOUND,((float)GameSettingsA[iPad]->ucSoundFXVolume)/100.0f); + } + break; + case eGameSetting_Gamma: + if(iPad==ProfileManager.GetPrimaryPad()) + { + // ucGamma range is 0-100, UpdateGamma is 0 - 32768 + float fVal=((float)GameSettingsA[iPad]->ucGamma)*327.68f; + RenderManager.UpdateGamma((unsigned short)fVal); +#ifdef _WINDOWS64 + extern void Windows64_UpdateGamma(unsigned short); + Windows64_UpdateGamma((unsigned short)fVal); +#endif + } + + break; + case eGameSetting_Difficulty: + if(iPad==ProfileManager.GetPrimaryPad()) + { + pMinecraft->options->toggle(Options::Option::DIFFICULTY,GameSettingsA[iPad]->usBitmaskValues&0x03); + app.DebugPrintf("Difficulty toggle to %d\n",GameSettingsA[iPad]->usBitmaskValues&0x03); + + // Update the Game Host setting + app.SetGameHostOption(eGameHostOption_Difficulty,pMinecraft->options->difficulty); + + // send this to the other players if we are in-game + bool bInGame=pMinecraft->level!=NULL; + + // Game Host only (and for now we can't change the diff while in game, so this shouldn't happen) + if(bInGame && g_NetworkManager.IsHost() && (iPad==ProfileManager.GetPrimaryPad())) + { + app.SetXuiServerAction(iPad,eXuiServerAction_ServerSettingChanged_Difficulty); + } + } + else + { + app.DebugPrintf("NOT ACTIONING DIFFICULTY - Primary pad is %d, This pad is %d\n",ProfileManager.GetPrimaryPad(),iPad); + } + + break; + case eGameSetting_Sensitivity_InGame: + // 4J-PB - we don't use the options value + // tell the input that we've changed the sensitivity - range of the slider is 0 to 200, default is 100 + pMinecraft->options->set(Options::Option::SENSITIVITY,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); + //InputManager.SetJoypadSensitivity(iPad,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); + + break; + case eGameSetting_ViewBob: + // 4J-PB - not handled here any more - it's read from the gamesettings per player + //pMinecraft->options->toggle(Options::Option::VIEW_BOBBING,GameSettingsA[iPad]->usBitmaskValues&0x04); + break; + case eGameSetting_ControlScheme: + InputManager.SetJoypadMapVal(iPad,(GameSettingsA[iPad]->usBitmaskValues&0x30)>>4); + break; + + case eGameSetting_ControlInvertLook: + // Nothing specific to do for this setting. + break; + + case eGameSetting_ControlSouthPaw: + // What is the setting? + if ( GameSettingsA[iPad]->usBitmaskValues & 0x80 ) + { + // Southpaw. + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_LX, AXIS_MAP_RX ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_LY, AXIS_MAP_RY ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_RX, AXIS_MAP_LX ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_RY, AXIS_MAP_LY ); + InputManager.SetJoypadStickTriggerMap( iPad, TRIGGER_MAP_0, TRIGGER_MAP_1 ); + InputManager.SetJoypadStickTriggerMap( iPad, TRIGGER_MAP_1, TRIGGER_MAP_0 ); + } + else + { + // Right handed. + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_LX, AXIS_MAP_LX ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_LY, AXIS_MAP_LY ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_RX, AXIS_MAP_RX ); + InputManager.SetJoypadStickAxisMap( iPad, AXIS_MAP_RY, AXIS_MAP_RY ); + InputManager.SetJoypadStickTriggerMap( iPad, TRIGGER_MAP_0, TRIGGER_MAP_0 ); + InputManager.SetJoypadStickTriggerMap( iPad, TRIGGER_MAP_1, TRIGGER_MAP_1 ); + } + break; + case eGameSetting_SplitScreenVertical: + if(iPad==ProfileManager.GetPrimaryPad()) + { + pMinecraft->updatePlayerViewportAssignments(); + } + break; + case eGameSetting_GamertagsVisible: + { + bool bInGame=pMinecraft->level!=NULL; + + // Game Host only + if(bInGame && g_NetworkManager.IsHost() && (iPad==ProfileManager.GetPrimaryPad())) + { + // Update the Game Host setting if you are the host and you are in-game + app.SetGameHostOption(eGameHostOption_Gamertags,((GameSettingsA[iPad]->usBitmaskValues&0x0008)!=0)?1:0); + app.SetXuiServerAction(iPad,eXuiServerAction_ServerSettingChanged_Gamertags); + + PlayerList *players = MinecraftServer::getInstance()->getPlayerList(); + for(AUTO_VAR(it3, players->players.begin()); it3 != players->players.end(); ++it3) + { + shared_ptr decorationPlayer = *it3; + decorationPlayer->setShowOnMaps((app.GetGameHostOption(eGameHostOption_Gamertags)!=0)?true:false); + } + } + } + break; +// Interim TU 1.6.6 + case eGameSetting_Sensitivity_InMenu: + // 4J-PB - we don't use the options value + // tell the input that we've changed the sensitivity - range of the slider is 0 to 200, default is 100 + //pMinecraft->options->set(Options::Option::SENSITIVITY,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); + //InputManager.SetJoypadSensitivity(iPad,((float)GameSettingsA[iPad]->ucSensitivity)/100.0f); + + break; + + case eGameSetting_DisplaySplitscreenGamertags: + for( BYTE idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(pMinecraft->localplayers[idx] != NULL) + { + if(pMinecraft->localplayers[idx]->m_iScreenSection==C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + ui.DisplayGamertag(idx,false); + } + else + { + ui.DisplayGamertag(idx,true); + } + } + } + + break; + case eGameSetting_InterfaceOpacity: + // update the tooltips display + ui.RefreshTooltips( iPad); + + break; + case eGameSetting_Hints: + //nothing to do here + break; + case eGameSetting_Tooltips: + if((GameSettingsA[iPad]->usBitmaskValues&0x8000)!=0) + { + ui.SetEnableTooltips(iPad,TRUE); + } + else + { + ui.SetEnableTooltips(iPad,FALSE); + } + break; + case eGameSetting_Clouds: + //nothing to do here + break; + case eGameSetting_Online: + //nothing to do here + break; + case eGameSetting_InviteOnly: + //nothing to do here + break; + case eGameSetting_FriendsOfFriends: + //nothing to do here + break; + case eGameSetting_BedrockFog: + { + bool bInGame=pMinecraft->level!=NULL; + + // Game Host only + if(bInGame && g_NetworkManager.IsHost() && (iPad==ProfileManager.GetPrimaryPad())) + { + // Update the Game Host setting if you are the host and you are in-game + app.SetGameHostOption(eGameHostOption_BedrockFog,GetGameSettings(iPad,eGameSetting_BedrockFog)?1:0); + app.SetXuiServerAction(iPad,eXuiServerAction_ServerSettingChanged_BedrockFog); + } + } + break; + case eGameSetting_DisplayHUD: + //nothing to do here + break; + case eGameSetting_DisplayHand: + //nothing to do here + break; + case eGameSetting_CustomSkinAnim: + //nothing to do here + break; + case eGameSetting_DeathMessages: + //nothing to do here + break; + case eGameSetting_UISize: + //nothing to do here + break; + case eGameSetting_UISizeSplitscreen: + //nothing to do here + break; + case eGameSetting_AnimatedCharacter: + //nothing to do here + break; + case eGameSetting_PS3_EULA_Read: + //nothing to do here + break; + case eGameSetting_PSVita_NetworkModeAdhoc: + //nothing to do here + break; + } +} + +void CMinecraftApp::SetPlayerSkin(int iPad,const wstring &name) +{ + DWORD skinId = app.getSkinIdFromPath(name); + + SetPlayerSkin(iPad,skinId); +} + +void CMinecraftApp::SetPlayerSkin(int iPad,DWORD dwSkinId) +{ + DebugPrintf("Setting skin for %d to %08X\n", iPad, dwSkinId); + + GameSettingsA[iPad]->dwSelectedSkin = dwSkinId; + GameSettingsA[iPad]->bSettingsChanged = true; + + TelemetryManager->RecordSkinChanged(iPad, GameSettingsA[iPad]->dwSelectedSkin); + + if(Minecraft::GetInstance()->localplayers[iPad]!=NULL) Minecraft::GetInstance()->localplayers[iPad]->setAndBroadcastCustomSkin(dwSkinId); +} + + +wstring CMinecraftApp::GetPlayerSkinName(int iPad) +{ + return app.getSkinPathFromId(GameSettingsA[iPad]->dwSelectedSkin); +} + +DWORD CMinecraftApp::GetPlayerSkinId(int iPad) +{ + // 4J-PB -check the user has rights to use this skin - they may have had at some point but the entitlement has been removed. + DLCPack *Pack=NULL; + DLCSkinFile *skinFile=NULL; + DWORD dwSkin=GameSettingsA[iPad]->dwSelectedSkin; + wchar_t chars[256]; + + if( GET_IS_DLC_SKIN_FROM_BITMASK(dwSkin) ) + { + // 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually + swprintf(chars, 256, L"dlcskin%08d.png", GET_DLC_SKIN_ID_FROM_BITMASK(dwSkin)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + bool bSkinIsFree = skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ); + bool bLicensed = Pack->hasPurchasedFile( DLCManager::e_DLCType_Skin, skinFile->getPath() ); + + if(bSkinIsFree || bLicensed) + { + return dwSkin; + } + else + { + return 0; + } + } + } + + + return dwSkin; +} + + DWORD CMinecraftApp::GetAdditionalModelParts(int iPad) + { + return m_dwAdditionalModelParts[iPad]; + } + + +void CMinecraftApp::SetPlayerCape(int iPad,const wstring &name) +{ + DWORD capeId = Player::getCapeIdFromPath(name); + + SetPlayerCape(iPad,capeId); +} + +void CMinecraftApp::SetPlayerCape(int iPad,DWORD dwCapeId) +{ + DebugPrintf("Setting cape for %d to %08X\n", iPad, dwCapeId); + + GameSettingsA[iPad]->dwSelectedCape = dwCapeId; + GameSettingsA[iPad]->bSettingsChanged = true; + + //SentientManager.RecordSkinChanged(iPad, GameSettingsA[iPad]->dwSelectedSkin); + + if(Minecraft::GetInstance()->localplayers[iPad]!=NULL) Minecraft::GetInstance()->localplayers[iPad]->setAndBroadcastCustomCape(dwCapeId); +} + +wstring CMinecraftApp::GetPlayerCapeName(int iPad) +{ + return Player::getCapePathFromId(GameSettingsA[iPad]->dwSelectedCape); +} + +DWORD CMinecraftApp::GetPlayerCapeId(int iPad) +{ + return GameSettingsA[iPad]->dwSelectedCape; +} + +void CMinecraftApp::SetPlayerFavoriteSkin(int iPad, int iIndex,unsigned int uiSkinID) +{ + DebugPrintf("Setting favorite skin for %d to %08X\n", iPad, uiSkinID); + + GameSettingsA[iPad]->uiFavoriteSkinA[iIndex] = uiSkinID; + GameSettingsA[iPad]->bSettingsChanged = true; +} + +unsigned int CMinecraftApp::GetPlayerFavoriteSkin(int iPad,int iIndex) +{ + return GameSettingsA[iPad]->uiFavoriteSkinA[iIndex]; +} + +unsigned char CMinecraftApp::GetPlayerFavoriteSkinsPos(int iPad) +{ + return GameSettingsA[iPad]->ucCurrentFavoriteSkinPos; +} + +void CMinecraftApp::SetPlayerFavoriteSkinsPos(int iPad, int iPos) +{ + GameSettingsA[iPad]->ucCurrentFavoriteSkinPos=(unsigned char)iPos; + GameSettingsA[iPad]->bSettingsChanged = true; +} + +unsigned int CMinecraftApp::GetPlayerFavoriteSkinsCount(int iPad) +{ + unsigned int uiCount=0; + for(int i=0;iuiFavoriteSkinA[i]!=0xFFFFFFFF) + { + uiCount++; + } + else + { + break; + } + } + return uiCount; +} + +void CMinecraftApp::ValidateFavoriteSkins(int iPad) +{ + unsigned int uiCount=GetPlayerFavoriteSkinsCount(iPad); + + // remove invalid skins + unsigned int uiValidSkin=0; + wchar_t chars[256]; + + for(unsigned int i=0;igetFile(DLCManager::e_DLCType_Skin,chars); + DLCSkinFile *pSkinFile = pDLCPack->getSkinFile(chars); + + if( pDLCPack->hasPurchasedFile(DLCManager::e_DLCType_Skin, L"") || (pSkinFile && pSkinFile->isFree())) + { + GameSettingsA[iPad]->uiFavoriteSkinA[uiValidSkin++]=GameSettingsA[iPad]->uiFavoriteSkinA[i]; + } + } + } + + for(unsigned int i=uiValidSkin;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } +} + +// Mash-up pack worlds +void CMinecraftApp::HideMashupPackWorld(int iPad, unsigned int iMashupPackID) +{ + unsigned int uiPackID=iMashupPackID - 1024; // mash-up ids start at 1024 + GameSettingsA[iPad]->uiMashUpPackWorldsDisplay&=~(1<bSettingsChanged = true; +} + +void CMinecraftApp::SetMinecraftLanguage(int iPad, unsigned char ucLanguage) +{ + GameSettingsA[iPad]->ucLanguage = ucLanguage; + GameSettingsA[iPad]->bSettingsChanged = true; +} + +void CMinecraftApp::EnableMashupPackWorlds(int iPad) +{ + GameSettingsA[iPad]->uiMashUpPackWorldsDisplay=0xFFFFFFFF; + GameSettingsA[iPad]->bSettingsChanged = true; +} + +unsigned int CMinecraftApp::GetMashupPackWorlds(int iPad) +{ + return GameSettingsA[iPad]->uiMashUpPackWorldsDisplay; +} + +unsigned char CMinecraftApp::GetMinecraftLanguage(int iPad) +{ + // if there are no game settings read yet, return the default language + if(GameSettingsA[iPad]==NULL) + { + return 0; + } + else + { + return GameSettingsA[iPad]->ucLanguage; + } +} + +void CMinecraftApp::SetGameSettings(int iPad,eGameSetting eVal,unsigned char ucVal) +{ + //Minecraft *pMinecraft=Minecraft::GetInstance(); + + switch(eVal) + { + case eGameSetting_MusicVolume: + if(GameSettingsA[iPad]->ucMusicVolume!=ucVal) + { + GameSettingsA[iPad]->ucMusicVolume=ucVal; + if(iPad==ProfileManager.GetPrimaryPad()) + { + ActionGameSettings(iPad,eVal); + } + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_SoundFXVolume: + if(GameSettingsA[iPad]->ucSoundFXVolume!=ucVal) + { + GameSettingsA[iPad]->ucSoundFXVolume=ucVal; + if(iPad==ProfileManager.GetPrimaryPad()) + { + ActionGameSettings(iPad,eVal); + } + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_Gamma: + if(GameSettingsA[iPad]->ucGamma!=ucVal) + { + GameSettingsA[iPad]->ucGamma=ucVal; + if(iPad==ProfileManager.GetPrimaryPad()) + { + ActionGameSettings(iPad,eVal); + } + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_Difficulty: + if((GameSettingsA[iPad]->usBitmaskValues&0x03)!=(ucVal&0x03)) + { + GameSettingsA[iPad]->usBitmaskValues&=~0x03; + GameSettingsA[iPad]->usBitmaskValues|=ucVal&0x03; + if(iPad==ProfileManager.GetPrimaryPad()) + { + ActionGameSettings(iPad,eVal); + } + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_Sensitivity_InGame: + if(GameSettingsA[iPad]->ucSensitivity!=ucVal) + { + GameSettingsA[iPad]->ucSensitivity=ucVal; + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_ViewBob: + if((GameSettingsA[iPad]->usBitmaskValues&0x0004)!=((ucVal&0x01)<<2)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0004; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0004; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_ControlScheme: // bits 5 and 6 + if((GameSettingsA[iPad]->usBitmaskValues&0x30)!=((ucVal&0x03)<<4)) + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0030; + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=(ucVal&0x03)<<4; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + + case eGameSetting_ControlInvertLook: + if((GameSettingsA[iPad]->usBitmaskValues&0x0040)!=((ucVal&0x01)<<6)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0040; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0040; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + + case eGameSetting_ControlSouthPaw: + if((GameSettingsA[iPad]->usBitmaskValues&0x0080)!=((ucVal&0x01)<<7)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0080; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0080; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_SplitScreenVertical: + if((GameSettingsA[iPad]->usBitmaskValues&0x0100)!=((ucVal&0x01)<<8)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0100; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0100; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_GamertagsVisible: + if((GameSettingsA[iPad]->usBitmaskValues&0x0008)!=((ucVal&0x01)<<3)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0008; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0008; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + + // 4J-PB - Added for Interim TU for 1.6.6 + case eGameSetting_Sensitivity_InMenu: + if(GameSettingsA[iPad]->ucMenuSensitivity!=ucVal) + { + GameSettingsA[iPad]->ucMenuSensitivity=ucVal; + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_DisplaySplitscreenGamertags: + if((GameSettingsA[iPad]->usBitmaskValues&0x0200)!=((ucVal&0x01)<<9)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0200; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0200; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_Hints: + if((GameSettingsA[iPad]->usBitmaskValues&0x0400)!=((ucVal&0x01)<<10)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x0400; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x0400; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_Autosave: + if((GameSettingsA[iPad]->usBitmaskValues&0x7800)!=((ucVal&0x0F)<<11)) + { + GameSettingsA[iPad]->usBitmaskValues&=~0x7800; + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=(ucVal&0x0F)<<11; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + + case eGameSetting_Tooltips: + if((GameSettingsA[iPad]->usBitmaskValues&0x8000)!=((ucVal&0x01)<<15)) + { + if(ucVal!=0) + { + GameSettingsA[iPad]->usBitmaskValues|=0x8000; + } + else + { + GameSettingsA[iPad]->usBitmaskValues&=~0x8000; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_InterfaceOpacity: + if(GameSettingsA[iPad]->ucInterfaceOpacity!=ucVal) + { + GameSettingsA[iPad]->ucInterfaceOpacity=ucVal; + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_Clouds: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_CLOUDS)!=(ucVal&0x01)) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_CLOUDS; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_CLOUDS; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + + case eGameSetting_Online: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_ONLINE)!=(ucVal&0x01)<<1) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_ONLINE; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_ONLINE; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_InviteOnly: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_INVITEONLY)!=(ucVal&0x01)<<2) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_INVITEONLY; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_INVITEONLY; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_FriendsOfFriends: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_FRIENDSOFFRIENDS)!=(ucVal&0x01)<<3) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_FRIENDSOFFRIENDS; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_FRIENDSOFFRIENDS; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_DisplayUpdateMessage: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYUPDATEMSG)!=(ucVal&0x03)<<4) + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_DISPLAYUPDATEMSG; + if(ucVal>0) + { + GameSettingsA[iPad]->uiBitmaskValues|=(ucVal&0x03)<<4; + } + + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + + case eGameSetting_BedrockFog: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_BEDROCKFOG)!=(ucVal&0x01)<<6) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_BEDROCKFOG; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_DisplayHUD: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYHUD)!=(ucVal&0x01)<<7) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_DISPLAYHUD; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + case eGameSetting_DisplayHand: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYHAND)!=(ucVal&0x01)<<8) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_DISPLAYHAND; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + + case eGameSetting_CustomSkinAnim: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_CUSTOMSKINANIM)!=(ucVal&0x01)<<9) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_CUSTOMSKINANIM; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + + break; + // TU9 + case eGameSetting_DeathMessages: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DEATHMESSAGES)!=(ucVal&0x01)<<10) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_DEATHMESSAGES; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_UISize: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_UISIZE)!=((ucVal&0x03)<<11)) + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_UISIZE; + if(ucVal!=0) + { + GameSettingsA[iPad]->uiBitmaskValues|=(ucVal&0x03)<<11; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_UISizeSplitscreen: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_UISIZE_SPLITSCREEN)!=((ucVal&0x03)<<13)) + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_UISIZE_SPLITSCREEN; + if(ucVal!=0) + { + GameSettingsA[iPad]->uiBitmaskValues|=(ucVal&0x03)<<13; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_AnimatedCharacter: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_ANIMATEDCHARACTER)!=(ucVal&0x01)<<15) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_ANIMATEDCHARACTER; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_PS3_EULA_Read: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_PS3EULAREAD)!=(ucVal&0x01)<<16) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_PS3EULAREAD; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_PS3EULAREAD; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + case eGameSetting_PSVita_NetworkModeAdhoc: + if((GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_PSVITANETWORKMODEADHOC)!=(ucVal&0x01)<<17) + { + if(ucVal==1) + { + GameSettingsA[iPad]->uiBitmaskValues|=GAMESETTING_PSVITANETWORKMODEADHOC; + } + else + { + GameSettingsA[iPad]->uiBitmaskValues&=~GAMESETTING_PSVITANETWORKMODEADHOC; + } + ActionGameSettings(iPad,eVal); + GameSettingsA[iPad]->bSettingsChanged=true; + } + break; + + } +} + +unsigned char CMinecraftApp::GetGameSettings(eGameSetting eVal) +{ + int iPad=ProfileManager.GetPrimaryPad(); + + return GetGameSettings(iPad,eVal); +} + +unsigned char CMinecraftApp::GetGameSettings(int iPad,eGameSetting eVal) +{ + switch(eVal) + { + case eGameSetting_MusicVolume: + return GameSettingsA[iPad]->ucMusicVolume; + break; + case eGameSetting_SoundFXVolume: + return GameSettingsA[iPad]->ucSoundFXVolume; + break; + case eGameSetting_Gamma: + return GameSettingsA[iPad]->ucGamma; + break; + case eGameSetting_Difficulty: + return GameSettingsA[iPad]->usBitmaskValues&0x0003; + break; + case eGameSetting_Sensitivity_InGame: + return GameSettingsA[iPad]->ucSensitivity; + break; + case eGameSetting_ViewBob: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0004)>>2); + break; + case eGameSetting_GamertagsVisible: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0008)>>3); + break; + case eGameSetting_ControlScheme: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0030)>>4); // 2 bits + break; + case eGameSetting_ControlInvertLook: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0040)>>6); + break; + case eGameSetting_ControlSouthPaw: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0080)>>7); + break; + case eGameSetting_SplitScreenVertical: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0100)>>8); + break; + // 4J-PB - Added for Interim TU for 1.6.6 + case eGameSetting_Sensitivity_InMenu: + return GameSettingsA[iPad]->ucMenuSensitivity; + break; + + case eGameSetting_DisplaySplitscreenGamertags: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0200)>>9); + break; + + case eGameSetting_Hints: + return ((GameSettingsA[iPad]->usBitmaskValues&0x0400)>>10); + break; + case eGameSetting_Autosave: + { + unsigned char ucVal=(GameSettingsA[iPad]->usBitmaskValues&0x7800)>>11; + return ucVal; + } + break; + case eGameSetting_Tooltips: + return ((GameSettingsA[iPad]->usBitmaskValues&0x8000)>>15); + break; + + case eGameSetting_InterfaceOpacity: + return GameSettingsA[iPad]->ucInterfaceOpacity; + break; + + case eGameSetting_Clouds: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_CLOUDS); + break; + case eGameSetting_Online: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_ONLINE)>>1; + break; + case eGameSetting_InviteOnly: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_INVITEONLY)>>2; + break; + case eGameSetting_FriendsOfFriends: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_FRIENDSOFFRIENDS)>>3; + break; + case eGameSetting_DisplayUpdateMessage: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYUPDATEMSG)>>4; + break; + case eGameSetting_BedrockFog: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_BEDROCKFOG)>>6; + break; + case eGameSetting_DisplayHUD: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYHUD)>>7; + break; + case eGameSetting_DisplayHand: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DISPLAYHAND)>>8; + break; + case eGameSetting_CustomSkinAnim: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_CUSTOMSKINANIM)>>9; + break; + // TU9 + case eGameSetting_DeathMessages: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_DEATHMESSAGES)>>10; + break; + case eGameSetting_UISize: + { + unsigned char ucVal=(GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_UISIZE)>>11; + return ucVal; + } + break; + case eGameSetting_UISizeSplitscreen: + { + unsigned char ucVal=(GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_UISIZE_SPLITSCREEN)>>13; + return ucVal; + } + break; + case eGameSetting_AnimatedCharacter: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_ANIMATEDCHARACTER)>>15; + + case eGameSetting_PS3_EULA_Read: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_PS3EULAREAD)>>16; + + case eGameSetting_PSVita_NetworkModeAdhoc: + return (GameSettingsA[iPad]->uiBitmaskValues&GAMESETTING_PSVITANETWORKMODEADHOC)>>17; + + } + return 0; +} + +void CMinecraftApp::CheckGameSettingsChanged(bool bOverride5MinuteTimer, int iPad) +{ + // If the settings have changed, write them to the profile + + if(iPad==XUSER_INDEX_ANY) + { + for(int i=0;ibSettingsChanged) + { +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__ ) + StorageManager.WriteToProfile(i,true, bOverride5MinuteTimer); +#else + ProfileManager.WriteToProfile(i,true, bOverride5MinuteTimer); +#endif + GameSettingsA[i]->bSettingsChanged=false; + } + } + } + else + { + if(GameSettingsA[iPad]->bSettingsChanged) + { +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + StorageManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); +#else + ProfileManager.WriteToProfile(iPad,true, bOverride5MinuteTimer); +#endif + GameSettingsA[iPad]->bSettingsChanged=false; + } + } +} + +void CMinecraftApp::ClearGameSettingsChangedFlag(int iPad) +{ + GameSettingsA[iPad]->bSettingsChanged=false; +} + +/////////////////////////// +// +// Remove the debug settings in the content package build +// +//////////////////////////// +#ifndef _DEBUG_MENUS_ENABLED +unsigned int CMinecraftApp::GetGameSettingsDebugMask(int iPad,bool bOverridePlayer) //bOverridePlayer is to force the send for the server to get the read options +{ + return 0; +} + +void CMinecraftApp::SetGameSettingsDebugMask(int iPad, unsigned int uiVal) +{ +} + +void CMinecraftApp::ActionDebugMask(int iPad,bool bSetAllClear) +{ +} + +#else + +unsigned int CMinecraftApp::GetGameSettingsDebugMask(int iPad,bool bOverridePlayer) //bOverridePlayer is to force the send for the server to get the read options +{ + if(iPad==-1) + { + iPad=ProfileManager.GetPrimaryPad(); + } + if(iPad < 0) iPad = 0; + + shared_ptr player = Minecraft::GetInstance()->localplayers[iPad]; + + if(bOverridePlayer || player==NULL) + { + return GameSettingsA[iPad]->uiDebugBitmask; + } + else + { + return player->GetDebugOptions(); + } +} + + +void CMinecraftApp::SetGameSettingsDebugMask(int iPad, unsigned int uiVal) +{ +#ifndef _CONTENT_PACKAGE + GameSettingsA[iPad]->bSettingsChanged=true; + GameSettingsA[iPad]->uiDebugBitmask=uiVal; + + // update the value so the network server can use it + shared_ptr player = Minecraft::GetInstance()->localplayers[iPad]; + + if(player) + { + Minecraft::GetInstance()->localgameModes[iPad]->handleDebugOptions(uiVal,player); + } +#endif +} + +void CMinecraftApp::ActionDebugMask(int iPad,bool bSetAllClear) +{ + unsigned int ulBitmask=app.GetGameSettingsDebugMask(iPad); + + if(bSetAllClear) ulBitmask=0L; + + + + // these settings should only be actioned for the primary player + if(ProfileManager.GetPrimaryPad()!=iPad) return; + + for(int i=0;iiPad, actionInfo->action); +} + + +void CMinecraftApp::HandleXuiActions(void) +{ + eXuiAction eAction; + eTMSAction eTMS; + LPVOID param; + Minecraft *pMinecraft=Minecraft::GetInstance(); + shared_ptr player; + + // are there any global actions to deal with? + eAction = app.GetGlobalXuiAction(); + if(eAction!=eAppAction_Idle) + { + switch(eAction) + { + case eAppAction_DisplayLavaMessage: + // Display a warning about placing lava in the spawn area + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CANT_PLACE_NEAR_SPAWN_TITLE, IDS_CANT_PLACE_NEAR_SPAWN_TEXT, uiIDA,1,XUSER_INDEX_ANY,NULL,NULL, app.GetStringTable()); + if(result != C4JStorage::EMessage_Busy) SetGlobalXuiAction(eAppAction_Idle); + + } + break; + default: + break; + } + } + + // are there any app actions to deal with? + for(int i=0;iIsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() ) + { + // disable character name tags for the shot + //m_bwasHidingGui = pMinecraft->options->hideGui; // 4J Stu - Removed 1.8.2 bug fix (TU6) as don't need this + pMinecraft->options->hideGui = true; + + SetAction(i,eAppAction_SocialPostScreenshot); + } + else + { + SetAction(i,eAppAction_Idle); + } + } + else + { + SetAction(i,eAppAction_Idle); + } + break; + case eAppAction_SocialPostScreenshot: + { + SetAction(i,eAppAction_Idle); + bool bKeepHiding = false; + for(int j=0; j < XUSER_MAX_COUNT;++j) + { + if(app.GetXuiAction(j) == eAppAction_SocialPostScreenshot) + { + bKeepHiding = true; + break; + } + } + pMinecraft->options->hideGui=bKeepHiding; + + // Facebook Share + + if(app.GetLocalPlayerCount()>1) + { + ui.NavigateToScene(i,eUIScene_SocialPost); + } + else + { + ui.NavigateToScene(i,eUIScene_SocialPost); + } + } + break; + case eAppAction_SaveGame: + SetAction(i,eAppAction_Idle); + if(!GetChangingSessionType()) + { + // If this is the trial game, do an upsell + if(ProfileManager.IsFullVersion()) + { + + // flag the render to capture the screenshot for the save + SetAction(i,eAppAction_SaveGameCapturedThumbnail); + } + else + { + // ask the player if they would like to upgrade, or they'll lose the level + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_UNLOCK_TITLE, IDS_UNLOCK_TOSAVE_TEXT, uiIDA, 2,i,&CMinecraftApp::UnlockFullSaveReturned,this,app.GetStringTable()); + } + } + + break; + case eAppAction_AutosaveSaveGame: + { + // Need to run a check to see if the save exists in order to stop the dialog asking if we want to overwrite it coming up on an autosave + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + + SetAction(i,eAppAction_Idle); + if(!GetChangingSessionType()) + { + + // flag the render to capture the screenshot for the save + SetAction(i,eAppAction_AutosaveSaveGameCapturedThumbnail); + } + } + + break; + + case eAppAction_SaveGameCapturedThumbnail: + // reset the autosave timer + app.SetAutosaveTimerTime(); + SetAction(i,eAppAction_Idle); + // Check that there is a name for the save - if we're saving from the tutorial and this is the first save from the tutorial, we'll not have a name + /*if(StorageManager.GetSaveName()==NULL) + { + app.NavigateToScene(i,eUIScene_SaveWorld); + } + else*/ + { + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // Hide the other players scenes + ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false); + + //INT saveOrCheckpointId = 0; + //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_PauseMenu::SaveWorldThreadProc; + loadingParams->lpParam = (LPVOID)false; + + // 4J-JEV - PS4: Fix for #5708 - [ONLINE] - If the user pulls their network cable out while saving the title will hang. + loadingParams->waitForThreadToDelete = true; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->iPad = ProfileManager.GetPrimaryPad(); + + if( ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem ) ) + { + completionData->scene = eUIScene_EndPoem; + } + else + { + completionData->scene = eUIScene_PauseMenu; + } + + loadingParams->completionData = completionData; + + // 4J Stu - Xbox only +#ifdef _XBOX + // Temporarily make this scene fullscreen + CXuiSceneBase::SetPlayerBaseScenePosition( ProfileManager.GetPrimaryPad(), CXuiSceneBase::e_BaseScene_Fullscreen ); +#endif + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams , eUILayer_Fullscreen, eUIGroup_Fullscreen); + + } + break; + case eAppAction_AutosaveSaveGameCapturedThumbnail: + + { + app.SetAutosaveTimerTime(); + SetAction(i,eAppAction_Idle); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_AutoSaveGame); + + if(app.GetGameHostOption(eGameHostOption_DisableSaving)) StorageManager.SetSaveDisabled(true); +#else + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + //app.CloseAllPlayersXuiScenes(); + // Hide the other players scenes + ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false); + + // This just allows it to be shown + if(pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false); + + //INT saveOrCheckpointId = 0; + //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId); + + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_PauseMenu::SaveWorldThreadProc; + + loadingParams->lpParam = (LPVOID)true; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_AutosaveNavigateBack; + completionData->iPad = ProfileManager.GetPrimaryPad(); + //completionData->bAutosaveWasMenuDisplayed=ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad()); + loadingParams->completionData = completionData; + + // 4J Stu - Xbox only +#ifdef _XBOX + // Temporarily make this scene fullscreen + CXuiSceneBase::SetPlayerBaseScenePosition( ProfileManager.GetPrimaryPad(), CXuiSceneBase::e_BaseScene_Fullscreen ); +#endif + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams , eUILayer_Fullscreen, eUIGroup_Fullscreen); +#endif + } + break; + case eAppAction_ExitPlayer: + // a secondary player has chosen to quit + { + int iPlayerC=g_NetworkManager.GetPlayerCount(); + + // Since the player is exiting, let's flush any profile writes for them, and hope we're not breaking TCR 136... +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + StorageManager.ForceQueuedProfileWrites(i); + LeaderboardManager::Instance()->OpenSession(); + for (int j = 0; j < XUSER_MAX_COUNT; j++) + { + if( ProfileManager.IsSignedIn(j) ) + { + app.DebugPrintf("Stats save for an offline game for the player at index %d\n", 0); + Minecraft::GetInstance()->forceStatsSave(j); + } + } + LeaderboardManager::Instance()->CloseSession(); +#else + ProfileManager.ForceQueuedProfileWrites(i); +#endif + + // not required - it's done within the removeLocalPlayerIdx + // if(pMinecraft->level->isClientSide) + // { + // // we need to remove the qnetplayer, or this player won't be able to get back into the game until qnet times out and removes them + // g_NetworkManager.NotifyPlayerLeaving(g_NetworkManager.GetLocalPlayerByUserIndex(i)); + // } + + // if there are any tips showing, we need to close them + + pMinecraft->gui->clearMessages(i); + + // Make sure we've not got this player selected as current - this shouldn't be the case anyway + pMinecraft->setLocalPlayerIdx(ProfileManager.GetPrimaryPad()); + pMinecraft->removeLocalPlayerIdx(i); + +#ifdef _XBOX + // tell the xui scenes a splitscreen player left - has to come after removeLocalPlayerIdx which calls updatePlayerViewportAssignments + XUIMessage xuiMsg; + CustomMessage_Splitscreenplayer_Struct myMsgData; + CustomMessage_Splitscreenplayer( &xuiMsg, &myMsgData, false); + + // send the message + for(int idx=0;idxlocalplayers[idx]!=NULL)) + { + XuiBroadcastMessage( CXuiSceneBase::GetPlayerBaseScene(idx), &xuiMsg ); + } + } +#endif + +#ifndef _XBOX + // Wipe out the tooltips + ui.SetTooltips(i, -1); +#endif + + // Change the presence info + // Are we offline or online, and how many players are there + if(iPlayerC>2) // one player is about to leave here - they'll be set to idle in the qnet manager player leave + { + for(int iPlayer=0;iPlayerlocalplayers[iPlayer]) + { + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + } + } + } + else + { + for(int iPlayer=0;iPlayerlocalplayers[iPlayer]) + { + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + } + } + } + } + +#ifdef _DURANGO + ProfileManager.RemoveGamepadFromGame(i); +#endif + + SetAction(i,eAppAction_Idle); + } + break; + case eAppAction_ExitPlayerPreLogin: + { + int iPlayerC=g_NetworkManager.GetPlayerCount(); + // Since the player is exiting, let's flush any profile writes for them, and hope we're not breaking TCR 136... +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + StorageManager.ForceQueuedProfileWrites(i); +#else + ProfileManager.ForceQueuedProfileWrites(i); +#endif + // if there are any tips showing, we need to close them + + pMinecraft->gui->clearMessages(i); + + // Make sure we've not got this player selected as current - this shouldn't be the case anyway + pMinecraft->setLocalPlayerIdx(ProfileManager.GetPrimaryPad()); + pMinecraft->removeLocalPlayerIdx(i); + +#ifdef _XBOX + // tell the xui scenes a splitscreen player left - has to come after removeLocalPlayerIdx which calls updatePlayerViewportAssignments + XUIMessage xuiMsg; + CustomMessage_Splitscreenplayer_Struct myMsgData; + CustomMessage_Splitscreenplayer( &xuiMsg, &myMsgData, false); + + // send the message + for(int idx=0;idxlocalplayers[idx]!=NULL)) + { + XuiBroadcastMessage( CXuiSceneBase::GetPlayerBaseScene(idx), &xuiMsg ); + } + } +#endif + +#ifndef _XBOX + // Wipe out the tooltips + ui.SetTooltips(i, -1); +#endif + + // Change the presence info + // Are we offline or online, and how many players are there + if(iPlayerC>2) // one player is about to leave here - they'll be set to idle in the qnet manager player leave + { + for(int iPlayer=0;iPlayerlocalplayers[iPlayer]) + { + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + } + } + } + else + { + for(int iPlayer=0;iPlayerlocalplayers[iPlayer]) + { + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(iPlayer,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + } + } + } + } + SetAction(i,eAppAction_Idle); + } + break; + +#ifdef __ORBIS__ + case eAppAction_OptionsSaveNoSpace: + { + SetAction(i,eAppAction_Idle); + + SceSaveDataDialogParam param; + SceSaveDataDialogSystemMessageParam sysParam; + SceSaveDataDialogItems items; + SceSaveDataDirName dirName; + + sceSaveDataDialogParamInitialize(¶m); + param.mode = SCE_SAVE_DATA_DIALOG_MODE_SYSTEM_MSG; + param.dispType = SCE_SAVE_DATA_DIALOG_TYPE_SAVE; + memset(&sysParam,0,sizeof(sysParam)); + param.sysMsgParam = &sysParam; + param.sysMsgParam->sysMsgType = SCE_SAVE_DATA_DIALOG_SYSMSG_TYPE_NOSPACE_CONTINUABLE; + param.sysMsgParam->value = app.GetOptionsBlocksRequired(i); + memset(&items, 0, sizeof(items)); + param.items = &items; + param.items->userId = ProfileManager.getUserID(i); + + int ret = sceSaveDataDialogInitialize(); + ret = sceSaveDataDialogOpen(¶m); + + app.SetOptionsSaveDataDialogRunning(true);//m_bOptionsSaveDataDialogRunning = true; + //pClass->m_eSaveIncompleteType = saveIncompleteType; + + //StorageManager.SetSaveDisabled(true); + //pClass->EnterSaveNotificationSection(); + + } + break; +#endif + + case eAppAction_ExitWorld: + + SetAction(i,eAppAction_Idle); + + // If we're already leaving don't exit + if (g_NetworkManager.IsLeavingGame()) + { + break; + } + + pMinecraft->gui->clearMessages(); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // reset the flag stopping new dlc message being shown if you've seen the message before + DisplayNewDLCTipAgain(); + + // clear the autosave timer that might be on screen + ui.ShowAutosaveCountdownTimer(false); + + // Hide the selected item text + ui.HideAllGameUIElements(); + + // Since the player forced the exit, let's flush any profile writes, and hope we're not breaking TCR 136... +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + StorageManager.ForceQueuedProfileWrites(); + LeaderboardManager::Instance()->OpenSession(); + for (int j = 0; j < XUSER_MAX_COUNT; j++) + { + if( ProfileManager.IsSignedIn(j) ) + { + app.DebugPrintf("Stats save for an offline game for the player at index %d\n", 0); + Minecraft::GetInstance()->forceStatsSave(j); + } + } + LeaderboardManager::Instance()->CloseSession(); +#elif (defined _XBOX) + ProfileManager.ForceQueuedProfileWrites(); +#endif + + // 4J-PB - cancel any possible string verifications queued with LIVE + //InputManager.CancelAllVerifyInProgress(); + + if(ProfileManager.IsFullVersion()) + { + + // In a split screen, only the primary player actually quits the game, others just remove their players + if( i != ProfileManager.GetPrimaryPad() ) + { + // Make sure we've not got this player selected as current - this shouldn't be the case anyway + pMinecraft->setLocalPlayerIdx(ProfileManager.GetPrimaryPad()); + pMinecraft->removeLocalPlayerIdx(i); + +#ifdef _DURANGO + ProfileManager.RemoveGamepadFromGame(i); +#endif + SetAction(i,eAppAction_Idle); + return; + } + // flag to capture the save thumbnail + SetAction(i,eAppAction_ExitWorldCapturedThumbnail, param); + } + else + { + // ask the player if they would like to upgrade, or they'll lose the level + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_UNLOCK_TITLE, IDS_UNLOCK_TOSAVE_TEXT, uiIDA, 2, i,&CMinecraftApp::UnlockFullExitReturned,this,app.GetStringTable()); + } + + // Change the presence info + // Are we offline or online, and how many players are there + + if(g_NetworkManager.GetPlayerCount()>1) + { + for(int j=0;jlocalplayers[j]) + { + if(g_NetworkManager.IsLocalGame()) + { + app.SetRichPresenceContext(j,CONTEXT_GAME_STATE_BLANK); + ProfileManager.SetCurrentGameActivity(j,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + } + else + { + app.SetRichPresenceContext(j,CONTEXT_GAME_STATE_BLANK); + ProfileManager.SetCurrentGameActivity(j,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + TelemetryManager->RecordLevelExit(j, eSen_LevelExitStatus_Exited); + } + } + } + else + { + app.SetRichPresenceContext(i,CONTEXT_GAME_STATE_BLANK); + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(i,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(i,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + } + TelemetryManager->RecordLevelExit(i, eSen_LevelExitStatus_Exited); + } + break; + case eAppAction_ExitWorldCapturedThumbnail: + { + SetAction(i,eAppAction_Idle); + // Stop app running + SetGameStarted(false); + SetChangingSessionType(true); // Added to stop handling ethernet disconnects + + ui.CloseAllPlayersScenes(); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(idx,true); +#endif + + // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial + // It doesn't matter if they were in the tutorial already + pMinecraft->playerLeftTutorial( idx ); + } + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_PauseMenu::ExitWorldThreadProc; + loadingParams->lpParam = param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + // If param is non-null then this is a forced exit by the server, so make sure the player knows why + // 4J Stu - Changed - Don't use the FullScreenProgressScreen for action, use a dialog instead + completionData->bRequiresUserAction = FALSE;//(param != NULL) ? TRUE : FALSE; + completionData->bShowTips = (param != NULL) ? FALSE : TRUE; + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateToHomeMenu; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + break; + case eAppAction_ExitWorldTrial: + { + SetAction(i,eAppAction_Idle); + + pMinecraft->gui->clearMessages(); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // Stop app running + SetGameStarted(false); + + ui.CloseAllPlayersScenes(); + + // 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(idx,true); +#endif + + // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial + // It doesn't matter if they were in the tutorial already + pMinecraft->playerLeftTutorial( idx ); + } + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_PauseMenu::ExitWorldThreadProc; + loadingParams->lpParam = param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateToHomeMenu; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + + break; + case eAppAction_ExitTrial: + //XLaunchNewImage(XLAUNCH_KEYWORD_DASH_ARCADE, 0); + ExitGame(); + break; + + case eAppAction_Respawn: + { + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = i; + param->stringId = IDS_PROGRESS_RESPAWNING; + param->showTooltips = false; + param->setFailTimer = false; + ui.NavigateToScene(i,eUIScene_ConnectingProgress, param); + + // Need to reset this incase the player has already died and respawned + pMinecraft->localplayers[i]->SetPlayerRespawned(false); + + SetAction(i,eAppAction_WaitForRespawnComplete); + if( app.GetLocalPlayerCount()>1 ) + { + // In split screen mode, we don't want to do any async loading or flushing of the cache, just a simple respawn + pMinecraft->localplayers[i]->respawn(); + + // If the respawn requires a dimension change then the action will have changed + //if(app.GetXuiAction(i) == eAppAction_Respawn) + //{ + // SetAction(i,eAppAction_Idle); + // CloseXuiScenes(i); + //} + } + else + { + //SetAction(i,eAppAction_WaitForRespawnComplete); + + //LoadingInputParams *loadingParams = new LoadingInputParams(); + //loadingParams->func = &CScene_Death::RespawnThreadProc; + //loadingParams->lpParam = (LPVOID)i; + + // Disable game & update thread whilst we do any of this + //app.SetGameStarted(false); + pMinecraft->gameRenderer->DisableUpdateThread(); + + // 4J Stu - We don't need this on a thread in multiplayer as respawning is asynchronous. + pMinecraft->localplayers[i]->respawn(); + + //app.SetGameStarted(true); + pMinecraft->gameRenderer->EnableUpdateThread(); + + //UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + //completionData->bShowBackground=TRUE; + //completionData->bShowLogo=TRUE; + //completionData->type = e_ProgressCompletion_CloseUIScenes; + //completionData->iPad = i; + //loadingParams->completionData = completionData; + + //app.NavigateToScene(i,eUIScene_FullscreenProgress, loadingParams, true); + } + } + break; + case eAppAction_WaitForRespawnComplete: + player = pMinecraft->localplayers[i]; + if(player != NULL && player->GetPlayerRespawned()) + { + SetAction(i,eAppAction_Idle); + + if(ui.IsSceneInStack(i, eUIScene_EndPoem)) + { + ui.NavigateBack(i,false,eUIScene_EndPoem); + } + else + { + ui.CloseUIScenes(i); + } + + // clear the progress messages + +// pMinecraft->progressRenderer->progressStart(-1); +// pMinecraft->progressRenderer->progressStage(-1); + } + else if(!g_NetworkManager.IsInGameplay()) + { + SetAction(i,eAppAction_Idle); + } + break; + case eAppAction_WaitForDimensionChangeComplete: + player = pMinecraft->localplayers[i]; + if(player != NULL && player->connection && player->connection->isStarted()) + { + SetAction(i,eAppAction_Idle); + ui.CloseUIScenes(i); + } + else if(!g_NetworkManager.IsInGameplay()) + { + SetAction(i,eAppAction_Idle); + } + break; + case eAppAction_PrimaryPlayerSignedOut: + { + //SetAction(i,eAppAction_Idle); + + // clear the autosavetimer that might be displayed + ui.ShowAutosaveCountdownTimer(false); + + // If the player signs out before the game started the server can be killed a bit earlier to stop + // the loading or saving of a new game continuing running while the UI/Guide is up + if(!app.GetGameStarted()) MinecraftServer::HaltServer(true); + + // inform the player they are being returned to the menus because they signed out + StorageManager.SetSaveDeviceSelected(i,false); + // need to clear the player stats - can't assume it'll be done in setlevel - we may not be in the game + StatsCounter* pStats = Minecraft::GetInstance()->stats[ i ]; + pStats->clear(); + + // 4J-PB - the libs will display the Returned to Title screen +// UINT uiIDA[1]; +// uiIDA[0]=IDS_CONFIRM_OK; +// +// ui.RequestMessageBox(IDS_RETURNEDTOMENU_TITLE, IDS_RETURNEDTOTITLESCREEN_TEXT, uiIDA, 1, i,&CMinecraftApp::PrimaryPlayerSignedOutReturned,this,app.GetStringTable()); + if( g_NetworkManager.IsInSession() ) + { + app.SetAction(i,eAppAction_PrimaryPlayerSignedOutReturned); + } + else + { + app.SetAction(i,eAppAction_PrimaryPlayerSignedOutReturned_Menus); + MinecraftServer::resetFlags(); + } + } + break; + case eAppAction_EthernetDisconnected: + { + app.DebugPrintf("Handling eAppAction_EthernetDisconnected\n"); + SetAction(i,eAppAction_Idle); + + // 4J Stu - Fix for #12530 -TCR 001 BAS Game Stability: Title will crash if the player disconnects while starting a new world and then opts to play the tutorial once they have been returned to the Main Menu. + if(!g_NetworkManager.IsLeavingGame()) + { + app.DebugPrintf("Handling eAppAction_EthernetDisconnected - Not leaving game\n"); + // 4J-PB - not the same as a signout. We should only leave the game if this machine is not the host. We shouldn't get rid of the save device either. + if( g_NetworkManager.IsHost() ) + { + app.DebugPrintf("Handling eAppAction_EthernetDisconnected - Is Host\n"); + // If it's already a local game, then an ethernet disconnect should have no effect + if( !g_NetworkManager.IsLocalGame() && g_NetworkManager.IsInGameplay() ) + { + // Change the session to an offline session + SetAction(i,eAppAction_ChangeSessionType); + } + else if(!g_NetworkManager.IsLocalGame() && !g_NetworkManager.IsInGameplay() ) + { + // There are two cases here, either: + // 1. We're early enough in the create/load game that we can do a really minimal shutdown or + // 2. We're far enough in (game has started but the actual game started flag hasn't been set) that we should just wait until we're in the game and switch to offline mode + + // If there's a non-null level then, for our purposes, the game has started + bool gameStarted = false; + for(int i = 0; i < pMinecraft->levels.length; i++) + { + if (pMinecraft->levels.data[i] != nullptr) + { + gameStarted = true; + break; + } + } + + if (!gameStarted) + { + // 1. Exit + MinecraftServer::HaltServer(); + + // Fix for #12530 - TCR 001 BAS Game Stability: Title will crash if the player disconnects while starting a new world and then opts to play the tutorial once they have been returned to the Main Menu. + // 4J Stu - Leave the session + g_NetworkManager.LeaveGame(FALSE); + + // need to clear the player stats - can't assume it'll be done in setlevel - we may not be in the game + StatsCounter* pStats = Minecraft::GetInstance()->stats[ i ]; + pStats->clear(); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST), g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE), uiIDA, 1, i,&CMinecraftApp::EthernetDisconnectReturned,this, app.GetStringTable()); + } + else + { + // 2. Switch to offline + SetAction(i,eAppAction_ChangeSessionType); + } + } + } + else + { + app.DebugPrintf("Handling eAppAction_EthernetDisconnected - Not host\n"); + // need to clear the player stats - can't assume it'll be done in setlevel - we may not be in the game + StatsCounter* pStats = Minecraft::GetInstance()->stats[ i ]; + pStats->clear(); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST), g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE), uiIDA, 1, i,&CMinecraftApp::EthernetDisconnectReturned,this, app.GetStringTable()); + } + } + } + break; + // We currently handle both these returns the same way. + case eAppAction_EthernetDisconnectedReturned: + case eAppAction_PrimaryPlayerSignedOutReturned: + { + SetAction(i,eAppAction_Idle); + + pMinecraft->gui->clearMessages(); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // set the state back to pre-game + ProfileManager.ResetProfileProcessState(); + + + if( g_NetworkManager.IsLeavingGame() ) + { + // 4J Stu - If we are already leaving the game, then we just need to signal that the player signed out to stop saves + pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME ); + pMinecraft->progressRenderer->progressStage(-1); + // This has no effect on client machines + MinecraftServer::HaltServer(true); + } + else + { + // Stop app running + SetGameStarted(false); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + ui.CloseAllPlayersScenes(); + + // 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(idx,true); +#endif + + // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial + // It doesn't matter if they were in the tutorial already + pMinecraft->playerLeftTutorial( idx ); + } + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CMinecraftApp::SignoutExitWorldThreadProc; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->iPad=DEFAULT_XUI_MENU_USER; + completionData->type = e_ProgressCompletion_NavigateToHomeMenu; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + } + break; + case eAppAction_PrimaryPlayerSignedOutReturned_Menus: + SetAction(i,eAppAction_Idle); + // set the state back to pre-game + ProfileManager.ResetProfileProcessState(); + // clear the save device + StorageManager.SetSaveDeviceSelected(i,false); + + ui.UpdatePlayerBasePositions(); + // there are multiple layers in the help menu, so a navigate back isn't enough + ui.NavigateToHomeMenu(); + + break; + case eAppAction_EthernetDisconnectedReturned_Menus: + SetAction(i,eAppAction_Idle); + // set the state back to pre-game + ProfileManager.ResetProfileProcessState(); + + ui.UpdatePlayerBasePositions(); + + // there are multiple layers in the help menu, so a navigate back isn't enough + ui.NavigateToHomeMenu(); + + break; + + case eAppAction_TrialOver: + { + SetAction(i,eAppAction_Idle); + UINT uiIDA[2]; + uiIDA[0]=IDS_UNLOCK_TITLE; + uiIDA[1]=IDS_EXIT_GAME; + + ui.RequestMessageBox(IDS_TRIALOVER_TITLE, IDS_TRIALOVER_TEXT, uiIDA, 2, i,&CMinecraftApp::TrialOverReturned,this,app.GetStringTable()); + } + break; + + // INVITES + case eAppAction_DashboardTrialJoinFromInvite: + { + TelemetryManager->RecordUpsellPresented(i, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + + SetAction(i,eAppAction_Idle); + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_UNLOCK_TITLE, IDS_UNLOCK_ACCEPT_INVITE, uiIDA, 2, i,&CMinecraftApp::UnlockFullInviteReturned,this,app.GetStringTable()); + } + break; + case eAppAction_ExitAndJoinFromInvite: + { + UINT uiIDA[3]; + + SetAction(i,eAppAction_Idle); + // Check the player really wants to do this + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Show save option is saves ARE disabled + if(ProfileManager.IsFullVersion() && StorageManager.GetSaveDisabled() && i==ProfileManager.GetPrimaryPad() && g_NetworkManager.IsHost() && GetGameStarted() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_LEAVE_VIA_INVITE, uiIDA, 3, i,&CMinecraftApp::ExitAndJoinFromInviteSaveDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else +#else + if(ProfileManager.IsFullVersion() && !StorageManager.GetSaveDisabled() && i==ProfileManager.GetPrimaryPad() && g_NetworkManager.IsHost() && GetGameStarted() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_LEAVE_VIA_INVITE, uiIDA, 3, i,&CMinecraftApp::ExitAndJoinFromInviteSaveDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else +#endif + { + if(!ProfileManager.IsFullVersion()) + { + TelemetryManager->RecordUpsellPresented(i, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + + // upsell + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_UNLOCK_TITLE, IDS_UNLOCK_ACCEPT_INVITE, uiIDA, 2, i,&CMinecraftApp::UnlockFullInviteReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_LEAVE_VIA_INVITE, uiIDA, 2,i,&CMinecraftApp::ExitAndJoinFromInvite,this,app.GetStringTable(), 0, 0, false); + } + } + } + break; + case eAppAction_ExitAndJoinFromInviteConfirmed: + { + SetAction(i,eAppAction_Idle); + + pMinecraft->gui->clearMessages(); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + // Stop app running + SetGameStarted(false); + + ui.CloseAllPlayersScenes(); + + // 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(idx,true); +#endif + + // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial + // It doesn't matter if they were in the tutorial already + pMinecraft->playerLeftTutorial( idx ); + } + + // 4J-PB - may have been using a texture pack with audio , so clean up anything texture pack related here + + // unload any texture pack audio + // if there is audio in use, clear out the audio, and unmount the pack + TexturePack *pTexPack=Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=NULL; + + if(pTexPack->hasAudio()) + { + // get the dlc texture pack, and store it + pDLCTexPack=(DLCTexturePack *)pTexPack; + } + + // change to the default texture pack + pMinecraft->skins->selectTexturePackById(TexturePackRepository::DEFAULT_TEXTURE_PACK_ID); + + if(pTexPack->hasAudio()) + { + // need to stop the streaming audio - by playing streaming audio from the default texture pack now + // reset the streaming sounds back to the normal ones +#ifndef _XBOX + pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3, + eStream_Nether1,eStream_Nether4, + eStream_end_dragon,eStream_end_end, + eStream_CD_1); +#endif + pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); + +#ifdef _XBOX + if(pDLCTexPack->m_pStreamedWaveBank!=NULL) + { + pDLCTexPack->m_pStreamedWaveBank->Destroy(); + } + if(pDLCTexPack->m_pSoundBank!=NULL) + { + pDLCTexPack->m_pSoundBank->Destroy(); + } +#endif +#ifdef _DURANGO + DWORD result = StorageManager.UnmountInstalledDLC(L"TPACK"); +#else + DWORD result = StorageManager.UnmountInstalledDLC("TPACK"); +#endif + app.DebugPrintf("Unmount result is %d\n",result); + } + +#ifdef _XBOX_ONE + // 4J Stu - It's possible that we can sign in/remove players between the mask initially being set and this point + m_InviteData.dwLocalUsersMask = 0; + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + if(index==i || pMinecraft->localplayers[index]!=NULL ) + { + m_InviteData.dwLocalUsersMask |= g_NetworkManager.GetLocalPlayerMask( index ); + } + } + } +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::ExitAndJoinFromInviteThreadProc; + loadingParams->lpParam = (LPVOID)&m_InviteData; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->iPad=DEFAULT_XUI_MENU_USER; + completionData->type = e_ProgressCompletion_NoAction; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + + break; + case eAppAction_JoinFromInvite: + { + SetAction(i,eAppAction_Idle); + + // 4J Stu - Move this state block from CPlatformNetworkManager::ExitAndJoinFromInviteThreadProc, as g_NetworkManager.JoinGameFromInviteInfo ultimately can call NavigateToScene, + /// and we should only be calling that from the main thread + app.SetTutorialMode( false ); + + g_NetworkManager.SetLocalGame(false); + + JoinFromInviteData *inviteData = (JoinFromInviteData *)param; + // 4J-PB - clear any previous connection errors + Minecraft::GetInstance()->clearConnectionFailed(); + + app.DebugPrintf( "Changing Primary Pad on an invite accept - pad was %d, and is now %d\n", ProfileManager.GetPrimaryPad(), inviteData->dwUserIndex ); + ProfileManager.SetLockedProfile(inviteData->dwUserIndex); + ProfileManager.SetPrimaryPad(inviteData->dwUserIndex); + +#ifdef _XBOX_ONE + // 4J Stu - If a player is signed in (i.e. locked) but not in the mask, unlock them + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if( index != inviteData->dwUserIndex && ProfileManager.IsSignedIn(index) ) + { + if( (m_InviteData.dwLocalUsersMask & g_NetworkManager.GetLocalPlayerMask( index ) ) == 0 ) + { + ProfileManager.RemoveGamepadFromGame(index); + } + } + } +#endif + + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + bool success = g_NetworkManager.JoinGameFromInviteInfo( + inviteData->dwUserIndex, // dwUserIndex + inviteData->dwLocalUsersMask, // dwUserMask + inviteData->pInviteInfo ); // pInviteInfo + + if( !success ) + { + app.DebugPrintf( "Failed joining game from invite\n" ); + //return hr; + + // 4J Stu - Copied this from XUI_FullScreenProgress to properly handle the fail case, as the thread will no longer be failing + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_CONNECTION_LOST_SERVER, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + } + } + break; + case eAppAction_ChangeSessionType: + { + // If we are not in gameplay yet, then wait until the server is setup before changing the session type + if( g_NetworkManager.IsInGameplay() ) + { + // This kicks off a thread that waits for the server to end, then closes the current session, starts a new one and joins the local players into it + + SetAction(i,eAppAction_Idle); + + if( !GetChangingSessionType() && !g_NetworkManager.IsLocalGame() ) + { + SetGameStarted(false); + SetChangingSessionType(true); + SetReallyChangingSessionType(true); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + if( !ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem ) ) + { + ui.CloseAllPlayersScenes(); + } + ui.ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), true); + + // Remove this line to fix: + // #49084 - TU5: Code: Gameplay: The title crashes every time client navigates to 'Play game' menu and loads/creates new game after a "Connection to Xbox LIVE was lost" message has appeared. + //app.NavigateToScene(0,eUIScene_Main); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::ChangeSessionTypeThreadProc; + loadingParams->lpParam = NULL; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); +#ifdef __PS3__ + completionData->bRequiresUserAction=FALSE; +#else + completionData->bRequiresUserAction=TRUE; +#endif + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->iPad=DEFAULT_XUI_MENU_USER; + if( ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem ) ) + { + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->scene = eUIScene_EndPoem; + } + else + { + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + } + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + } + else if( g_NetworkManager.IsLeavingGame() ) + { + // If we are leaving the game, then ignore the state change + SetAction(i,eAppAction_Idle); + } +#if 0 + // 4J-HG - Took this out since ChangeSessionType is only set in two places (both in EthernetDisconnected) and this case is handled there, plus this breaks + // this if statements original purpose (to allow us to wait for IsInGameplay before actioning switching to offline + + // QNet must do this kind of thing automatically by itself, but on PS3 at least, we need the disconnection to definitely end up with us out of the game one way or another, + // and the other two cases above don't catch the case where we are just starting the game and get a disconnection during the loading/creation + else + { + if( g_NetworkManager.IsInSession() ) + { + g_NetworkManager._LeaveGame(); + } + } +#endif + } + break; + case eAppAction_SetDefaultOptions: + SetAction(i,eAppAction_Idle); +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + SetDefaultOptions((C4JStorage::PROFILESETTINGS *)param,i); +#else + SetDefaultOptions((C_4JProfile::PROFILESETTINGS *)param,i); +#endif + + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + CheckGameSettingsChanged(true,i); + + break; + + case eAppAction_RemoteServerSave: + { + // If the remote server save has already finished, don't complete the action + if (GetGameStarted()) + { + SetAction(ProfileManager.GetPrimaryPad(), eAppAction_Idle); + break; + } + + SetAction(i,eAppAction_WaitRemoteServerSaveComplete); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + ui.CloseUIScenes(i, true); + } + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + ui.HideAllGameUIElements(); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CMinecraftApp::RemoteSaveThreadProc; + loadingParams->lpParam = NULL; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bRequiresUserAction=FALSE; + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->iPad=DEFAULT_XUI_MENU_USER; + if( ui.IsSceneInStack( ProfileManager.GetPrimaryPad(), eUIScene_EndPoem ) ) + { + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->scene = eUIScene_EndPoem; + } + else + { + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + } + loadingParams->completionData = completionData; + + loadingParams->cancelFunc = &CMinecraftApp::ExitGameFromRemoteSave; + loadingParams->cancelText = IDS_TOOLTIPS_EXIT; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + break; + case eAppAction_WaitRemoteServerSaveComplete: + // Do nothing + break; + case eAppAction_FailedToJoinNoPrivileges: + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + if(result != C4JStorage::EMessage_Busy) SetAction(i,eAppAction_Idle); + } + break; + case eAppAction_ProfileReadError: + // Return player to the main menu - code largely copied from that for handling + // eAppAction_PrimaryPlayerSignedOut, although I don't think we should have got as + // far as needing to halt the server, or running the game, before returning to the menu + if(!app.GetGameStarted()) MinecraftServer::HaltServer(true); + + if( g_NetworkManager.IsInSession() ) + { + app.SetAction(i,eAppAction_PrimaryPlayerSignedOutReturned); + } + else + { + app.SetAction(i,eAppAction_PrimaryPlayerSignedOutReturned_Menus); + MinecraftServer::resetFlags(); + } + break; + + case eAppAction_BanLevel: + { + // It's possible that this state can get set after the game has been exited (e.g. by network disconnection) so we can't ban the level at that point + if(g_NetworkManager.IsInGameplay() && !g_NetworkManager.IsLeavingGame()) + { + TelemetryManager->RecordBanLevel(i); + +#if defined _XBOX + INetworkPlayer *pHost=g_NetworkManager.GetHostPlayer(); + // write the level to the banned level list, and exit the world + AddLevelToBannedLevelList(i,((NetworkPlayerXbox *)pHost)->GetUID(),GetUniqueMapName(),true); +#elif defined _XBOX_ONE + INetworkPlayer *pHost=g_NetworkManager.GetHostPlayer(); + AddLevelToBannedLevelList(i,pHost->GetUID(),GetUniqueMapName(),true); +#endif + // primary player would exit the world, secondary would exit the player + if(ProfileManager.GetPrimaryPad()==i) + { + SetAction(i,eAppAction_ExitWorld); + } + else + { + SetAction(i,eAppAction_ExitPlayer); + } + } + } + break; + case eAppAction_LevelInBanLevelList: + { + UINT uiIDA[2]; + uiIDA[0]=IDS_BUTTON_REMOVE_FROM_BAN_LIST; + uiIDA[1]=IDS_EXIT_GAME; + + // pass in the gamertag format string + WCHAR wchFormat[40]; + INetworkPlayer *player = g_NetworkManager.GetLocalPlayerByUserIndex(i); + + // If not the primary player, but the primary player has banned this level and decided not to unban + // then we may have left the game by now + if(player) + { + swprintf(wchFormat, 40, L"%ls\n\n%%ls",player->GetOnlineName()); + + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_BANNED_LEVEL_TITLE, IDS_PLAYER_BANNED_LEVEL, uiIDA,2,i,&CMinecraftApp::BannedLevelDialogReturned,this, app.GetStringTable(),wchFormat); + if(result != C4JStorage::EMessage_Busy) SetAction(i,eAppAction_Idle); + } + else + { + SetAction(i,eAppAction_Idle); + } + } + break; + case eAppAction_DebugText: + // launch the xui for text entry + { +#ifdef _XBOX + CScene_TextEntry::XuiTextInputParams *pDebugTextParams= new CScene_TextEntry::XuiTextInputParams; + pDebugTextParams->iPad=i; + pDebugTextParams->wch=(WCHAR)param; + + app.NavigateToScene(i,eUIScene_TextEntry,pDebugTextParams); +#endif + SetAction(i,eAppAction_Idle); + } + break; + + case eAppAction_ReloadTexturePack: + { + SetAction(i,eAppAction_Idle); + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->textures->reloadAll(); + pMinecraft->skins->updateUI(); + + if(!pMinecraft->skins->isUsingDefaultSkin()) + { + TexturePack *pTexturePack = pMinecraft->skins->getSelected(); + + DLCPack *pDLCPack=pTexturePack->getDLCPack(); + + bool purchased = false; + // do we have a license? + if(pDLCPack && pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + purchased = true; + } +#ifdef _XBOX + TelemetryManager->RecordTexturePackLoaded(i, pTexturePack->getId(), purchased?1:0); +#endif + } + + // 4J-PB - If the texture pack has audio, we need to switch to this + if(pMinecraft->skins->getSelected()->hasAudio()) + { + Minecraft::GetInstance()->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); + } + } + + break; + + case eAppAction_TexturePackRequired: + { +#ifdef _XBOX + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(app.GetRequiredTexturePackID(),&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + UINT uiIDA[2]; + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::TexturePackDialogReturned,this,app.GetStringTable()); + SetAction(i,eAppAction_Idle); + } + + break; + } + } + + // Any TMS actions? + + eTMS = app.GetTMSAction(i); + + if(eTMS!=eTMSAction_Idle) + { + switch(eTMS) + { + // TMS++ actions + case eTMSAction_TMSPP_RetrieveFiles_CreateLoad_SignInReturned: + case eTMSAction_TMSPP_RetrieveFiles_RunPlayGame: +#ifdef _XBOX + app.TMSPP_SetTitleGroupID(GROUP_ID); + SetTMSAction(i,eTMSAction_TMSPP_GlobalFileList); +#elif defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_GlobalFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_Title,eTMSAction_TMSPP_UserFileList); +#else + SetTMSAction(i,eTMSAction_TMSPP_UserFileList); + #endif + break; + +#ifdef _XBOX + case eTMSAction_TMSPP_GlobalFileList: + SetTMSAction(i,eTMSAction_TMSPP_GlobalFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_Title,"\\",eTMSAction_TMSPP_UserFileList); + break; +#endif + case eTMSAction_TMSPP_UserFileList: + // retrieve the file list first +#if defined _XBOX + SetTMSAction(i,eTMSAction_TMSPP_UserFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_TitleUser,"\\",eTMSAction_TMSPP_XUIDSFile); +#elif defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_UserFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_TitleUser,eTMSAction_TMSPP_DLCFile); +#else + SetTMSAction(i,eTMSAction_TMSPP_XUIDSFile); +#endif + break; + case eTMSAction_TMSPP_XUIDSFile: +#ifdef _XBOX + SetTMSAction(i,eTMSAction_TMSPP_XUIDSFile_Waiting); + // pass in the next app action on the call or callback completing + app.TMSPP_ReadXuidsFile(i,eTMSAction_TMSPP_DLCFile); +#else + SetTMSAction(i,eTMSAction_TMSPP_DLCFile); +#endif + + break; + case eTMSAction_TMSPP_DLCFile: +#if defined _XBOX || defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_DLCFile_Waiting); + // pass in the next app action on the call or callback completing + app.TMSPP_ReadDLCFile(i,eTMSAction_TMSPP_BannedListFile); +#else + SetTMSAction(i,eTMSAction_TMSPP_BannedListFile); +#endif + break; + case eTMSAction_TMSPP_BannedListFile: + // If we have one in TMSPP, then we can assume we can ignore TMS +#if defined _XBOX + SetTMSAction(i,eTMSAction_TMSPP_BannedListFile_Waiting); + // pass in the next app action on the call or callback completing + if(app.TMSPP_ReadBannedList(i,eTMSAction_TMS_RetrieveFiles_Complete)==false) + { + // we don't have a banned list in TMSPP, so we should check TMS + app.ReadBannedList(i, eTMSAction_TMS_RetrieveFiles_Complete,true); + } +#elif defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_BannedListFile_Waiting); + // pass in the next app action on the call or callback completing + app.TMSPP_ReadBannedList(i,eTMSAction_TMS_RetrieveFiles_Complete); + +#else + SetTMSAction(i,eTMSAction_TMS_RetrieveFiles_Complete); +#endif + break; + + // SPECIAL CASE - where the user goes directly in to Help & Options from the main menu + case eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions: + case eTMSAction_TMSPP_RetrieveFiles_DLCMain: + // retrieve the file list first +#if defined _XBOX + // pass in the next app action on the call or callback completing + SetTMSAction(i,eTMSAction_TMSPP_XUIDSFile_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_Title,"\\",eTMSAction_TMSPP_DLCFileOnly); +#elif defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_GlobalFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_Title,eTMSAction_TMSPP_RetrieveUserFilelist_DLCFileOnly); +#else + SetTMSAction(i,eTMSAction_TMSPP_DLCFileOnly); +#endif + break; + case eTMSAction_TMSPP_RetrieveUserFilelist_DLCFileOnly: +#if defined _XBOX + SetTMSAction(i,eTMSAction_TMSPP_UserFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_TitleUser,"\\",eTMSAction_TMSPP_XUIDSFile); +#elif defined _XBOX_ONE + //StorageManager.TMSPP_DeleteFile(i,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"TP06.png",NULL,NULL, 0); + SetTMSAction(i,eTMSAction_TMSPP_UserFileList_Waiting); + app.TMSPP_RetrieveFileList(i,C4JStorage::eGlobalStorage_TitleUser,eTMSAction_TMSPP_DLCFileOnly); +#else + SetTMSAction(i,eTMSAction_TMSPP_DLCFileOnly); +#endif + + break; + + case eTMSAction_TMSPP_DLCFileOnly: +#if defined _XBOX || defined _XBOX_ONE + SetTMSAction(i,eTMSAction_TMSPP_DLCFile_Waiting); + // pass in the next app action on the call or callback completing + app.TMSPP_ReadDLCFile(i,eTMSAction_TMSPP_RetrieveFiles_Complete); +#else + SetTMSAction(i,eTMSAction_TMSPP_RetrieveFiles_Complete); +#endif + break; + + + case eTMSAction_TMSPP_RetrieveFiles_Complete: + SetTMSAction(i,eTMSAction_Idle); + break; + + + // TMS files +/* case eTMSAction_TMS_RetrieveFiles_CreateLoad_SignInReturned: + case eTMSAction_TMS_RetrieveFiles_RunPlayGame: +#ifdef _XBOX + SetTMSAction(i,eTMSAction_TMS_XUIDSFile_Waiting); + // pass in the next app action on the call or callback completing + app.ReadXuidsFileFromTMS(i,eTMSAction_TMS_DLCFile,true); +#else + SetTMSAction(i,eTMSAction_TMS_DLCFile); +#endif + break; + + case eTMSAction_TMS_DLCFile: +#ifdef _XBOX + SetTMSAction(i,eTMSAction_TMS_DLCFile_Waiting); + // pass in the next app action on the call or callback completing + app.ReadDLCFileFromTMS(i,eTMSAction_TMS_BannedListFile,true); +#else + SetTMSAction(i,eTMSAction_TMS_BannedListFile); +#endif + + break; + + case eTMSAction_TMS_RetrieveFiles_HelpAndOptions: + case eTMSAction_TMS_RetrieveFiles_DLCMain: +#ifdef _XBOX + SetTMSAction(i,eTMSAction_TMS_DLCFile_Waiting); + // pass in the next app action on the call or callback completing + app.ReadDLCFileFromTMS(i,eTMSAction_Idle,true); +#else + SetTMSAction(i,eTMSAction_Idle); +#endif + + break; + case eTMSAction_TMS_BannedListFile: +#ifdef _XBOX + SetTMSAction(i,eTMSAction_TMS_BannedListFile_Waiting); + // pass in the next app action on the call or callback completing + app.ReadBannedList(i, eTMSAction_TMS_RetrieveFiles_Complete,true); +#else + SetTMSAction(i,eTMSAction_TMS_RetrieveFiles_Complete); +#endif + + break; + + */ + case eTMSAction_TMS_RetrieveFiles_Complete: + SetTMSAction(i,eTMSAction_Idle); + // if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,pClass)) + // { + // // save device already selected + // // ensure we've applied this player's settings + // app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); + // app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); + // } + break; + } + } + + } +} + +int CMinecraftApp::BannedLevelDialogReturned(void *pParam,int iPad,const C4JStorage::EMessageResult result) +{ + CMinecraftApp* pApp = (CMinecraftApp*)pParam; + //Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(result==C4JStorage::EMessage_ResultAccept) + { +#if defined _XBOX || defined _XBOX_ONE + INetworkPlayer *pHost = g_NetworkManager.GetHostPlayer(); + // unban the level + if (pHost != NULL) + { +#if defined _XBOX + pApp->RemoveLevelFromBannedLevelList(iPad,((NetworkPlayerXbox *)pHost)->GetUID(),pApp->GetUniqueMapName()); +#else + pApp->RemoveLevelFromBannedLevelList(iPad,pHost->GetUID(),pApp->GetUniqueMapName()); +#endif + } +#endif + } + else + { + if( iPad == ProfileManager.GetPrimaryPad() ) + { + pApp->SetAction(iPad,eAppAction_ExitWorld); + } + else + { + pApp->SetAction(iPad,eAppAction_ExitPlayer); + } + } + + return 0; +} + +void CMinecraftApp::loadMediaArchive() +{ + wstring mediapath = L""; + +#ifdef __PS3__ + mediapath = L"Common\\Media\\MediaPS3.arc"; +#elif _WINDOWS64 + mediapath = L"Common\\Media\\MediaWindows64.arc"; +#elif __ORBIS__ + mediapath = L"Common\\Media\\MediaOrbis.arc"; +#elif _DURANGO + mediapath = L"Common\\Media\\MediaDurango.arc"; +#elif __PSVITA__ + mediapath = L"Common\\Media\\MediaPSVita.arc"; +#endif + + if (!mediapath.empty()) + { + m_mediaArchive = new ArchiveFile( File(mediapath) ); + } +#if 0 + string path = "Common\\media.arc"; + HANDLE hFile = CreateFile( path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL ); + + if( hFile != INVALID_HANDLE_VALUE ) + { + File fileHelper(convStringToWstring(path)); + DWORD dwFileSize = fileHelper.length(); + + // Initialize memory. + PBYTE m_fBody = new BYTE[ dwFileSize ]; + ZeroMemory(m_fBody, dwFileSize); + + DWORD m_fSize = 0; + BOOL hr = ReadFile( hFile, + m_fBody, + dwFileSize, + &m_fSize, + NULL ); + + assert( m_fSize == dwFileSize ); + + CloseHandle( hFile ); + + m_mediaArchive = new ArchiveFile(m_fBody, m_fSize); + } + else + { + assert( false ); + // AHHHHHHHHHHHH + m_mediaArchive = NULL; + } +#endif +} + +void CMinecraftApp::loadStringTable() +{ +#ifndef _XBOX + + if(m_stringTable!=NULL) + { + // we need to unload the current string table, this is a reload + delete m_stringTable; + } + wstring localisationFile = L"languages.loc"; + if (m_mediaArchive->hasFile(localisationFile)) + { + byteArray locFile = m_mediaArchive->getFile(localisationFile); + m_stringTable = new StringTable(locFile.data, locFile.length); + delete locFile.data; + } + else + { + m_stringTable = NULL; + assert(false); + // AHHHHHHHHH. + } +#endif +} + +int CMinecraftApp::PrimaryPlayerSignedOutReturned(void *pParam,int iPad,const C4JStorage::EMessageResult) +{ + //CMinecraftApp* pApp = (CMinecraftApp*)pParam; + //Minecraft *pMinecraft=Minecraft::GetInstance(); + + // if the player is null, we're in the menus + //if(Minecraft::GetInstance()->player!=NULL) + + // We always create a session before kicking of any of the game code, so even though we may still be joining/creating a game + // at this point we want to handle it differently from just being in a menu + if( g_NetworkManager.IsInSession() ) + { + app.SetAction(iPad,eAppAction_PrimaryPlayerSignedOutReturned); + } + else + { + app.SetAction(iPad,eAppAction_PrimaryPlayerSignedOutReturned_Menus); + } + return 0; +} + +int CMinecraftApp::EthernetDisconnectReturned(void *pParam,int iPad,const C4JStorage::EMessageResult) +{ + //CMinecraftApp* pApp = (CMinecraftApp*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // if the player is null, we're in the menus + if(Minecraft::GetInstance()->player!=NULL) + { + app.SetAction(pMinecraft->player->GetXboxPad(),eAppAction_EthernetDisconnectedReturned); + } + else + { + // 4J-PB - turn off the PSN store icon just in case this happened when we were in one of the DLC menus +#ifdef __ORBIS__ + sceNpCommerceHidePsStoreIcon(); +#elif defined __PSVITA__ + sceNpCommerce2HidePsStoreIcon(); +#endif + app.SetAction(iPad,eAppAction_EthernetDisconnectedReturned_Menus); + } + return 0; +} + +int CMinecraftApp::SignoutExitWorldThreadProc( void* lpParameter ) +{ + + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + //app.SetGameStarted(false); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + int exitReasonStringId = -1; + + bool saveStats = false; + if (pMinecraft->isClientSide() || g_NetworkManager.IsInSession() ) + { + if(lpParameter != NULL ) + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + break; +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + break; +#endif + case DisconnectPacket::eDisconnect_NoFlying: + exitReasonStringId = IDS_DISCONNECTED_FLYING; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + break; + default: + exitReasonStringId = IDS_DISCONNECTED; + } + pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + // 4J - Force a disconnection, this handles the situation that the server has already disconnected + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(false); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(false); + } + else + { + exitReasonStringId = IDS_EXITING_GAME; + pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME ); + + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(); + } + + // 4J Stu - This only does something if we actually have a server, so don't need to do any other checks + MinecraftServer::HaltServer(true); + + // We need to call the stats & leaderboards save before we exit the session + //pMinecraft->forceStatsSave(); + saveStats = false; + + // 4J Stu - Leave the session once the disconnect packet has been sent + g_NetworkManager.LeaveGame(FALSE); + } + else + { + if(lpParameter != NULL ) + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + break; +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: + exitReasonStringId = IDS_CONTENT_RESTRICTION_MULTIPLAYER; + break; + case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: + exitReasonStringId = IDS_CONTENT_RESTRICTION; + break; +#endif +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + break; +#endif + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + default: + exitReasonStringId = IDS_DISCONNECTED; + } + pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + } + } + pMinecraft->setLevel(NULL,exitReasonStringId,nullptr,saveStats,true); + + // 4J-JEV: Fix for #106402 - TCR #014 BAS Debug Output: + // TU12: Mass Effect Mash-UP: Save file "Default_DisplayName" is created on all storage devices after signing out from a re-launched pre-generated world + app.m_gameRules.unloadCurrentGameRules(); // + + MinecraftServer::resetFlags(); + + // We can't start/join a new game until the session is destroyed, so wait for it to be idle again + while( g_NetworkManager.IsInSession() ) + { + Sleep(1); + } + + return S_OK; +} + +int CMinecraftApp::UnlockFullInviteReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //CMinecraftApp* pApp = (CMinecraftApp*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + bool bNoPlayer; + + // bug 11285 - TCR 001: BAS Game Stability: CRASH - When trying to join a full version game with a trial version, the trial crashes + // 4J-PB - we may be in the main menus here, and we don't have a pMinecraft->player + + if(pMinecraft->player==NULL) + { + bNoPlayer=true; + } + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedInLive(iPad)) + { + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,&app, app.GetStringTable()); + } + else +#endif + { + ProfileManager.DisplayFullVersionPurchase(false,iPad,eSen_UpsellID_Full_Version_Of_Game); + } + } +#if defined(__PS3__) + else + { + // you're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::MustSignInFullVersionPurchaseReturned,&app, app.GetStringTable()); + + } +#endif + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, eSen_UpsellOutcome_Declined); + } + + return 0; +} + +int CMinecraftApp::UnlockFullSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //CMinecraftApp* pApp = (CMinecraftApp*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedInLive(pMinecraft->player->GetXboxPad())) + { + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,&app, app.GetStringTable()); + } + else +#endif + { + ProfileManager.DisplayFullVersionPurchase(false,pMinecraft->player->GetXboxPad(),eSen_UpsellID_Full_Version_Of_Game); + } + } +#if defined(__PS3__) + else + { + // you're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::MustSignInFullVersionPurchaseReturned,&app, app.GetStringTable()); + } +#elif defined(__ORBIS__) + else + { + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPad, &CMinecraftApp::MustSignInFullVersionPurchaseReturned,&app, app.GetStringTable(), NULL, 0, false); + } + } +#endif + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, eSen_UpsellOutcome_Declined); + } + + return 0; +} + +int CMinecraftApp::UnlockFullExitReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CMinecraftApp* pApp = (CMinecraftApp*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedInLive(pMinecraft->player->GetXboxPad())) + { + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,&app, app.GetStringTable()); + } + else +#endif + { + ProfileManager.DisplayFullVersionPurchase(false,pMinecraft->player->GetXboxPad(),eSen_UpsellID_Full_Version_Of_Game); +#if defined __ORBIS__ || defined __PS3__ || defined __PSVITA__ + // still need to exit the trial or we'll be in the Pause menu with input ignored + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitWorldTrial); +#endif + } + } +#if defined(__PS3__) || defined __PSVITA__ + else + { + // you're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::MustSignInFullVersionPurchaseReturnedExitTrial,&app, app.GetStringTable()); + } +#elif defined(__ORBIS__) + else + { + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + // still need to exit the trial or we'll be in the Pause menu with input ignored + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitWorldTrial); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPad, &CMinecraftApp::MustSignInFullVersionPurchaseReturnedExitTrial,&app, app.GetStringTable(), NULL, 0, false); + } + } +#endif + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, eSen_UpsellOutcome_Declined); + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitWorldTrial); + } + + return 0; +} + +int CMinecraftApp::TrialOverReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CMinecraftApp* pApp = (CMinecraftApp*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(result==C4JStorage::EMessage_ResultAccept) + { + // we need a signed in user for the unlock + if(ProfileManager.IsSignedInLive(pMinecraft->player->GetXboxPad())) + { + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,&app, app.GetStringTable()); + } + else +#endif + { + ProfileManager.DisplayFullVersionPurchase(false,pMinecraft->player->GetXboxPad(),eSen_UpsellID_Full_Version_Of_Game); + } + } + else + { +#if defined(__PS3__) + + // you're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::MustSignInFullVersionPurchaseReturned,&app, app.GetStringTable()); + + // 4J Stu - We can't actually exit the game, so just exit back to the main menu + //pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitWorldTrial); +#else + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitTrial); +#endif + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, eSen_UpsellOutcome_Declined); + +#if defined(__PS3__) || defined(__ORBIS__) + // 4J Stu - We can't actually exit the game, so just exit back to the main menu + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitWorldTrial); +#else + pApp->SetAction(pMinecraft->player->GetXboxPad(),eAppAction_ExitTrial); +#endif + } + + return 0; +} + +void CMinecraftApp::ProfileReadErrorCallback(void *pParam) +{ + CMinecraftApp *pApp=(CMinecraftApp *)pParam; + int iPrimaryPlayer=ProfileManager.GetPrimaryPad(); + pApp->SetAction(iPrimaryPlayer, eAppAction_ProfileReadError); +} + +void CMinecraftApp::ClearSignInChangeUsersMask() +{ + // 4J-PB - When in the main menu, the user is on pad 0, and any change they make to their profile will be to pad 0 data + // If they then go in as a secondary player to a splitscreen game, their profile will not be read again on pad 1 if they were previously in a splitscreen game + // This is because m_uiLastSignInData remembers they were in previously, and doesn't read the profile data for them again + // Fix this by resetting the m_uiLastSignInData on pressing play game for secondary users. The Primary user does a read profile on play game anyway + int iPrimaryPlayer=ProfileManager.GetPrimaryPad(); + + if(m_uiLastSignInData!=0) + { + if(iPrimaryPlayer>=0) + { + m_uiLastSignInData=1<user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); +#endif + + CMinecraftApp *pApp=(CMinecraftApp *)pParam; + // check if the primary player signed out + int iPrimaryPlayer=ProfileManager.GetPrimaryPad(); + + if((ProfileManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1) + { + if ( ((uiSignInData & (1<SetAction(iPrimaryPlayer,eAppAction_PrimaryPlayerSignedOut); + + // 4J-PB - invalidate their banned level list + pApp->InvalidateBannedList(iPrimaryPlayer); + + // need to ditch any DLCOffers info + StorageManager.ClearDLCOffers(); + pApp->ClearAndResetDLCDownloadQueue(); + pApp->ClearDLCInstalled(); + } + else + { + unsigned int uiChangedPlayers = uiSignInData ^ m_uiLastSignInData; + + if( g_NetworkManager.IsInSession() ) + { + bool hasGuestIdChanged = false; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + DWORD guestNumber = 0; + if(ProfileManager.IsSignedIn(i)) + { + XUSER_SIGNIN_INFO info; + XUserGetSigninInfo(i,XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY ,&info); + pApp->DebugPrintf("Player at index %d has guest number %d\n", i,info.dwGuestNumber ); + guestNumber = info.dwGuestNumber; + } + if( pApp->m_currentSigninInfo[i].dwGuestNumber != 0 && guestNumber != 0 && pApp->m_currentSigninInfo[i].dwGuestNumber != guestNumber ) + { + hasGuestIdChanged = true; + } + } + + if( hasGuestIdChanged ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_GUEST_ORDER_CHANGED_TITLE, IDS_GUEST_ORDER_CHANGED_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL,app.GetStringTable()); + } + + // 4J Stu - On PS4 we can also cause to exit players if they are signed out here, but we shouldn't do that if + // we are going to switch to an offline game as it will likely crash due to incompatible parallel processes + bool switchToOffline = false; + // If it's an online game, and the primary profile is no longer signed into LIVE then we act as if disconnected + if( !ProfileManager.IsSignedInLive( ProfileManager.GetLockedProfile() ) && !g_NetworkManager.IsLocalGame() ) + { + switchToOffline = true; + } + + //printf("Old: %x, New: %x, Changed: %x\n", m_ulLastSignInData, ulSignInData, changedPlayers); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + // Primary player shouldn't be subjected to these checks, and shouldn't call ExitPlayer + if(i == iPrimaryPlayer) continue; + + // A guest a signed in or out, out of order which invalidates all the guest players we have in the game + if(hasGuestIdChanged && pApp->m_currentSigninInfo[i].dwGuestNumber != 0 && g_NetworkManager.GetLocalPlayerByUserIndex(i)!=NULL) + { + pApp->DebugPrintf("Recommending removal of player at index %d because their guest id changed\n",i); + pApp->SetAction(i,eAppAction_ExitPlayer); + } + else + { + XUSER_SIGNIN_INFO info; + XUserGetSigninInfo(i,XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY ,&info); + // 4J Stu - Also need to detect the case where the sign in mask is the same, but the player has swapped users (eg still signed in but xuid different) + // Fix for #48451 - TU5: Code: UI: Splitscreen: Title crashes when switching to a profile previously signed out via splitscreen profile selection + + // 4J-PB - compiler complained about if below ('&&' within '||') - making it easier to read + bool bPlayerChanged=(uiChangedPlayers&(1<m_currentSigninInfo[i].xuid, info.xuid) ) )) + { + // 4J-PB - invalidate their banned level list + pApp->DebugPrintf("Player at index %d Left - invalidating their banned list\n",i); + pApp->InvalidateBannedList(i); + + if(g_NetworkManager.GetLocalPlayerByUserIndex(i)!=NULL) + { + pApp->DebugPrintf("Player %d signed out\n", i); + pApp->SetAction(i,eAppAction_ExitPlayer); + } + } + } +#ifdef __ORBIS__ + // check if any of the addition players have signed out of PSN (primary player is handled below) + if(!switchToOffline && i != ProfileManager.GetLockedProfile() && !g_NetworkManager.IsLocalGame()) + { + if(g_NetworkManager.GetLocalPlayerByUserIndex(i)!=NULL) + { + if(ProfileManager.IsSignedInLive(i) == false) + { + pApp->DebugPrintf("Recommending removal of player at index %d because they're no longer signed into PSNd\n",i); + pApp->SetAction(i,eAppAction_ExitPlayer); + } + } + } +#endif + } + + // If it's an online game, and the primary profile is no longer signed into LIVE then we act as if disconnected + if( switchToOffline ) + { + pApp->SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected); + } + + + g_NetworkManager.HandleSignInChange(); + } + // Some menus require the player to be signed in to live, so if this callback happens and the primary player is + // no longer signed in then nav back + else if ( pApp->GetLiveLinkRequired() && !ProfileManager.IsSignedInLive( ProfileManager.GetLockedProfile() ) ) + { + pApp->SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected); + } + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__ ) + // 4J-JEV: Need to kick of loading of profile data for sub-sign in players. + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( i != iPrimaryPlayer + && ( uiChangedPlayers & (1<InvalidateBannedList(iPrimaryPlayer); + + // need to ditch any DLCOffers info + StorageManager.ClearDLCOffers(); + pApp->ClearAndResetDLCDownloadQueue(); + pApp->ClearDLCInstalled(); + + } + + // Update the guest numbers to the current state + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(FAILED(XUserGetSigninInfo(i,XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY,&pApp->m_currentSigninInfo[i]))) + { + pApp->m_currentSigninInfo[i].xuid = INVALID_XUID; + pApp->m_currentSigninInfo[i].dwGuestNumber = 0; + } + app.DebugPrintf("Player at index %d has guest number %d\n", i,pApp->m_currentSigninInfo[i].dwGuestNumber ); + } +} + +void CMinecraftApp::NotificationsCallback(LPVOID pParam,DWORD dwNotification, unsigned int uiParam) +{ + CMinecraftApp* pClass = (CMinecraftApp*)pParam; + + // push these on to the notifications to be handled in qnet's dowork + + PNOTIFICATION pNotification = new NOTIFICATION; + + pNotification->dwNotification=dwNotification; + pNotification->uiParam=uiParam; + + switch( dwNotification ) + { + case XN_SYS_SIGNINCHANGED: + { + pClass->DebugPrintf("Signing changed - %d\n", uiParam ); + } + break; + case XN_SYS_INPUTDEVICESCHANGED: + if(app.GetGameStarted() && g_NetworkManager.IsInSession()) + { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(!InputManager.IsPadConnected(i) && + Minecraft::GetInstance()->localplayers[i] != NULL && + !ui.IsPauseMenuDisplayed(i) && !ui.IsSceneInStack(i, eUIScene_EndPoem) ) + { + ui.CloseUIScenes(i); + ui.NavigateToScene(i,eUIScene_PauseMenu); + } + } + } + break; + case XN_LIVE_CONTENT_INSTALLED: + // Need to inform xuis that we've possibly had DLC installed + { + //app.m_dlcManager.SetNeedsUpdated(true); + // Clear the DLC installed flag to cause a GetDLC to run if it's called + app.ClearDLCInstalled(); + + ui.HandleDLCInstalled(ProfileManager.GetPrimaryPad()); + } + break; + case XN_SYS_STORAGEDEVICESCHANGED: + { +#ifdef _XBOX + // If the devices have changed, and we've got a dlc pack with audio selected, and that pack's content device is no longer valid... then pull the plug on + // audio streaming, as if we leave this until later xact gets locked up attempting to destroy the streamed wave bank. + TexturePack *pTexPack=Minecraft::GetInstance()->skins->getSelected(); + if(pTexPack->hasAudio()) + { + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)pTexPack; + XCONTENTDEVICEID deviceID = pDLCTexPack->GetDLCDeviceID(); + if( XContentGetDeviceState( deviceID, NULL ) != ERROR_SUCCESS ) + { + // Set texture pack flag so that it is now considered as not having audio - this is critical so that the next playStreaming does what it is meant to do, + // and also so that we don't try and unmount this again, or play any sounds from it in the future + pTexPack->setHasAudio(false); + // need to stop the streaming audio - by playing streaming audio from the default texture pack now + Minecraft::GetInstance()->soundEngine->playStreaming(L"", 0, 0, 0, 0, 0); + + if(pDLCTexPack->m_pStreamedWaveBank!=NULL) + { + pDLCTexPack->m_pStreamedWaveBank->Destroy(); + } + if(pDLCTexPack->m_pSoundBank!=NULL) + { + pDLCTexPack->m_pSoundBank->Destroy(); + } + DWORD result = StorageManager.UnmountInstalledDLC("TPACK"); + app.DebugPrintf("Unmount result is %d\n",result); + } + } +#endif + } + break; + } + + pClass->m_vNotifications.push_back(pNotification); +} + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +int CMinecraftApp::MustSignInFullVersionPurchaseReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + SQRNetworkManager_PS3::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#else // __PS4__ + SQRNetworkManager_Orbis::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#endif + } + + return 0; +} + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +int CMinecraftApp::MustSignInFullVersionPurchaseReturnedExitTrial(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + SQRNetworkManager_PS3::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#else // __PS4__ + SQRNetworkManager_Orbis::AttemptPSNSignIn(&CMinecraftApp::NowDisplayFullVersionPurchase, &app,true); +#endif + } + + //4J-PB - we need to exit the trial, or we'll be in the pause menu with ignore input true + app.SetAction(iPad,eAppAction_ExitWorldTrial); + + return 0; +} +#endif + +int CMinecraftApp::NowDisplayFullVersionPurchase(void *pParam, bool bContinue, int iPad) +{ + app.m_bDisplayFullVersionPurchase=true; + return 0; +} +#endif +void CMinecraftApp::UpsellReturnedCallback(LPVOID pParam, eUpsellType type, eUpsellResponse result, int iUserData) +{ + ESen_UpsellID senType; + ESen_UpsellOutcome senResponse; +#ifdef __PS3__ + UINT uiIDA[2]; +#endif + + // Map the eUpsellResponse to the enum we use for sentient + switch(result) + { + case eUpsellResponse_Accepted_NoPurchase: + senResponse = eSen_UpsellOutcome_Went_To_Guide; + break; + case eUpsellResponse_Accepted_Purchase: + senResponse = eSen_UpsellOutcome_Accepted; + break; +#ifdef __PS3__ + // special case for people who are not signed in to the PSN while playing the trial game + case eUpsellResponse_UserNotSignedInPSN: + + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::MustSignInFullVersionPurchaseReturned,&app, app.GetStringTable()); + + return; + + case eUpsellResponse_NotAllowedOnline: // On earning a trophy in the trial version, where the user is underage and can't go online to buy the game, but they selected to buy the game on the trophy upsell + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,&app, app.GetStringTable()); + break; +#endif + case eUpsellResponse_Declined: + default: + senResponse = eSen_UpsellOutcome_Declined; + break; + }; + + // Map the eUpsellType to the enum we use for sentient + switch(type) + { + case eUpsellType_Custom: + senType = eSen_UpsellID_Full_Version_Of_Game; + break; + default: + senType = eSen_UpsellID_Undefined; + break; + }; + + // Always the primary pad that gets an upsell + TelemetryManager->RecordUpsellResponded(ProfileManager.GetPrimaryPad(), eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, senResponse); +} + +void CMinecraftApp::SetDebugSequence(const char *pchSeq) +{ + InputManager.SetDebugSequence(pchSeq,&CMinecraftApp::DebugInputCallback,this); +} +int CMinecraftApp::DebugInputCallback(LPVOID pParam) +{ + CMinecraftApp* pClass = (CMinecraftApp*)pParam; + //printf("sequence matched\n"); + pClass->m_bDebugOptions=!pClass->m_bDebugOptions; + + for(int i=0;ilocalplayers[i] != NULL) + { + iPlayerC++; + } + } + + return iPlayerC; +} + +int CMinecraftApp::MarketplaceCountsCallback(LPVOID pParam,C4JStorage::DLC_TMS_DETAILS *pTMSDetails, int iPad) +{ + app.DebugPrintf("Marketplace Counts= New - %d Total - %d\n",pTMSDetails->dwNewOffers,pTMSDetails->dwTotalOffers); + + if(pTMSDetails->dwNewOffers>0) + { + app.m_bNewDLCAvailable=true; + app.m_bSeenNewDLCTip=false; + } + else + { + app.m_bNewDLCAvailable=false; + app.m_bSeenNewDLCTip=true; + } + + return 0; +} + +bool CMinecraftApp::StartInstallDLCProcess(int iPad) +{ + app.DebugPrintf("--- CMinecraftApp::StartInstallDLCProcess: pad=%i.\n", iPad); + + // If there is already a call to this in progress, then do nothing + // If the app says dlc is installed, then there has been no new system message to tell us there's new DLC since the last call to StartInstallDLCProcess + if((app.DLCInstallProcessCompleted()==false) && (m_bDLCInstallPending==false)) + { + app.m_dlcManager.resetUnnamedCorruptCount(); + m_bDLCInstallPending = true; + m_iTotalDLC = 0; + m_iTotalDLCInstalled = 0; + app.DebugPrintf("--- CMinecraftApp::StartInstallDLCProcess - StorageManager.GetInstalledDLC\n"); + + StorageManager.GetInstalledDLC(iPad,&CMinecraftApp::DLCInstalledCallback,this); + return true; + } + else + { + app.DebugPrintf("--- CMinecraftApp::StartInstallDLCProcess - nothing to do\n"); + + return false; + } + +} + +// Installed DLC callback +int CMinecraftApp::DLCInstalledCallback(LPVOID pParam,int iInstalledC,int iPad) +{ + app.DebugPrintf("--- CMinecraftApp::DLCInstalledCallback: totalDLC=%i, pad=%i.\n", iInstalledC, iPad); + app.m_iTotalDLC = iInstalledC; + app.MountNextDLC(iPad); + return 0; +} + +void CMinecraftApp::MountNextDLC(int iPad) +{ + app.DebugPrintf("--- CMinecraftApp::MountNextDLC: pad=%i.\n", iPad); + if(m_iTotalDLCInstalled < m_iTotalDLC) + { + // Mount it + // We also need to match the ones the user wants to mount with the installed DLC + // We're supposed to use a generic save game as a cache of these to do this, with XUSER_ANY + + if(StorageManager.MountInstalledDLC(iPad,m_iTotalDLCInstalled,&CMinecraftApp::DLCMountedCallback,this)!=ERROR_IO_PENDING ) + { + // corrupt DLC + app.DebugPrintf("Failed to mount DLC %d for pad %d\n",m_iTotalDLCInstalled,iPad); + ++m_iTotalDLCInstalled; + app.MountNextDLC(iPad); + } + else + { + app.DebugPrintf("StorageManager.MountInstalledDLC ok\n"); + } + } + else + { + /* Removed - now loading these on demand instead of as each pack is mounted + if(m_iTotalDLCInstalled > 0) + { + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->levelRenderer->AddDLCSkinsToMemTextures(); + } + */ + + m_bDLCInstallPending = false; + m_bDLCInstallProcessCompleted=true; + + ui.HandleDLCMountingComplete(); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Check if the current texture pack is now installed + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pParentPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + + if(pParentPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + StorageManager.SetSaveDisabled(false); + } + } +#endif + } +} + +// 4J-JEV: For the sake of clarity in DLCMountedCallback. +#if defined(_XBOX) || defined(__PS3__) || defined(_WINDOWS64) +#define CONTENT_DATA_DISPLAY_NAME(a) (a.szDisplayName) +#else +#define CONTENT_DATA_DISPLAY_NAME(a) (a.wszDisplayName) +#endif + +int CMinecraftApp::DLCMountedCallback(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicenceMask) +{ +#if defined(_XBOX) || defined(_DURANGO) || defined(__PS3__) || defined(__ORBIS__) || defined(_WINDOWS64) || defined (__PSVITA__) //Chris TODO + app.DebugPrintf("--- CMinecraftApp::DLCMountedCallback\n"); + + if(dwErr!=ERROR_SUCCESS) + { + // corrupt DLC + app.DebugPrintf("Failed to mount DLC for pad %d: %d\n",iPad,dwErr); + app.m_dlcManager.incrementUnnamedCorruptCount(); + } + else + { + XCONTENT_DATA ContentData = StorageManager.GetDLC(app.m_iTotalDLCInstalled); + + DLCPack *pack = app.m_dlcManager.getPack( CONTENT_DATA_DISPLAY_NAME(ContentData) ); + + if( pack != NULL && pack->IsCorrupt() ) + { + app.DebugPrintf("Pack '%ls' is corrupt, removing it from the DLC Manager.\n", CONTENT_DATA_DISPLAY_NAME(ContentData)); + + app.m_dlcManager.removePack(pack); + pack = NULL; + } + + if(pack == NULL) + { + app.DebugPrintf("Pack \"%ls\" is not installed, so adding it\n", CONTENT_DATA_DISPLAY_NAME(ContentData)); + +#if defined(_XBOX) || defined(__PS3__) || defined(_WINDOWS64) + pack = new DLCPack(ContentData.szDisplayName,dwLicenceMask); +#elif defined _XBOX_ONE + pack = new DLCPack(ContentData.wszDisplayName,ContentData.wszProductID,dwLicenceMask); +#else + pack = new DLCPack(ContentData.wszDisplayName,dwLicenceMask); +#endif + pack->SetDLCMountIndex(app.m_iTotalDLCInstalled); + pack->SetDLCDeviceID(ContentData.DeviceID); + app.m_dlcManager.addPack(pack); + + app.HandleDLC(pack); + + if(pack->getDLCItemsCount(DLCManager::e_DLCType_Texture) > 0) + { + Minecraft::GetInstance()->skins->addTexturePackFromDLC(pack, pack->GetPackId() ); + } + } + else + { + app.DebugPrintf("Pack \"%ls\" is already installed. Updating license to %d\n", CONTENT_DATA_DISPLAY_NAME(ContentData), dwLicenceMask); + + pack->SetDLCMountIndex(app.m_iTotalDLCInstalled); + pack->SetDLCDeviceID(ContentData.DeviceID); + pack->updateLicenseMask(dwLicenceMask); + } + + StorageManager.UnmountInstalledDLC(); + } + ++app.m_iTotalDLCInstalled; + app.MountNextDLC(iPad); + +#endif // __PSVITA__ + return 0; +} +#undef CONTENT_DATA_DISPLAY_NAME + +// void CMinecraftApp::InstallDefaultCape() +// { +// if(!m_bDefaultCapeInstallAttempted) +// { +// // we only attempt to install the cape once per launch of the game +// m_bDefaultCapeInstallAttempted=true; +// +// wstring wTemp=L"Default_Cape.png"; +// bool bRes=app.IsFileInMemoryTextures(wTemp); +// // if the file is not already in the memory textures, then read it from TMS +// if(!bRes) +// { +// BYTE *pBuffer=NULL; +// DWORD dwSize=0; +// // 4J-PB - out for now for DaveK so he doesn't get the birthday cape +// #ifdef _CONTENT_PACKAGE +// C4JStorage::ETMSStatus eTMSStatus; +// eTMSStatus=StorageManager.ReadTMSFile(ProfileManager.GetPrimaryPad(),C4JStorage::eGlobalStorage_Title,C4JStorage::eTMS_FileType_Graphic, L"Default_Cape.png",&pBuffer, &dwSize); +// if(eTMSStatus==C4JStorage::ETMSStatus_Idle) +// { +// app.AddMemoryTextureFile(wTemp,pBuffer,dwSize); +// } +// #endif +// } +// } +// } + + void CMinecraftApp::HandleDLC(DLCPack *pack) + { + DWORD dwFilesProcessed = 0; +#ifndef _XBOX +#if defined(__PS3__) || defined(__ORBIS__) || defined(_WINDOWS64) || defined (__PSVITA__) + std::vector dlcFilenames; +#elif defined _DURANGO + std::vector dlcFilenames; +#endif + StorageManager.GetMountedDLCFileList("DLCDrive", dlcFilenames); + for(int i=0; ieXuid==eXUID_Deadmau5) + { + return true; + } + } + + return false; +} + +void CMinecraftApp::AddMemoryTextureFile(const wstring &wName,PBYTE pbData,DWORD dwBytes) +{ + EnterCriticalSection(&csMemFilesLock); + // check it's not already in + PMEMDATA pData=NULL; + AUTO_VAR(it, m_MEM_Files.find(wName)); + if(it != m_MEM_Files.end()) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Incrementing the memory texture file count for %ls\n", wName.c_str()); +#endif + pData = (*it).second; + + if(pData->dwBytes == 0 && dwBytes != 0) + { + // This should never be NULL if dwBytes is 0 + if(pData->pbData!=NULL) delete [] pData->pbData; + + pData->pbData=pbData; + pData->dwBytes=dwBytes; + } + + ++pData->ucRefCount; + LeaveCriticalSection(&csMemFilesLock); + return; + } + +#ifndef _CONTENT_PACKAGE + //wprintf(L"Adding the memory texture file data for %ls\n", wName.c_str()); +#endif + // this is a texture (png) file + + // add this texture to the list of memory texture files - it will then be picked up by the level renderer's AddEntity + + pData = (PMEMDATA)new BYTE[sizeof(MEMDATA)]; + ZeroMemory( pData, sizeof(MEMDATA) ); + pData->pbData=pbData; + pData->dwBytes=dwBytes; + pData->ucRefCount = 1; + + // use the xuid to access the skin data + m_MEM_Files[wName]=pData; + + LeaveCriticalSection(&csMemFilesLock); +} + +void CMinecraftApp::RemoveMemoryTextureFile(const wstring &wName) +{ + EnterCriticalSection(&csMemFilesLock); + + AUTO_VAR(it, m_MEM_Files.find(wName)); + if(it != m_MEM_Files.end()) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Decrementing the memory texture file count for %ls\n", wName.c_str()); +#endif + PMEMDATA pData = (*it).second; + --pData->ucRefCount; + if(pData->ucRefCount <= 0) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Erasing the memory texture file data for %ls\n", wName.c_str()); +#endif + delete [] pData; + m_MEM_Files.erase(wName); + } + } + LeaveCriticalSection(&csMemFilesLock); +} + +bool CMinecraftApp::DefaultCapeExists() +{ + wstring wTex=L"Special_Cape.png"; + bool val = false; + + EnterCriticalSection(&csMemFilesLock); + AUTO_VAR(it, m_MEM_Files.find(wTex)); + if(it != m_MEM_Files.end()) val = true; + LeaveCriticalSection(&csMemFilesLock); + + return val; +} + +bool CMinecraftApp::IsFileInMemoryTextures(const wstring &wName) +{ + bool val = false; + + EnterCriticalSection(&csMemFilesLock); + AUTO_VAR(it, m_MEM_Files.find(wName)); + if(it != m_MEM_Files.end()) val = true; + LeaveCriticalSection(&csMemFilesLock); + + return val; +} + +void CMinecraftApp::GetMemFileDetails(const wstring &wName,PBYTE *ppbData,DWORD *pdwBytes) +{ + EnterCriticalSection(&csMemFilesLock); + AUTO_VAR(it, m_MEM_Files.find(wName)); + if(it != m_MEM_Files.end()) + { + PMEMDATA pData = (*it).second; + *ppbData=pData->pbData; + *pdwBytes=pData->dwBytes; + } + LeaveCriticalSection(&csMemFilesLock); +} + +void CMinecraftApp::AddMemoryTPDFile(int iConfig,PBYTE pbData,DWORD dwBytes) +{ + EnterCriticalSection(&csMemTPDLock); + // check it's not already in + PMEMDATA pData=NULL; + AUTO_VAR(it, m_MEM_TPD.find(iConfig)); + if(it == m_MEM_TPD.end()) + { + pData = (PMEMDATA)new BYTE[sizeof(MEMDATA)]; + ZeroMemory( pData, sizeof(MEMDATA) ); + pData->pbData=pbData; + pData->dwBytes=dwBytes; + pData->ucRefCount = 1; + + m_MEM_TPD[iConfig]=pData; + } + + LeaveCriticalSection(&csMemTPDLock); +} + +void CMinecraftApp::RemoveMemoryTPDFile(int iConfig) +{ + EnterCriticalSection(&csMemTPDLock); + // check it's not already in + PMEMDATA pData=NULL; + AUTO_VAR(it, m_MEM_TPD.find(iConfig)); + if(it != m_MEM_TPD.end()) + { + pData=m_MEM_TPD[iConfig]; + delete [] pData; + m_MEM_TPD.erase(iConfig); + } + + LeaveCriticalSection(&csMemTPDLock); +} + +#ifdef _XBOX +int CMinecraftApp::GetTPConfigVal(WCHAR *pwchDataFile) +{ + DLC_INFO *pDLCInfo=NULL; + // run through the DLC info to find the right texture pack/mash-up pack + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + + if(wcscmp(pwchDataFile,pDLCInfo->wchDataFile)==0) + { + return pDLCInfo->iConfig; + } + } + + return -1; +} +#elif defined _XBOX_ONE +int CMinecraftApp::GetTPConfigVal(WCHAR *pwchDataFile) +{ + DLC_INFO *pDLCInfo=NULL; + // run through the DLC info to find the right texture pack/mash-up pack + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + pDLCInfo=app.GetDLCInfoForFullOfferID((WCHAR *)app.GetDLCInfoTexturesFullOffer(i).c_str()); + + if(wcscmp(pwchDataFile,pDLCInfo->wchDataFile)==0) + { + return pDLCInfo->iConfig; + } + } + + return -1; +} +#elif defined _WINDOWS64 +int CMinecraftApp::GetTPConfigVal(WCHAR *pwchDataFile) +{ + return -1; +} +#endif +bool CMinecraftApp::IsFileInTPD(int iConfig) +{ + bool val = false; + + EnterCriticalSection(&csMemTPDLock); + AUTO_VAR(it, m_MEM_TPD.find(iConfig)); + if(it != m_MEM_TPD.end()) val = true; + LeaveCriticalSection(&csMemTPDLock); + + return val; +} + +void CMinecraftApp::GetTPD(int iConfig,PBYTE *ppbData,DWORD *pdwBytes) +{ + EnterCriticalSection(&csMemTPDLock); + AUTO_VAR(it, m_MEM_TPD.find(iConfig)); + if(it != m_MEM_TPD.end()) + { + PMEMDATA pData = (*it).second; + *ppbData=pData->pbData; + *pdwBytes=pData->dwBytes; + } + LeaveCriticalSection(&csMemTPDLock); +} + + +// bool CMinecraftApp::UploadFileToGlobalStorage(int iQuadrant, C4JStorage::eGlobalStorage eStorageFacility, wstring *wsFile ) +// { +// bool bRes=false; +// #ifndef _CONTENT_PACKAGE +// // read the local file +// File gtsFile( wsFile->c_str() ); +// +// __int64 fileSize = gtsFile.length(); +// +// if(fileSize!=0) +// { +// FileInputStream fis(gtsFile); +// byteArray ba((int)fileSize); +// fis.read(ba); +// fis.close(); +// +// bRes=StorageManager.WriteTMSFile(iQuadrant,eStorageFacility,(WCHAR *)wsFile->c_str(),ba.data, ba.length); +// +// } +// #endif +// return bRes; +// } + + + + + + +void CMinecraftApp::StoreLaunchData() +{ + +} + +void CMinecraftApp::ExitGame() +{ +} + +// Invites + +void CMinecraftApp::ProcessInvite(DWORD dwUserIndex, DWORD dwLocalUsersMask, const INVITE_INFO * pInviteInfo) +{ + m_InviteData.dwUserIndex=dwUserIndex; + m_InviteData.dwLocalUsersMask=dwLocalUsersMask; + m_InviteData.pInviteInfo=pInviteInfo; + //memcpy(&m_InviteData,pJoinData,sizeof(JoinFromInviteData)); + SetAction(dwUserIndex,eAppAction_ExitAndJoinFromInvite); +} + +int CMinecraftApp::ExitAndJoinFromInvite(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CMinecraftApp* pApp = (CMinecraftApp*)pParam; + //Minecraft *pMinecraft=Minecraft::GetInstance(); + + // buttons are swapped on this menu + if(result==C4JStorage::EMessage_ResultDecline) + { + pApp->SetAction(iPad,eAppAction_ExitAndJoinFromInviteConfirmed); + } + + return 0; +} + +int CMinecraftApp::ExitAndJoinFromInviteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CMinecraftApp *pClass = (CMinecraftApp *)pParam; + // Exit with or without saving + // Decline means save in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) + { + if( result==C4JStorage::EMessage_ResultDecline ) // Save + { + // Check they have the full texture pack if they are using one + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + + DLCPack * pDLCPack=tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // upsell + // get the dlc texture pack + +#ifdef _XBOX + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, iPad,&CMinecraftApp::WarningTrialTexturePackReturned,pClass,app.GetStringTable()); + + return S_OK; + } + } +#ifndef _XBOX_ONE + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::ExitAndJoinFromInviteAndSaveReturned,pClass, app.GetStringTable()); + return 0; + } + else +#endif + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( true ); + } + } + else + { + // been a few requests for a confirm on exit without saving + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_DECLINE_SAVE_GAME, IDS_CONFIRM_DECLINE_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CMinecraftApp::ExitAndJoinFromInviteDeclineSaveReturned,pClass, app.GetStringTable()); + return 0; + } + + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitAndJoinFromInviteConfirmed); + } + return 0; +} + +int CMinecraftApp::WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ +#ifdef _XBOX + + CMinecraftApp* pClass = (CMinecraftApp*)pParam; + + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + ULONGLONG ullIndexA[1]; + + // Need to get the parent packs id, since this may be one of many child packs with their own ids + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullIndexA[0]); + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + // need to allow downloads here, or the player would need to quit the game to let the download of a texture pack happen. This might affect the network traffic, since the download could take all the bandwidth... + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( ullIndexA[0] & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } +#endif + return 0; +} + +int CMinecraftApp::ExitAndJoinFromInviteAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //CMinecraftApp* pClass = (CMinecraftApp*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + INT saveOrCheckpointId = 0; + + // Check they have the full texture pack if they are using one + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + + DLCPack * pDLCPack=tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // upsell + // get the dlc texture pack + +#ifdef _XBOX + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, iPad,&CMinecraftApp::WarningTrialTexturePackReturned,NULL,app.GetStringTable()); + + return S_OK; + } + } + //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId); + MinecraftServer::getInstance()->setSaveOnExit( true ); + // flag a app action of exit and join game from invite + app.SetAction(iPad,eAppAction_ExitAndJoinFromInviteConfirmed); + } + return 0; +} + +int CMinecraftApp::ExitAndJoinFromInviteDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( false ); + // flag a app action of exit and join game from invite + app.SetAction(iPad,eAppAction_ExitAndJoinFromInviteConfirmed); + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +// +// FatalLoadError +// +// This is called when we can't load one of the required files at startup +// It tends to mean the files have been corrupted. +// We have to assume that we've not been able to load the text for the game. +// +////////////////////////////////////////////////////////////////////////// +void CMinecraftApp::FatalLoadError() +{ + +} + +TIPSTRUCT CMinecraftApp::m_GameTipA[MAX_TIPS_GAMETIP]= +{ + { 0, IDS_TIPS_GAMETIP_1}, + { 0, IDS_TIPS_GAMETIP_2}, + { 0, IDS_TIPS_GAMETIP_3}, + { 0, IDS_TIPS_GAMETIP_4}, + { 0, IDS_TIPS_GAMETIP_5}, + { 0, IDS_TIPS_GAMETIP_6}, + { 0, IDS_TIPS_GAMETIP_7}, + { 0, IDS_TIPS_GAMETIP_8}, + { 0, IDS_TIPS_GAMETIP_9}, + { 0, IDS_TIPS_GAMETIP_10}, + { 0, IDS_TIPS_GAMETIP_11}, + { 0, IDS_TIPS_GAMETIP_12}, + { 0, IDS_TIPS_GAMETIP_13}, + { 0, IDS_TIPS_GAMETIP_14}, + { 0, IDS_TIPS_GAMETIP_15}, + { 0, IDS_TIPS_GAMETIP_16}, + { 0, IDS_TIPS_GAMETIP_17}, + { 0, IDS_TIPS_GAMETIP_18}, + { 0, IDS_TIPS_GAMETIP_19}, + { 0, IDS_TIPS_GAMETIP_20}, + { 0, IDS_TIPS_GAMETIP_21}, + { 0, IDS_TIPS_GAMETIP_22}, + { 0, IDS_TIPS_GAMETIP_23}, + { 0, IDS_TIPS_GAMETIP_24}, + { 0, IDS_TIPS_GAMETIP_25}, + { 0, IDS_TIPS_GAMETIP_26}, + { 0, IDS_TIPS_GAMETIP_27}, + { 0, IDS_TIPS_GAMETIP_28}, + { 0, IDS_TIPS_GAMETIP_29}, + { 0, IDS_TIPS_GAMETIP_30}, + { 0, IDS_TIPS_GAMETIP_31}, + { 0, IDS_TIPS_GAMETIP_32}, + { 0, IDS_TIPS_GAMETIP_33}, + { 0, IDS_TIPS_GAMETIP_34}, + { 0, IDS_TIPS_GAMETIP_35}, + { 0, IDS_TIPS_GAMETIP_36}, + { 0, IDS_TIPS_GAMETIP_37}, + { 0, IDS_TIPS_GAMETIP_38}, + { 0, IDS_TIPS_GAMETIP_39}, + { 0, IDS_TIPS_GAMETIP_40}, + { 0, IDS_TIPS_GAMETIP_41}, + { 0, IDS_TIPS_GAMETIP_42}, + { 0, IDS_TIPS_GAMETIP_43}, + { 0, IDS_TIPS_GAMETIP_44}, + { 0, IDS_TIPS_GAMETIP_45}, + { 0, IDS_TIPS_GAMETIP_46}, + { 0, IDS_TIPS_GAMETIP_47}, + { 0, IDS_TIPS_GAMETIP_48}, + { 0, IDS_TIPS_GAMETIP_49}, + { 0, IDS_TIPS_GAMETIP_50}, +}; + +TIPSTRUCT CMinecraftApp::m_TriviaTipA[MAX_TIPS_TRIVIATIP]= +{ + { 0, IDS_TIPS_TRIVIA_1}, + { 0, IDS_TIPS_TRIVIA_2}, + { 0, IDS_TIPS_TRIVIA_3}, + { 0, IDS_TIPS_TRIVIA_4}, + { 0, IDS_TIPS_TRIVIA_5}, + { 0, IDS_TIPS_TRIVIA_6}, + { 0, IDS_TIPS_TRIVIA_7}, + { 0, IDS_TIPS_TRIVIA_8}, + { 0, IDS_TIPS_TRIVIA_9}, + { 0, IDS_TIPS_TRIVIA_10}, + { 0, IDS_TIPS_TRIVIA_11}, + { 0, IDS_TIPS_TRIVIA_12}, + { 0, IDS_TIPS_TRIVIA_13}, + { 0, IDS_TIPS_TRIVIA_14}, + { 0, IDS_TIPS_TRIVIA_15}, + { 0, IDS_TIPS_TRIVIA_16}, + { 0, IDS_TIPS_TRIVIA_17}, + { 0, IDS_TIPS_TRIVIA_18}, + { 0, IDS_TIPS_TRIVIA_19}, + { 0, IDS_TIPS_TRIVIA_20}, +}; + +Random *CMinecraftApp::TipRandom = new Random(); + +int CMinecraftApp::TipsSortFunction(const void* a, const void* b) +{ + return ((TIPSTRUCT*)a)->iSortValue - ((TIPSTRUCT*)b)->iSortValue; +} + +void CMinecraftApp::InitialiseTips() +{ + // We'll randomise the tips at start up based on their priority + + ZeroMemory(m_TipIDA,sizeof(UINT)*MAX_TIPS_GAMETIP+MAX_TIPS_TRIVIATIP); + + // Make the first tip tell you that you can play splitscreen in HD modes if you are in SD + if(!RenderManager.IsHiDef()) + { + m_GameTipA[0].uiStringID=IDS_TIPS_GAMETIP_0; + } + // randomise then quicksort + // going to leave the multiplayer tip so it is always first + +// Only randomise the content package build +#ifdef _CONTENT_PACKAGE + + for(int i=1;inextInt(); + } + qsort( &m_GameTipA[1], MAX_TIPS_GAMETIP-1, sizeof(TIPSTRUCT), TipsSortFunction ); +#endif + + for(int i=0;inextInt(); + } + qsort( m_TriviaTipA, MAX_TIPS_TRIVIATIP, sizeof(TIPSTRUCT), TipsSortFunction ); + + + int iCurrentGameTip=0; + int iCurrentTriviaTip=0; + + for(int i=0;iskins->getSelected()->getColourTable()->getColour(colour); +} + +int CMinecraftApp::GetHTMLFontSize(EHTMLFontSize size) +{ + return s_iHTMLFontSizesA[size]; +} + +wstring CMinecraftApp::FormatHTMLString(int iPad, const wstring &desc, int shadowColour /*= 0xFFFFFFFF*/) +{ + wstring text(desc); + + wchar_t replacements[64]; + // We will also insert line breaks here as couldn't figure out how to get them to come through from strings.resx ! + text = replaceAll(text, L"{*B*}", L"
" ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_T1)); + text = replaceAll(text, L"{*T1*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_T2)); + text = replaceAll(text, L"{*T2*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_T3)); + text = replaceAll(text, L"{*T3*}", replacements ); // for How To Play + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_Black)); + text = replaceAll(text, L"{*ETB*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_White)); + text = replaceAll(text, L"{*ETW*}", replacements ); + text = replaceAll(text, L"{*EF*}", L"" ); + + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_0), shadowColour); + text = replaceAll(text, L"{*C0*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_1), shadowColour); + text = replaceAll(text, L"{*C1*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_2), shadowColour); + text = replaceAll(text, L"{*C2*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_3), shadowColour); + text = replaceAll(text, L"{*C3*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_4), shadowColour); + text = replaceAll(text, L"{*C4*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_5), shadowColour); + text = replaceAll(text, L"{*C5*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_6), shadowColour); + text = replaceAll(text, L"{*C6*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_7), shadowColour); + text = replaceAll(text, L"{*C7*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_8), shadowColour); + text = replaceAll(text, L"{*C8*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_9), shadowColour); + text = replaceAll(text, L"{*C9*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_a), shadowColour); + text = replaceAll(text, L"{*CA*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_b), shadowColour); + text = replaceAll(text, L"{*CB*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_c), shadowColour); + text = replaceAll(text, L"{*CC*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_d), shadowColour); + text = replaceAll(text, L"{*CD*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_e), shadowColour); + text = replaceAll(text, L"{*CE*}", replacements ); + swprintf(replacements,64,L"", GetHTMLColour(eHTMLColor_f), shadowColour); + text = replaceAll(text, L"{*CF*}", replacements ); + + // Swap for southpaw. + if ( app.GetGameSettings(iPad,eGameSetting_ControlSouthPaw) ) + { + text = replaceAll(text, L"{*CONTROLLER_ACTION_MOVE*}", GetActionReplacement(iPad,MINECRAFT_ACTION_LOOK_RIGHT ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_LOOK*}", GetActionReplacement(iPad,MINECRAFT_ACTION_RIGHT ) ); + + text = replaceAll(text, L"{*CONTROLLER_MENU_NAVIGATE*}", GetVKReplacement(VK_PAD_RTHUMB_LEFT) ); + } + else // Normal right handed. + { + text = replaceAll(text, L"{*CONTROLLER_ACTION_MOVE*}", GetActionReplacement(iPad,MINECRAFT_ACTION_RIGHT ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_LOOK*}", GetActionReplacement(iPad,MINECRAFT_ACTION_LOOK_RIGHT ) ); + + text = replaceAll(text, L"{*CONTROLLER_MENU_NAVIGATE*}", GetVKReplacement(VK_PAD_LTHUMB_LEFT) ); + } + + text = replaceAll(text, L"{*CONTROLLER_ACTION_JUMP*}", GetActionReplacement(iPad,MINECRAFT_ACTION_JUMP ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_SNEAK*}", GetActionReplacement(iPad,MINECRAFT_ACTION_SNEAK_TOGGLE ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_USE*}", GetActionReplacement(iPad,MINECRAFT_ACTION_USE ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_ACTION*}", GetActionReplacement(iPad,MINECRAFT_ACTION_ACTION ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_LEFT_SCROLL*}", GetActionReplacement(iPad,MINECRAFT_ACTION_LEFT_SCROLL ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_RIGHT_SCROLL*}", GetActionReplacement(iPad,MINECRAFT_ACTION_RIGHT_SCROLL ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_INVENTORY*}", GetActionReplacement(iPad,MINECRAFT_ACTION_INVENTORY ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_CRAFTING*}", GetActionReplacement(iPad,MINECRAFT_ACTION_CRAFTING ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_DROP*}", GetActionReplacement(iPad,MINECRAFT_ACTION_DROP ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_CAMERA*}", GetActionReplacement(iPad,MINECRAFT_ACTION_RENDER_THIRD_PERSON ) ); + text = replaceAll(text, L"{*CONTROLLER_VK_A*}", GetVKReplacement(VK_PAD_A) ); + text = replaceAll(text, L"{*CONTROLLER_VK_B*}", GetVKReplacement(VK_PAD_B) ); + text = replaceAll(text, L"{*CONTROLLER_VK_X*}", GetVKReplacement(VK_PAD_X) ); + text = replaceAll(text, L"{*CONTROLLER_VK_Y*}", GetVKReplacement(VK_PAD_Y) ); + text = replaceAll(text, L"{*CONTROLLER_VK_LB*}", GetVKReplacement(VK_PAD_LSHOULDER) ); + text = replaceAll(text, L"{*CONTROLLER_VK_RB*}", GetVKReplacement(VK_PAD_RSHOULDER) ); + text = replaceAll(text, L"{*CONTROLLER_VK_LS*}", GetVKReplacement(VK_PAD_LTHUMB_UP) ); + text = replaceAll(text, L"{*CONTROLLER_VK_RS*}", GetVKReplacement(VK_PAD_RTHUMB_UP) ); + text = replaceAll(text, L"{*CONTROLLER_VK_LT*}", GetVKReplacement(VK_PAD_LTRIGGER) ); + text = replaceAll(text, L"{*CONTROLLER_VK_RT*}", GetVKReplacement(VK_PAD_RTRIGGER) ); + text = replaceAll(text, L"{*ICON_SHANK_01*}", GetIconReplacement(XZP_ICON_SHANK_01) ); + text = replaceAll(text, L"{*ICON_SHANK_03*}", GetIconReplacement(XZP_ICON_SHANK_03) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_DPAD_UP*}", GetActionReplacement(iPad,MINECRAFT_ACTION_DPAD_UP ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_DPAD_DOWN*}", GetActionReplacement(iPad,MINECRAFT_ACTION_DPAD_DOWN ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_DPAD_RIGHT*}", GetActionReplacement(iPad,MINECRAFT_ACTION_DPAD_RIGHT ) ); + text = replaceAll(text, L"{*CONTROLLER_ACTION_DPAD_LEFT*}", GetActionReplacement(iPad,MINECRAFT_ACTION_DPAD_LEFT ) ); +#if defined _XBOX_ONE || defined __PSVITA__ + text = replaceAll(text, L"{*CONTROLLER_VK_START*}", GetVKReplacement(VK_PAD_START ) ); + text = replaceAll(text, L"{*CONTROLLER_VK_BACK*}", GetVKReplacement(VK_PAD_BACK ) ); +#endif + +#ifdef _XBOX + wstring imageRoot = L""; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + imageRoot = pMinecraft->skins->getSelected()->getXuiRootPath(); + + text = replaceAll(text, L"{*IMAGEROOT*}", imageRoot); +#endif // _XBOX + + // Fix for #8903 - UI: Localization: KOR/JPN/CHT: Button Icons are rendered with padding space, which looks no good + DWORD dwLanguage = XGetLanguage( ); + switch(dwLanguage) + { + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + text = replaceAll(text, L" ", L"" ); + break; + } + + return text; +} + +wstring CMinecraftApp::GetActionReplacement(int iPad, unsigned char ucAction) +{ + unsigned int input = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal(iPad) ,ucAction); + +#ifdef _XBOX + switch(input) + { + case _360_JOY_BUTTON_A: + return app.GetString( IDS_CONTROLLER_A ); + case _360_JOY_BUTTON_B: + return app.GetString( IDS_CONTROLLER_B ); + case _360_JOY_BUTTON_X: + return app.GetString( IDS_CONTROLLER_X ); + case _360_JOY_BUTTON_Y: + return app.GetString( IDS_CONTROLLER_Y ); + case _360_JOY_BUTTON_LSTICK_UP: + case _360_JOY_BUTTON_LSTICK_DOWN: + case _360_JOY_BUTTON_LSTICK_LEFT: + case _360_JOY_BUTTON_LSTICK_RIGHT: + return app.GetString( IDS_CONTROLLER_LEFT_STICK ); + case _360_JOY_BUTTON_RSTICK_LEFT: + case _360_JOY_BUTTON_RSTICK_RIGHT: + case _360_JOY_BUTTON_RSTICK_UP: + case _360_JOY_BUTTON_RSTICK_DOWN: + return app.GetString( IDS_CONTROLLER_RIGHT_STICK ); + case _360_JOY_BUTTON_LT: + return app.GetString( IDS_CONTROLLER_LEFT_TRIGGER ); + case _360_JOY_BUTTON_RT: + return app.GetString( IDS_CONTROLLER_RIGHT_TRIGGER ); + case _360_JOY_BUTTON_RB: + return app.GetString( IDS_CONTROLLER_RIGHT_BUMPER ); + case _360_JOY_BUTTON_LB: + return app.GetString( IDS_CONTROLLER_LEFT_BUMPER ); + case _360_JOY_BUTTON_BACK: + return app.GetString( IDS_CONTROLLER_BACK ); + case _360_JOY_BUTTON_START: + return app.GetString( IDS_CONTROLLER_START ); + case _360_JOY_BUTTON_RTHUMB: + return app.GetString( IDS_CONTROLLER_RIGHT_THUMBSTICK ); + case _360_JOY_BUTTON_LTHUMB: + return app.GetString( IDS_CONTROLLER_LEFT_THUMBSTICK ); + case _360_JOY_BUTTON_DPAD_LEFT: + return app.GetString( IDS_CONTROLLER_DPAD_L ); + case _360_JOY_BUTTON_DPAD_RIGHT: + return app.GetString( IDS_CONTROLLER_DPAD_R ); + case _360_JOY_BUTTON_DPAD_UP: + return app.GetString( IDS_CONTROLLER_DPAD_U ); + case _360_JOY_BUTTON_DPAD_DOWN: + return app.GetString( IDS_CONTROLLER_DPAD_D ); + }; + return L""; +#else + wstring replacement = L""; + + // 4J Stu - Some of our actions can be mapped to multiple physical buttons, so replaces the switch that was here + if (input & _360_JOY_BUTTON_A) replacement = L"ButtonA"; + else if(input &_360_JOY_BUTTON_B) replacement = L"ButtonB"; + else if(input &_360_JOY_BUTTON_X) replacement = L"ButtonX"; + else if(input &_360_JOY_BUTTON_Y) replacement = L"ButtonY"; + else if( + (input &_360_JOY_BUTTON_LSTICK_UP) || + (input &_360_JOY_BUTTON_LSTICK_DOWN) || + (input &_360_JOY_BUTTON_LSTICK_LEFT) || + (input &_360_JOY_BUTTON_LSTICK_RIGHT) + ) + { + replacement = L"ButtonLeftStick"; + } + else if( + (input &_360_JOY_BUTTON_RSTICK_LEFT) || + (input &_360_JOY_BUTTON_RSTICK_RIGHT) || + (input &_360_JOY_BUTTON_RSTICK_UP) || + (input &_360_JOY_BUTTON_RSTICK_DOWN) + ) + { + replacement = L"ButtonRightStick"; + } + else if(input &_360_JOY_BUTTON_DPAD_LEFT) replacement = L"ButtonDpadL"; + else if(input &_360_JOY_BUTTON_DPAD_RIGHT) replacement = L"ButtonDpadR"; + else if(input &_360_JOY_BUTTON_DPAD_UP) replacement = L"ButtonDpadU"; + else if(input &_360_JOY_BUTTON_DPAD_DOWN) replacement = L"ButtonDpadD"; + else if(input &_360_JOY_BUTTON_LT) replacement = L"ButtonLeftTrigger"; + else if(input &_360_JOY_BUTTON_RT) replacement = L"ButtonRightTrigger"; + else if(input &_360_JOY_BUTTON_RB) replacement = L"ButtonRightBumper"; + else if(input &_360_JOY_BUTTON_LB) replacement = L"ButtonLeftBumper"; + else if(input &_360_JOY_BUTTON_BACK) replacement = L"ButtonBack"; + else if(input &_360_JOY_BUTTON_START) replacement = L"ButtonStart"; + else if(input &_360_JOY_BUTTON_RTHUMB) replacement = L"ButtonRS"; + else if(input &_360_JOY_BUTTON_LTHUMB) replacement = L"ButtonLS"; + + wchar_t string[128]; + +#ifdef __PS3__ + int size = 30; +#elif defined _WIN64 + int size = 45; + if(ui.getScreenWidth() < 1920) size = 30; +#else + int size = 45; +#endif + + swprintf(string,128,L"", replacement.c_str(), size, size); + + return string; +#endif +} + +wstring CMinecraftApp::GetVKReplacement(unsigned int uiVKey) +{ +#ifdef _XBOX + switch(uiVKey) + { + case VK_PAD_A: + return app.GetString( IDS_CONTROLLER_A ); + case VK_PAD_B: + return app.GetString( IDS_CONTROLLER_B ); + case VK_PAD_X: + return app.GetString( IDS_CONTROLLER_X ); + case VK_PAD_Y: + return app.GetString( IDS_CONTROLLER_Y ); + case VK_PAD_LSHOULDER: + return app.GetString( IDS_CONTROLLER_LEFT_BUMPER ); + case VK_PAD_RSHOULDER: + return app.GetString( IDS_CONTROLLER_RIGHT_BUMPER ); + case VK_PAD_LTRIGGER: + return app.GetString( IDS_CONTROLLER_LEFT_TRIGGER ); + case VK_PAD_RTRIGGER: + return app.GetString( IDS_CONTROLLER_RIGHT_TRIGGER ); + case VK_PAD_LTHUMB_UP : + case VK_PAD_LTHUMB_DOWN : + case VK_PAD_LTHUMB_RIGHT : + case VK_PAD_LTHUMB_LEFT : + case VK_PAD_LTHUMB_UPLEFT : + case VK_PAD_LTHUMB_UPRIGHT : + case VK_PAD_LTHUMB_DOWNRIGHT: + case VK_PAD_LTHUMB_DOWNLEFT : + return app.GetString( IDS_CONTROLLER_LEFT_STICK ); + case VK_PAD_RTHUMB_UP : + case VK_PAD_RTHUMB_DOWN : + case VK_PAD_RTHUMB_RIGHT : + case VK_PAD_RTHUMB_LEFT : + case VK_PAD_RTHUMB_UPLEFT : + case VK_PAD_RTHUMB_UPRIGHT : + case VK_PAD_RTHUMB_DOWNRIGHT: + case VK_PAD_RTHUMB_DOWNLEFT : + return app.GetString( IDS_CONTROLLER_RIGHT_STICK ); + default: + break; + } + return NULL; +#else + wstring replacement = L""; + switch(uiVKey) + { + case VK_PAD_A: +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + if( InputManager.IsCircleCrossSwapped() ) replacement = L"ButtonB"; + else replacement = L"ButtonA"; +#else + replacement = L"ButtonA"; +#endif + break; + case VK_PAD_B: +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + if( InputManager.IsCircleCrossSwapped() ) replacement = L"ButtonA"; + else replacement = L"ButtonB"; +#else + replacement = L"ButtonB"; +#endif + break; + case VK_PAD_X: + replacement = L"ButtonX"; + break; + case VK_PAD_Y: + replacement = L"ButtonY"; + break; + case VK_PAD_LSHOULDER: + replacement = L"ButtonLeftBumper"; + break; + case VK_PAD_RSHOULDER: + replacement = L"ButtonRightBumper"; + break; + case VK_PAD_LTRIGGER: + replacement = L"ButtonLeftTrigger"; + break; + case VK_PAD_RTRIGGER: + replacement = L"ButtonRightTrigger"; + break; + case VK_PAD_LTHUMB_UP : + case VK_PAD_LTHUMB_DOWN : + case VK_PAD_LTHUMB_RIGHT : + case VK_PAD_LTHUMB_LEFT : + case VK_PAD_LTHUMB_UPLEFT : + case VK_PAD_LTHUMB_UPRIGHT : + case VK_PAD_LTHUMB_DOWNRIGHT: + case VK_PAD_LTHUMB_DOWNLEFT : + replacement = L"ButtonLeftStick"; + break; + case VK_PAD_RTHUMB_UP : + case VK_PAD_RTHUMB_DOWN : + case VK_PAD_RTHUMB_RIGHT : + case VK_PAD_RTHUMB_LEFT : + case VK_PAD_RTHUMB_UPLEFT : + case VK_PAD_RTHUMB_UPRIGHT : + case VK_PAD_RTHUMB_DOWNRIGHT: + case VK_PAD_RTHUMB_DOWNLEFT : + replacement = L"ButtonRightStick"; + break; +#if defined _XBOX_ONE || defined __PSVITA__ + case VK_PAD_START: + replacement = L"ButtonStart"; + break; + case VK_PAD_BACK: + replacement = L"ButtonBack"; + break; +#endif + default: + break; + } + wchar_t string[128]; + +#ifdef __PS3__ + int size = 30; +#elif defined _WIN64 + int size = 45; + if(ui.getScreenWidth() < 1920) size = 30; +#else + int size = 45; +#endif + + swprintf(string,128,L"", replacement.c_str(), size, size); + + return string; +#endif +} + +wstring CMinecraftApp::GetIconReplacement(unsigned int uiIcon) +{ +#ifdef _XBOX + switch(uiIcon) + { + case XZP_ICON_SHANK_01: + return app.GetString( IDS_ICON_SHANK_01 ); + case XZP_ICON_SHANK_03: + return app.GetString( IDS_ICON_SHANK_03 ); + default: + break; + } + return NULL; +#else + wchar_t string[128]; + +#ifdef __PS3__ + int size = 22; +#elif defined _WIN64 + int size = 33; + if(ui.getScreenWidth() < 1920) size = 22; +#else + int size = 33; +#endif + + swprintf(string,128,L"", size, size); + wstring result = L""; + switch(uiIcon) + { + case XZP_ICON_SHANK_01: + result = string; + break; + case XZP_ICON_SHANK_03: + result.append(string).append(string).append(string); + break; + default: + break; + } + return result; +#endif +} + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) +unordered_map CMinecraftApp::MojangData; +unordered_map CMinecraftApp::DLCTextures_PackID; +unordered_map CMinecraftApp::DLCInfo; +unordered_map CMinecraftApp::DLCInfo_SkinName; +#elif defined(_DURANGO) +unordered_map CMinecraftApp::MojangData; +unordered_map CMinecraftApp::DLCTextures_PackID; // for mash-up packs & texture packs +//unordered_map CMinecraftApp::DLCInfo_Trial; // full offerid, dlc_info +unordered_map CMinecraftApp::DLCInfo_Full; // full offerid, dlc_info +unordered_map CMinecraftApp::DLCInfo_SkinName; // skin name, full offer id +#else +unordered_map CMinecraftApp::MojangData; +unordered_map CMinecraftApp::DLCTextures_PackID; +unordered_map CMinecraftApp::DLCInfo_Trial; +unordered_map CMinecraftApp::DLCInfo_Full; +unordered_map CMinecraftApp::DLCInfo_SkinName; +#endif + + + +HRESULT CMinecraftApp::RegisterMojangData(WCHAR *pXuidName, PlayerUID xuid, WCHAR *pSkin, WCHAR *pCape) +{ + HRESULT hr=S_OK; + eXUID eTempXuid=eXUID_Undefined; + MOJANG_DATA *pMojangData=NULL; + + // ignore the names if we don't recognize them + if(pXuidName!=NULL) + { + if( wcscmp( pXuidName, L"XUID_NOTCH" ) == 0 ) + { + eTempXuid = eXUID_Notch; // might be needed for the apple at some point + } + else if( wcscmp( pXuidName, L"XUID_DEADMAU5" ) == 0 ) + { + eTempXuid = eXUID_Deadmau5; // Needed for the deadmau5 ears + } + else + { + eTempXuid=eXUID_NoName; + } + } + + if(eTempXuid!=eXUID_Undefined) + { + pMojangData = new MOJANG_DATA; + ZeroMemory(pMojangData,sizeof(MOJANG_DATA)); + pMojangData->eXuid=eTempXuid; + + wcsncpy( pMojangData->wchSkin, pSkin, MAX_CAPENAME_SIZE); + wcsncpy( pMojangData->wchCape, pCape, MAX_CAPENAME_SIZE); + MojangData[xuid]=pMojangData; + } + + return hr; +} + +MOJANG_DATA *CMinecraftApp::GetMojangDataForXuid(PlayerUID xuid) +{ + return MojangData[xuid]; +} + +HRESULT CMinecraftApp::RegisterConfigValues(WCHAR *pType, int iValue) +{ + HRESULT hr=S_OK; + +// #ifdef _XBOX +// if(pType!=NULL) +// { +// if(wcscmp(pType,L"XboxOneTransfer")==0) +// { +// if(iValue>0) +// { +// app.m_bTransferSavesToXboxOne=true; +// } +// else +// { +// app.m_bTransferSavesToXboxOne=false; +// } +// } +// else if(wcscmp(pType,L"TransferSlotCount")==0) +// { +// app.m_uiTransferSlotC=iValue; +// } +// +// } +// #endif + + + return hr; +} + +#if (defined _XBOX || defined _WINDOWS64) +HRESULT CMinecraftApp::RegisterDLCData(WCHAR *pType, WCHAR *pBannerName, int iGender, __uint64 ullOfferID_Full, __uint64 ullOfferID_Trial, WCHAR *pFirstSkin, unsigned int uiSortIndex, int iConfig, WCHAR *pDataFile) +{ + HRESULT hr=S_OK; + DLC_INFO *pDLCData=new DLC_INFO; + ZeroMemory(pDLCData,sizeof(DLC_INFO)); + pDLCData->ullOfferID_Full=ullOfferID_Full; + pDLCData->ullOfferID_Trial=ullOfferID_Trial; + pDLCData->eDLCType=e_DLC_NotDefined; + pDLCData->iGender=iGender; + pDLCData->uiSortIndex=uiSortIndex; + pDLCData->iConfig=iConfig; + +#ifndef __ORBIS__ + // ignore the names if we don't recognize them + if(pBannerName!=L"") + { + wcsncpy_s( pDLCData->wchBanner, pBannerName, MAX_BANNERNAME_SIZE); + } + + if(pDataFile[0]!=0) + { + wcsncpy_s( pDLCData->wchDataFile, pDataFile, MAX_BANNERNAME_SIZE); + } +#endif + + if(pType!=NULL) + { + if(wcscmp(pType,L"Skin")==0) + { + pDLCData->eDLCType=e_DLC_SkinPack; + } + else if(wcscmp(pType,L"Gamerpic")==0) + { + pDLCData->eDLCType=e_DLC_Gamerpics; + } + else if(wcscmp(pType,L"Theme")==0) + { + pDLCData->eDLCType=e_DLC_Themes; + } + else if(wcscmp(pType,L"Avatar")==0) + { + pDLCData->eDLCType=e_DLC_AvatarItems; + } + else if(wcscmp(pType,L"MashUpPack")==0) + { + pDLCData->eDLCType=e_DLC_MashupPacks; + DLCTextures_PackID[pDLCData->iConfig]=ullOfferID_Full; + } + else if(wcscmp(pType,L"TexturePack")==0) + { + pDLCData->eDLCType=e_DLC_TexturePacks; + DLCTextures_PackID[pDLCData->iConfig]=ullOfferID_Full; + } + + + } + + if(ullOfferID_Trial!=0ll) DLCInfo_Trial[ullOfferID_Trial]=pDLCData; + if(ullOfferID_Full!=0ll) DLCInfo_Full[ullOfferID_Full]=pDLCData; + if(pFirstSkin[0]!=0) DLCInfo_SkinName[pFirstSkin]=ullOfferID_Full; + + return hr; +} +#elif defined _XBOX_ONE + +unordered_map *CMinecraftApp::GetDLCInfo() +{ + return &DLCInfo_Full; +} + +HRESULT CMinecraftApp::RegisterDLCData(eDLCContentType eType, WCHAR *pwchBannerName,WCHAR *pwchProductId, WCHAR *pwchProductName, WCHAR *pwchFirstSkin, int iConfig, unsigned int uiSortIndex) +{ + HRESULT hr=S_OK; + // 4J-PB - need to convert the product id to uppercase because the catalog calls come back with upper case + WCHAR wchUppercaseProductID[64]; + if(pwchProductId[0]!=0) + { + for(int i=0;i<64;i++) + { + wchUppercaseProductID[i]=towupper((wchar_t)pwchProductId[i]); + } + } + + // check if we already have this info from the local DLC file + wstring wsTemp=wchUppercaseProductID; + + AUTO_VAR(it, DLCInfo_Full.find(wsTemp)); + if( it == DLCInfo_Full.end() ) + { + // Not found + + DLC_INFO *pDLCData=new DLC_INFO; + ZeroMemory(pDLCData,sizeof(DLC_INFO)); + + pDLCData->eDLCType=e_DLC_NotDefined; + pDLCData->uiSortIndex=uiSortIndex; + pDLCData->iConfig=iConfig; + + if(pwchProductId[0]!=0) + { + pDLCData->wsProductId=wchUppercaseProductID; + } + + // ignore the names if we don't recognize them + if(pwchBannerName!=L"") + { + wcsncpy_s( pDLCData->wchBanner, pwchBannerName, MAX_BANNERNAME_SIZE); + } + + if(pwchProductName[0]!=0) + { + pDLCData->wsDisplayName=pwchProductName; + } + + pDLCData->eDLCType=eType; + + switch(eType) + { + case e_DLC_MashupPacks: + case e_DLC_TexturePacks: + DLCTextures_PackID[iConfig]=pDLCData->wsProductId; + break; + } + + if(pwchFirstSkin[0]!=0) DLCInfo_SkinName[pwchFirstSkin]=pDLCData->wsProductId; + + #ifdef _XBOX_ONE + // ignore the names, and use the product id instead + DLCInfo_Full[pDLCData->wsProductId]=pDLCData; + #else + DLCInfo_Full[pDLCData->wsDisplayName]=pDLCData; + #endif + } + app.DebugPrintf("DLCInfo - type - %d, productID - %ls, name - %ls , banner - %ls, iconfig - %d, sort index - %d\n",eType,pwchProductId, pwchProductName,pwchBannerName, iConfig, uiSortIndex); + return hr; +} +#else + +HRESULT CMinecraftApp::RegisterDLCData(char *pchDLCName, unsigned int uiSortIndex,char *pchImageURL) +{ + // on PS3 we get all the required info from the name + char chDLCType[3]; + HRESULT hr=S_OK; + DLC_INFO *pDLCData=new DLC_INFO; + ZeroMemory(pDLCData,sizeof(DLC_INFO)); + + chDLCType[0]=pchDLCName[0]; + chDLCType[1]=pchDLCName[1]; + chDLCType[2]=0; + + pDLCData->iConfig = app.GetiConfigFromName(pchDLCName); + pDLCData->uiSortIndex=uiSortIndex; + pDLCData->eDLCType = app.GetDLCTypeFromName(pchDLCName); + strcpy(pDLCData->chImageURL,pchImageURL); + //bool bIsTrialDLC = app.GetTrialFromName(pchDLCName); + + switch(pDLCData->eDLCType) + { + case e_DLC_TexturePacks: + { + char *pchName=(char *)malloc(strlen(pchDLCName)+1); + strcpy(pchName,pchDLCName); + DLCTextures_PackID[pDLCData->iConfig]=pchName; + } + break; + case e_DLC_MashupPacks: + { + char *pchName=(char *)malloc(strlen(pchDLCName)+1); + strcpy(pchName,pchDLCName); + DLCTextures_PackID[pDLCData->iConfig]=pchName; + } + break; + default: + break; + } + + app.DebugPrintf(5,"Adding DLC - %s\n",pchDLCName); + DLCInfo[pchDLCName]=pDLCData; + +// if(ullOfferID_Trial!=0ll) DLCInfo_Trial[ullOfferID_Trial]=pDLCData; +// if(ullOfferID_Full!=0ll) DLCInfo_Full[ullOfferID_Full]=pDLCData; +// if(pFirstSkin[0]!=0) DLCInfo_SkinName[pFirstSkin]=ullOfferID_Full; + +// DLCInfo[ullOfferID_Trial]=pDLCData; + + return hr; +} +#endif + + + +#if defined( __PS3__) || defined(__ORBIS__) || defined(__PSVITA__) +bool CMinecraftApp::GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,ULONGLONG *pullVal) +{ + AUTO_VAR(it, DLCInfo_SkinName.find(FirstSkin)); + if( it == DLCInfo_SkinName.end() ) + { + return false; + } + else + { + *pullVal=(ULONGLONG)it->second; + return true; + } +} +bool CMinecraftApp::GetDLCNameForPackID(const int iPackID,char **ppchKeyID) +{ + AUTO_VAR(it, DLCTextures_PackID.find(iPackID)); + if( it == DLCTextures_PackID.end() ) + { + *ppchKeyID=NULL; + return false; + } + else + { + *ppchKeyID=(char *)it->second; + return true; + } +} +DLC_INFO *CMinecraftApp::GetDLCInfo(char *pchDLCName) +{ + string tempString=pchDLCName; + + if(DLCInfo.size()>0) + { + AUTO_VAR(it, DLCInfo.find(tempString)); + + if( it == DLCInfo.end() ) + { + // nothing for this + return NULL; + } + else + { + return it->second; + } + } + else return NULL; +} + +DLC_INFO *CMinecraftApp::GetDLCInfoFromTPackID(int iTPID) +{ + unordered_map::iterator it= DLCInfo.begin(); + + for(int i=0;isecond)->iConfig==iTPID) + { + return it->second; + } + ++it; + } + return NULL; +} + +DLC_INFO *CMinecraftApp::GetDLCInfo(int iIndex) +{ + unordered_map::iterator it= DLCInfo.begin(); + + for(int i=0;isecond; +} + +char *CMinecraftApp::GetDLCInfoTextures(int iIndex) +{ + unordered_map::iterator it= DLCTextures_PackID.begin(); + + for(int i=0;isecond; +} + +#elif defined _XBOX_ONE +bool CMinecraftApp::GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,wstring &ProductId) +{ + AUTO_VAR(it, DLCInfo_SkinName.find(FirstSkin)); + if( it == DLCInfo_SkinName.end() ) + { + return false; + } + else + { + ProductId=it->second; + return true; + } +} +bool CMinecraftApp::GetDLCFullOfferIDForPackID(const int iPackID,wstring &ProductId) +{ + AUTO_VAR(it, DLCTextures_PackID.find(iPackID)); + if( it == DLCTextures_PackID.end() ) + { + return false; + } + else + { + ProductId=it->second; + return true; + } +} +// DLC_INFO *CMinecraftApp::GetDLCInfoForTrialOfferID(wstring &ProductId) +// { +// return NULL; +// } + +DLC_INFO *CMinecraftApp::GetDLCInfoTrialOffer(int iIndex) +{ + return NULL; +} +DLC_INFO *CMinecraftApp::GetDLCInfoFullOffer(int iIndex) +{ + unordered_map::iterator it= DLCInfo_Full.begin(); + + for(int i=0;isecond; +} +wstring CMinecraftApp::GetDLCInfoTexturesFullOffer(int iIndex) +{ + unordered_map::iterator it= DLCTextures_PackID.begin(); + + for(int i=0;isecond; +} +#else +bool CMinecraftApp::GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,ULONGLONG *pullVal) +{ + AUTO_VAR(it, DLCInfo_SkinName.find(FirstSkin)); + if( it == DLCInfo_SkinName.end() ) + { + return false; + } + else + { + *pullVal=(ULONGLONG)it->second; + return true; + } +} +bool CMinecraftApp::GetDLCFullOfferIDForPackID(const int iPackID,ULONGLONG *pullVal) +{ + AUTO_VAR(it, DLCTextures_PackID.find(iPackID)); + if( it == DLCTextures_PackID.end() ) + { + *pullVal=(ULONGLONG)0; + return false; + } + else + { + *pullVal=(ULONGLONG)it->second; + return true; + } +} +DLC_INFO *CMinecraftApp::GetDLCInfoForTrialOfferID(ULONGLONG ullOfferID_Trial) +{ + //DLC_INFO *pDLCInfo=NULL; + if(DLCInfo_Trial.size()>0) + { + AUTO_VAR(it, DLCInfo_Trial.find(ullOfferID_Trial)); + + if( it == DLCInfo_Trial.end() ) + { + // nothing for this + return NULL; + } + else + { + return it->second; + } + } + else return NULL; +} + +DLC_INFO *CMinecraftApp::GetDLCInfoTrialOffer(int iIndex) +{ + unordered_map::iterator it= DLCInfo_Trial.begin(); + + for(int i=0;isecond; +} +DLC_INFO *CMinecraftApp::GetDLCInfoFullOffer(int iIndex) +{ + unordered_map::iterator it= DLCInfo_Full.begin(); + + for(int i=0;isecond; +} +ULONGLONG CMinecraftApp::GetDLCInfoTexturesFullOffer(int iIndex) +{ + unordered_map::iterator it= DLCTextures_PackID.begin(); + + for(int i=0;isecond; +} +#endif + +#ifdef _XBOX_ONE + +DLC_INFO *CMinecraftApp::GetDLCInfoForFullOfferID(WCHAR *pwchProductID) +{ + wstring wsTemp = pwchProductID; + if(DLCInfo_Full.size()>0) + { + AUTO_VAR(it, DLCInfo_Full.find(wsTemp)); + + if( it == DLCInfo_Full.end() ) + { + // nothing for this + return NULL; + } + else + { + return it->second; + } + } + else return NULL; +} +DLC_INFO *CMinecraftApp::GetDLCInfoForProductName(WCHAR *pwchProductName) +{ + unordered_map::iterator it= DLCInfo_Full.begin(); + wstring wsProductName=pwchProductName; + + for(int i=0;isecond; + if(wsProductName==pDLCInfo->wsDisplayName) + { + return pDLCInfo; + } + ++it; + } + + return NULL; +} + +#elif defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) +#else + +DLC_INFO *CMinecraftApp::GetDLCInfoForFullOfferID(ULONGLONG ullOfferID_Full) +{ + + if(DLCInfo_Full.size()>0) + { + AUTO_VAR(it, DLCInfo_Full.find(ullOfferID_Full)); + + if( it == DLCInfo_Full.end() ) + { + // nothing for this + return NULL; + } + else + { + return it->second; + } + } + else return NULL; +} +#endif + +void CMinecraftApp::EnterSaveNotificationSection() +{ + EnterCriticalSection(&m_saveNotificationCriticalSection); + if( m_saveNotificationDepth++ == 0 ) + { + MinecraftServer::getInstance()->broadcastStartSavingPacket(); + + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); + } + } + LeaveCriticalSection(&m_saveNotificationCriticalSection); +} + +void CMinecraftApp::LeaveSaveNotificationSection() +{ + EnterCriticalSection(&m_saveNotificationCriticalSection); + if( --m_saveNotificationDepth == 0 ) + { + MinecraftServer::getInstance()->broadcastStopSavingPacket(); + + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + } + LeaveCriticalSection(&m_saveNotificationCriticalSection); +} + + +int CMinecraftApp::RemoteSaveThreadProc( void* lpParameter ) +{ + // The game should be stopped while we are doing this, but the connections ticks may try to create some AABB's or Vec3's + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + // 4J-PB - Xbox 360 - 163153 - [CRASH] TU17: Code: Multiplayer: During the Autosave in an online Multiplayer session, the game occasionally crashes for one or more Clients + // callstack - > if(tls->tileId != this->id) updateDefaultShape(); + // callstack - > default.exe!WaterlilyTile::getAABB(Level * level, int x, int y, int z) line 38 + 8 bytes C++ + // ... + // default.exe!CMinecraftApp::RemoteSaveThreadProc(void * lpParameter) line 6694 C++ + // host autosave, and the clients can crash on receiving handleMoveEntity when it's a tile within this thread, so need to do the tls for tiles + Tile::CreateNewThreadStorage(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_HOST_SAVING ); + pMinecraft->progressRenderer->progressStage( -1 ); + pMinecraft->progressRenderer->progressStagePercentage(0); + + while( !app.GetGameStarted() && app.GetXuiAction( ProfileManager.GetPrimaryPad() ) == eAppAction_WaitRemoteServerSaveComplete ) + { + // Tick all the games connections + pMinecraft->tickAllConnections(); + Sleep( 100 ); + } + + if( app.GetXuiAction( ProfileManager.GetPrimaryPad() ) != eAppAction_WaitRemoteServerSaveComplete ) + { + // Something cancelled us? + return ERROR_CANCELLED; + } + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_Idle); + + ui.UpdatePlayerBasePositions(); + + Tile::ReleaseThreadStorage(); + + return S_OK; +} + +void CMinecraftApp::ExitGameFromRemoteSave( LPVOID lpParameter ) +{ + int primaryPad = ProfileManager.GetPrimaryPad(); + + UINT uiIDA[3]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, primaryPad,&CMinecraftApp::ExitGameFromRemoteSaveDialogReturned,NULL, app.GetStringTable(), 0, 0, false); +} + +int CMinecraftApp::ExitGameFromRemoteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //CScene_Pause* pClass = (CScene_Pause*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + app.SetAction(iPad,eAppAction_ExitWorld); + } + else + { +#ifndef _XBOX + // Inform fullscreen progress scene that it's not being cancelled after all + UIScene_FullscreenProgress *pScene = (UIScene_FullscreenProgress *)ui.FindScene(eUIScene_FullscreenProgress); +#ifdef __PS3__ + if(pScene!=NULL) +#else + if (pScene != nullptr) +#endif + { + pScene->SetWasCancelled(false); + } +#else + // Don't have to worry about this on Xbox +#endif + } + return 0; +} + +void CMinecraftApp::SetSpecialTutorialCompletionFlag(int iPad, int index) +{ + if(index >= 0 && index < 32 && GameSettingsA[iPad] != NULL) + { + GameSettingsA[iPad]->uiSpecialTutorialBitmask |= (1<clear(); + + if(BannedListA[iPad].pBannedList) + { + delete [] BannedListA[iPad].pBannedList; + BannedListA[iPad].pBannedList=NULL; + } + } +} + +#ifdef _XBOX_ONE +void CMinecraftApp::AddLevelToBannedLevelList(int iPad, PBANNEDLISTDATA pBannedListData, bool bWriteToTMS) +{ + PlayerUID xuid= pBannedListData->wchPlayerUID; + + AddLevelToBannedLevelList(iPad,xuid,pBannedListData->pszLevelName,bWriteToTMS); +} +#endif + +void CMinecraftApp::AddLevelToBannedLevelList(int iPad, PlayerUID xuid, char *pszLevelName, bool bWriteToTMS) +{ + // we will have retrieved the banned level list from TMS, so add this one to it and write it back to TMS + + BANNEDLISTDATA *pBannedListData = new BANNEDLISTDATA; + memset(pBannedListData,0,sizeof(BANNEDLISTDATA)); + +#ifdef _DURANGO + memcpy(&pBannedListData->wchPlayerUID, xuid.toString().c_str(), sizeof(WCHAR)*64); +#else + memcpy(&pBannedListData->xuid, &xuid, sizeof(PlayerUID)); +#endif + strcpy(pBannedListData->pszLevelName,pszLevelName); + m_vBannedListA[iPad]->push_back(pBannedListData); + + if(bWriteToTMS) + { + DWORD dwDataBytes=(DWORD)(sizeof(BANNEDLISTDATA)*m_vBannedListA[iPad]->size()); + PBANNEDLISTDATA pBannedList = (BANNEDLISTDATA *)(new CHAR [dwDataBytes]); + int iCount=0; + for(AUTO_VAR(it, m_vBannedListA[iPad]->begin()); it != m_vBannedListA[iPad]->end(); ++it) + { + PBANNEDLISTDATA pData=*it; + memcpy(&pBannedList[iCount++],pData,sizeof(BANNEDLISTDATA)); + } + + // 4J-PB - write to TMS++ now + + //bool bRes=StorageManager.WriteTMSFile(iPad,C4JStorage::eGlobalStorage_TitleUser,L"BannedList",(PBYTE)pBannedList, dwDataBytes); +#ifdef _XBOX + StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,C4JStorage::TMS_UGCTYPE_NONE,"BannedList",(PCHAR) pBannedList, dwDataBytes,NULL,NULL, 0); +#elif defined _XBOX_ONE + StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"BannedList",(PBYTE) pBannedList, dwDataBytes,NULL,NULL, 0); +#endif + } + // update telemetry too +} + +bool CMinecraftApp::IsInBannedLevelList(int iPad, PlayerUID xuid, char *pszLevelName) +{ + for(AUTO_VAR(it, m_vBannedListA[iPad]->begin()); it != m_vBannedListA[iPad]->end(); ++it) + { + PBANNEDLISTDATA pData=*it; +#ifdef _XBOX_ONE + PlayerUID bannedPlayerUID = pData->wchPlayerUID; + if(IsEqualXUID (bannedPlayerUID,xuid) && (strcmp(pData->pszLevelName,pszLevelName)==0)) +#else + if(IsEqualXUID (pData->xuid,xuid) && (strcmp(pData->pszLevelName,pszLevelName)==0)) +#endif + { + return true; + } + } + + return false; +} + +void CMinecraftApp::RemoveLevelFromBannedLevelList(int iPad, PlayerUID xuid, char *pszLevelName) +{ + //bool bFound=false; + //bool bRes; + + // we will have retrieved the banned level list from TMS, so remove this one from it and write it back to TMS + for(AUTO_VAR(it, m_vBannedListA[iPad]->begin()); it != m_vBannedListA[iPad]->end(); ) + { + PBANNEDLISTDATA pBannedListData = *it; + + if(pBannedListData!=NULL) + { +#ifdef _XBOX_ONE + PlayerUID bannedPlayerUID = pBannedListData->wchPlayerUID; + if(IsEqualXUID (bannedPlayerUID,xuid) && (strcmp(pBannedListData->pszLevelName,pszLevelName)==0)) +#else + if(IsEqualXUID (pBannedListData->xuid,xuid) && (strcmp(pBannedListData->pszLevelName,pszLevelName)==0)) +#endif + { + TelemetryManager->RecordUnBanLevel(iPad); + + // match found, so remove this entry + it = m_vBannedListA[iPad]->erase(it); + } + else + { + ++it; + } + } + else + { + ++it; + } + } + + DWORD dwDataBytes=(DWORD)(sizeof(BANNEDLISTDATA)*m_vBannedListA[iPad]->size()); + if(dwDataBytes==0) + { + // wipe the file +#ifdef _XBOX + StorageManager.DeleteTMSFile(iPad,C4JStorage::eGlobalStorage_TitleUser,L"BannedList"); +#elif defined _XBOX_ONE + StorageManager.TMSPP_DeleteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"BannedList",NULL,NULL, 0); +#endif + } + else + { + PBANNEDLISTDATA pBannedList = (BANNEDLISTDATA *)(new BYTE [dwDataBytes]); + + int iSize=(int)m_vBannedListA[iPad]->size(); + for(int i=0;iat(i); + + memcpy(&pBannedList[i],pBannedListData,sizeof(BANNEDLISTDATA)); + } +#ifdef _XBOX + StorageManager.WriteTMSFile(iPad,C4JStorage::eGlobalStorage_TitleUser,L"BannedList",(PBYTE)pBannedList, dwDataBytes); +#elif defined _XBOX_ONE + StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"BannedList",(PBYTE) pBannedList, dwDataBytes,NULL,NULL, 0); +#endif + delete [] pBannedList; + } + + // update telemetry too +} + +// function to add credits for the DLC packs +void CMinecraftApp::AddCreditText(LPCWSTR lpStr) +{ + DebugPrintf("ADDING CREDIT - %ls\n",lpStr); + // add a string from the DLC to a credits vector + SCreditTextItemDef *pCreditStruct = new SCreditTextItemDef; + pCreditStruct->m_eType=eSmallText; + pCreditStruct->m_iStringID[0]=NO_TRANSLATED_STRING; + pCreditStruct->m_iStringID[1]=NO_TRANSLATED_STRING; + pCreditStruct->m_Text=new WCHAR [wcslen(lpStr)+1]; + wcscpy((WCHAR *)pCreditStruct->m_Text,lpStr); + + vDLCCredits.push_back(pCreditStruct); +} + +bool CMinecraftApp::AlreadySeenCreditText(const wstring &wstemp) +{ + + for(unsigned int i=0;i>4; + break; + case eGameHostOption_All: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_ALL); + break; + case eGameHostOption_Tutorial: + // special case - tutorial is offline, but we want the gamertag option, and set Easy mode, structures on, fire on, tnt on, pvp on, trust players on + return ((uiHostSettings&GAME_HOST_OPTION_BITMASK_GAMERTAGS)|GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS|GAME_HOST_OPTION_BITMASK_FIRESPREADS|GAME_HOST_OPTION_BITMASK_TNT|GAME_HOST_OPTION_BITMASK_PVP|GAME_HOST_OPTION_BITMASK_STRUCTURES|1); + break; + case eGameHostOption_LevelType: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_LEVELTYPE); + break; + case eGameHostOption_Structures: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_STRUCTURES); + break; + case eGameHostOption_BonusChest: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_BONUSCHEST); + break; + case eGameHostOption_HasBeenInCreative: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_BEENINCREATIVE); + break; + case eGameHostOption_PvP: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_PVP); + break; + case eGameHostOption_TrustPlayers: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_TRUSTPLAYERS); + break; + case eGameHostOption_TNT: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_TNT); + break; + case eGameHostOption_FireSpreads: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_FIRESPREADS); + break; + case eGameHostOption_CheatsEnabled: + return (uiHostSettings&(GAME_HOST_OPTION_BITMASK_HOSTFLY|GAME_HOST_OPTION_BITMASK_HOSTHUNGER|GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE)); + break; + case eGameHostOption_HostCanFly: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_HOSTFLY); + break; + case eGameHostOption_HostCanChangeHunger: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_HOSTHUNGER); + break; + case eGameHostOption_HostCanBeInvisible: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_HOSTINVISIBLE); + break; + case eGameHostOption_BedrockFog: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_BEDROCKFOG); + break; + case eGameHostOption_DisableSaving: + return (uiHostSettings&GAME_HOST_OPTION_BITMASK_DISABLESAVE); + break; + } + + return false; +} + +bool CMinecraftApp::CanRecordStatsAndAchievements() +{ + // 4J Stu - All of these options give the host player some advantage, so should not allow achievements + return !(app.GetGameHostOption(eGameHostOption_HasBeenInCreative) || + app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) || + app.GetGameHostOption(eGameHostOption_HostCanChangeHunger) || + app.GetGameHostOption(eGameHostOption_HostCanFly)); +} + +void CMinecraftApp::processSchematics(LevelChunk *levelChunk) +{ + m_gameRules.processSchematics(levelChunk); +} + +void CMinecraftApp::processSchematicsLighting(LevelChunk *levelChunk) +{ + m_gameRules.processSchematicsLighting(levelChunk); +} + +void CMinecraftApp::loadDefaultGameRules() +{ + m_gameRules.loadDefaultGameRules(); +} + +void CMinecraftApp::setLevelGenerationOptions(LevelGenerationOptions *levelGen) +{ + m_gameRules.setLevelGenerationOptions(levelGen); +} + +LPCWSTR CMinecraftApp::GetGameRulesString(const wstring &key) +{ + return m_gameRules.GetGameRulesString(key); +} + +unsigned char CMinecraftApp::m_szPNG[8]= +{ + 137,80,78,71,13,10,26,10 +}; + +#define PNG_TAG_tEXt 0x74455874 + +unsigned int CMinecraftApp::FromBigEndian(unsigned int uiValue) +{ +#if defined(__PS3__) || defined(_XBOX) + // Keep it in big endian + return uiValue; +#else + unsigned int uiReturn = ( ( uiValue >> 24 ) & 0x000000ff ) | + ( ( uiValue >> 8 ) & 0x0000ff00 ) | + ( ( uiValue << 8 ) & 0x00ff0000 ) | + ( ( uiValue << 24 ) & 0xff000000 ); + return uiReturn; +#endif +} + +void CMinecraftApp::GetImageTextData(PBYTE pbImageData, DWORD dwImageBytes,unsigned char *pszSeed,unsigned int &uiHostOptions,bool &bHostOptionsRead,DWORD &uiTexturePack) +{ + unsigned char *ucPtr=pbImageData; + unsigned int uiCount=0; + unsigned int uiChunkLen; + unsigned int uiChunkType; + unsigned int uiCRC; + char szKeyword[80]; + + // check it's a png + for(int i=0;i<8;i++) + { + if(m_szPNG[i]!=ucPtr[i]) return; + } + + uiCount+=8; + + while(uiCount> std::hex >> uiHostOptions; + } + else if(strcmp(szKeyword,"4J_TEXTUREPACK")==0) + { + // read the texture pack value + unsigned int uiValueC=0; + unsigned char pszTexturePack[8]; // Hex representation of unsigned int + ZeroMemory(&pszTexturePack,8); + while(*pszKeyword!=0 && (pszKeyword < ucPtr + uiCount + uiChunkLen) && uiValueC < 8) + { + pszTexturePack[uiValueC++]=*pszKeyword; + pszKeyword++; + } + + std::stringstream ss; + ss << pszTexturePack; + ss >> std::hex >> uiTexturePack; + } + } + } + uiCount+=uiChunkLen; + uiCRC=*(unsigned int*)&ucPtr[uiCount]; + uiCRC=FromBigEndian(uiCRC); + uiCount+=sizeof(int); + } + + return; +} + +unsigned int CMinecraftApp::CreateImageTextData(PBYTE bTextMetadata, __int64 seed, bool hasSeed, unsigned int uiHostOptions, unsigned int uiTexturePackId) +{ + int iTextMetadataBytes = 0; + if(hasSeed) + { + strcpy((char *)bTextMetadata,"4J_SEED"); + _i64toa_s(seed,(char *)&bTextMetadata[8],42,10); + + // get the length + iTextMetadataBytes+=8; + while(bTextMetadata[iTextMetadataBytes]!=0) iTextMetadataBytes++; + ++iTextMetadataBytes; // Add a null terminator at the end of the seed value + } + + // Save the host options that this world was last played with + strcpy((char *)&bTextMetadata[iTextMetadataBytes],"4J_HOSTOPTIONS"); + _itoa_s(uiHostOptions,(char *)&bTextMetadata[iTextMetadataBytes+15],9,16); + + iTextMetadataBytes += 15; + while(bTextMetadata[iTextMetadataBytes]!=0) iTextMetadataBytes++; + ++iTextMetadataBytes; // Add a null terminator at the end of the host options value + + // Save the texture pack id + strcpy((char *)&bTextMetadata[iTextMetadataBytes],"4J_TEXTUREPACK"); + _itoa_s(uiTexturePackId,(char *)&bTextMetadata[iTextMetadataBytes+15],9,16); + + iTextMetadataBytes += 15; + while(bTextMetadata[iTextMetadataBytes]!=0) iTextMetadataBytes++; + + return iTextMetadataBytes; +} + +void CMinecraftApp::AddTerrainFeaturePosition(_eTerrainFeatureType eFeatureType,int x,int z) +{ + // check we don't already have this in + for(AUTO_VAR(it, m_vTerrainFeatures.begin()); it < m_vTerrainFeatures.end(); ++it) + { + FEATURE_DATA *pFeatureData=*it; + + if((pFeatureData->eTerrainFeature==eFeatureType) &&(pFeatureData->x==x) && (pFeatureData->z==z)) return; + } + + FEATURE_DATA *pFeatureData= new FEATURE_DATA; + pFeatureData->eTerrainFeature=eFeatureType; + pFeatureData->x=x; + pFeatureData->z=z; + + m_vTerrainFeatures.push_back(pFeatureData); +} + +_eTerrainFeatureType CMinecraftApp::IsTerrainFeature(int x,int z) +{ + for(AUTO_VAR(it, m_vTerrainFeatures.begin()); it < m_vTerrainFeatures.end(); ++it) + { + FEATURE_DATA *pFeatureData=*it; + + if((pFeatureData->x==x) && (pFeatureData->z==z)) return pFeatureData->eTerrainFeature; + } + + return eTerrainFeature_None; +} + +bool CMinecraftApp::GetTerrainFeaturePosition(_eTerrainFeatureType eType,int *pX, int *pZ) +{ + for(AUTO_VAR(it, m_vTerrainFeatures.begin()); it < m_vTerrainFeatures.end(); ++it) + { + FEATURE_DATA *pFeatureData=*it; + + if(pFeatureData->eTerrainFeature==eType) + { + *pX=pFeatureData->x; + *pZ=pFeatureData->z; + return true; + } + } + + return false; +} + +void CMinecraftApp::ClearTerrainFeaturePosition() +{ + FEATURE_DATA *pFeatureData; + while(m_vTerrainFeatures.size()>0) + { + pFeatureData = m_vTerrainFeatures.back(); + m_vTerrainFeatures.pop_back(); + delete pFeatureData; + } +} + +void CMinecraftApp::UpdatePlayerInfo(BYTE networkSmallId, SHORT playerColourIndex, unsigned int playerGamePrivileges) +{ + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if(m_playerColours[i]==networkSmallId) + { + m_playerColours[i] = 0; + m_playerGamePrivileges[i] = 0; + } + } + if(playerColourIndex >=0 && playerColourIndex < MINECRAFT_NET_MAX_PLAYERS) + { + m_playerColours[playerColourIndex] = networkSmallId; + m_playerGamePrivileges[playerColourIndex] = playerGamePrivileges; + } +} + +short CMinecraftApp::GetPlayerColour(BYTE networkSmallId) +{ + short index = -1; + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if(m_playerColours[i]==networkSmallId) + { + index = i; + break; + } + } + return index; +} + + +unsigned int CMinecraftApp::GetPlayerPrivileges(BYTE networkSmallId) +{ + unsigned int privileges = 0; + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if(m_playerColours[i]==networkSmallId) + { + privileges = m_playerGamePrivileges[i]; + break; + } + } + return privileges; +} + +wstring CMinecraftApp::getEntityName(eINSTANCEOF type) +{ + switch(type) + { + case eTYPE_WOLF: + return app.GetString(IDS_WOLF); + case eTYPE_CREEPER: + return app.GetString(IDS_CREEPER); + case eTYPE_SKELETON: + return app.GetString(IDS_SKELETON); + case eTYPE_SPIDER: + return app.GetString(IDS_SPIDER); + case eTYPE_ZOMBIE: + return app.GetString(IDS_ZOMBIE); + case eTYPE_PIGZOMBIE: + return app.GetString(IDS_PIGZOMBIE); + case eTYPE_ENDERMAN: + return app.GetString(IDS_ENDERMAN); + case eTYPE_SILVERFISH: + return app.GetString(IDS_SILVERFISH); + case eTYPE_CAVESPIDER: + return app.GetString(IDS_CAVE_SPIDER); + case eTYPE_GHAST: + return app.GetString(IDS_GHAST); + case eTYPE_SLIME: + return app.GetString(IDS_SLIME); + case eTYPE_ARROW: + return app.GetString(IDS_ITEM_ARROW); + case eTYPE_ENDERDRAGON: + return app.GetString(IDS_ENDERDRAGON); + case eTYPE_BLAZE: + return app.GetString(IDS_BLAZE); + case eTYPE_LAVASLIME: + return app.GetString(IDS_LAVA_SLIME); + // 4J-PB - fix for #107167 - Customer Encountered: TU12: Content: UI: There is no information what killed Player after being slain by Iron Golem. + case eTYPE_VILLAGERGOLEM: + return app.GetString(IDS_IRONGOLEM); + + }; + + return L""; +} + +DWORD CMinecraftApp::m_dwContentTypeA[e_Marketplace_MAX]= +{ + XMARKETPLACE_OFFERING_TYPE_CONTENT, // e_DLC_SkinPack, e_DLC_TexturePacks, e_DLC_MashupPacks +#ifndef _XBOX_ONE + XMARKETPLACE_OFFERING_TYPE_THEME, // e_DLC_Themes + XMARKETPLACE_OFFERING_TYPE_AVATARITEM, // e_DLC_AvatarItems + XMARKETPLACE_OFFERING_TYPE_TILE, // e_DLC_Gamerpics +#endif +}; + +unsigned int CMinecraftApp::AddDLCRequest(eDLCMarketplaceType eType, bool bPromote) +{ + // lock access + EnterCriticalSection(&csDLCDownloadQueue); + + // If it's already in there, promote it to the top of the list + int iPosition=0; + for(AUTO_VAR(it, m_DLCDownloadQueue.begin()); it != m_DLCDownloadQueue.end(); ++it) + { + DLCRequest *pCurrent = *it; + + if(pCurrent->dwType==m_dwContentTypeA[eType]) + { + // already got this in the list + if(pCurrent->eState == e_DLC_ContentState_Retrieving || pCurrent->eState == e_DLC_ContentState_Retrieved) + { + // already retrieved this + LeaveCriticalSection(&csDLCDownloadQueue); + return 0; + } + else + { + // promote + if(bPromote) + { + m_DLCDownloadQueue.erase(m_DLCDownloadQueue.begin()+iPosition); + m_DLCDownloadQueue.insert(m_DLCDownloadQueue.begin(),pCurrent); + } + LeaveCriticalSection(&csDLCDownloadQueue); + return 0; + } + } + iPosition++; + } + + DLCRequest *pDLCreq = new DLCRequest; + pDLCreq->dwType=m_dwContentTypeA[eType]; + pDLCreq->eState=e_DLC_ContentState_Idle; + + m_DLCDownloadQueue.push_back(pDLCreq); + + m_bAllDLCContentRetrieved=false; + LeaveCriticalSection(&csDLCDownloadQueue); + + app.DebugPrintf("[Consoles_App] Added DLC request.\n"); + return 1; +} + +unsigned int CMinecraftApp::AddTMSPPFileTypeRequest(eDLCContentType eType, bool bPromote) +{ +#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) + // lock access + EnterCriticalSection(&csTMSPPDownloadQueue); + + // If it's already in there, promote it to the top of the list + int iPosition=0; + //ignore promoting for now + /* + bool bPromoted=false; + + + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(pCurrent->eType==eType) + { + if(!(pCurrent->eState == e_TMS_ContentState_Retrieving || pCurrent->eState == e_TMS_ContentState_Retrieved)) + { + // promote + if(bPromote) + { + m_TMSPPDownloadQueue.erase(m_TMSPPDownloadQueue.begin()+iPosition); + m_TMSPPDownloadQueue.insert(m_TMSPPDownloadQueue.begin(),pCurrent); + bPromoted=true; + } + } + } + iPosition++; + } + + if(bPromoted) + { + // re-ordered the list, so leave now + LeaveCriticalSection(&csTMSPPDownloadQueue); + return 0; + } + */ + + // special case for data files (not image files) + if(eType==e_DLC_TexturePackData) + { + + + int iCount=GetDLCInfoFullOffersCount(); + + for(int i=0;ieDLCType==e_DLC_TexturePacks) || (pDLC->eDLCType==e_DLC_MashupPacks)) + { + // first check if the image is already in the memory textures, since we might be loading some from the Title Update partition + if(pDLC->wchDataFile[0]!=0) + { + //WCHAR *cString = pDLC->wchDataFile; + // 4J-PB - shouldn't check this here - let the TMS files override it, so if they are on TMS, we'll take them first + //int iIndex = app.GetLocalTMSFileIndex(pDLC->wchDataFile,true); + + //if(iIndex!=-1) + { + bool bPresent = app.IsFileInTPD(pDLC->iConfig); + + if(!bPresent) + { + // this may already be present in the vector because of a previous trial/full offer + + bool bAlreadyInQueue=false; + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(wcscmp(pDLC->wchDataFile,pCurrent->wchFilename)==0) + { + bAlreadyInQueue=true; + break; + } + } + + if(!bAlreadyInQueue) + { + TMSPPRequest *pTMSPPreq = new TMSPPRequest; + + pTMSPPreq->CallbackFunc=&CMinecraftApp::TMSPPFileReturned; + pTMSPPreq->lpCallbackParam=this; + pTMSPPreq->eStorageFacility=C4JStorage::eGlobalStorage_Title; + pTMSPPreq->eFileTypeVal=C4JStorage::TMS_FILETYPE_BINARY; + memcpy(pTMSPPreq->wchFilename,pDLC->wchDataFile,sizeof(WCHAR)*MAX_BANNERNAME_SIZE); + pTMSPPreq->eType=e_DLC_TexturePackData; + pTMSPPreq->eState=e_TMS_ContentState_Queued; + m_bAllTMSContentRetrieved=false; + m_TMSPPDownloadQueue.push_back(pTMSPPreq); + } + } + else + { + app.DebugPrintf("Texture data already present in the TPD\n"); + } + } + } + } + } + } + else + { // for all the files of type eType, add them to the download list + + // run through the trial offers first, then the full offers. Any duplicates won't be added to the download queue + int iCount; +#ifdef _XBOX // Only trial offers on Xbox 360 + iCount=GetDLCInfoTrialOffersCount(); + for(int i=0;ieDLCType==eType) + { + + WCHAR *cString = pDLC->wchBanner; + + // 4J-PB - shouldn't check this here - let the TMS files override it, so if they are on TMS, we'll take them first + // is the file in the TMS XZP? + //int iIndex = app.GetLocalTMSFileIndex(cString,true); + + //if(iIndex!=-1) + { + bool bPresent = app.IsFileInMemoryTextures(cString); + + if(!bPresent) // retrieve it from TMSPP + { + bool bAlreadyInQueue=false; + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(wcscmp(pDLC->wchBanner,pCurrent->wchFilename)==0) + { + bAlreadyInQueue=true; + break; + } + } + + if(!bAlreadyInQueue) + { + TMSPPRequest *pTMSPPreq = new TMSPPRequest; + + pTMSPPreq->CallbackFunc=&CMinecraftApp::TMSPPFileReturned; + pTMSPPreq->lpCallbackParam=this; + pTMSPPreq->eStorageFacility=C4JStorage::eGlobalStorage_Title; + pTMSPPreq->eFileTypeVal=C4JStorage::TMS_FILETYPE_BINARY; + //wcstombs(pTMSPPreq->szFilename,pDLC->wchBanner,MAX_TMSFILENAME_SIZE); + memcpy(pTMSPPreq->wchFilename,pDLC->wchBanner,sizeof(WCHAR)*MAX_BANNERNAME_SIZE); + pTMSPPreq->eType=eType; + pTMSPPreq->eState=e_TMS_ContentState_Queued; + + m_bAllTMSContentRetrieved=false; + m_TMSPPDownloadQueue.push_back(pTMSPPreq); + app.DebugPrintf("===m_TMSPPDownloadQueue Adding %ls, q size is %d\n",pTMSPPreq->wchFilename,m_TMSPPDownloadQueue.size()); + } + } + } + } + } +#endif + // and the full offers + + iCount=GetDLCInfoFullOffersCount(); + for(int i=0;iwchType,wchDLCTypeNames[eType])==0) + if(pDLC->eDLCType==eType) + { + // first check if the image is already in the memory textures, since we might be loading some from the Title Update partition + + WCHAR *cString = pDLC->wchBanner; + // 4J-PB - shouldn't check this here - let the TMS files override it, so if they are on TMS, we'll take them first + //int iIndex = app.GetLocalTMSFileIndex(cString,true); + + //if(iIndex!=-1) + { + bool bPresent = app.IsFileInMemoryTextures(cString); + + if(!bPresent) + { + // this may already be present in the vector because of a previous trial/full offer + + bool bAlreadyInQueue=false; + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(wcscmp(pDLC->wchBanner,pCurrent->wchFilename)==0) + { + bAlreadyInQueue=true; + break; + } + } + + if(!bAlreadyInQueue) + { + //app.DebugPrintf("Adding a request to the TMSPP download queue - %ls\n",pDLC->wchBanner); + TMSPPRequest *pTMSPPreq = new TMSPPRequest; + ZeroMemory(pTMSPPreq,sizeof(TMSPPRequest)); + + pTMSPPreq->CallbackFunc=&CMinecraftApp::TMSPPFileReturned; + pTMSPPreq->lpCallbackParam=this; + // 4J-PB - testing for now + //pTMSPPreq->eStorageFacility=C4JStorage::eGlobalStorage_TitleUser; + pTMSPPreq->eStorageFacility=C4JStorage::eGlobalStorage_Title; + pTMSPPreq->eFileTypeVal=C4JStorage::TMS_FILETYPE_BINARY; + //wcstombs(pTMSPPreq->szFilename,pDLC->wchBanner,MAX_TMSFILENAME_SIZE); + + memcpy(pTMSPPreq->wchFilename,pDLC->wchBanner,sizeof(WCHAR)*MAX_BANNERNAME_SIZE); + pTMSPPreq->eType=eType; + pTMSPPreq->eState=e_TMS_ContentState_Queued; + m_bAllTMSContentRetrieved=false; + m_TMSPPDownloadQueue.push_back(pTMSPPreq); + app.DebugPrintf("===m_TMSPPDownloadQueue Adding %ls, q size is %d\n",pTMSPPreq->wchFilename,m_TMSPPDownloadQueue.size()); + } + } + } + } + } + } + + LeaveCriticalSection(&csTMSPPDownloadQueue); +#endif + return 1; +} + +bool CMinecraftApp::CheckTMSDLCCanStop() +{ + EnterCriticalSection(&csTMSPPDownloadQueue); + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(pCurrent->eState==e_TMS_ContentState_Retrieving) + { + LeaveCriticalSection(&csTMSPPDownloadQueue); + return false; + } + } + LeaveCriticalSection(&csTMSPPDownloadQueue); + + return true; +} + + +bool CMinecraftApp::RetrieveNextDLCContent() +{ + // If there's already a retrieve in progress, quit + // we may have re-ordered the list, so need to check every item + + // is there a primary player and a network connection? + int primPad = ProfileManager.GetPrimaryPad(); + if ( primPad == -1 || !ProfileManager.IsSignedInLive(primPad) ) + { + return true; // 4J-JEV: We need to wait until the primary player is online. + } + + EnterCriticalSection(&csDLCDownloadQueue); + for(AUTO_VAR(it, m_DLCDownloadQueue.begin()); it != m_DLCDownloadQueue.end(); ++it) + { + DLCRequest *pCurrent = *it; + + if(pCurrent->eState==e_DLC_ContentState_Retrieving) + { + LeaveCriticalSection(&csDLCDownloadQueue); + return true; + } + } + + // Now look for the next retrieval + for(AUTO_VAR(it, m_DLCDownloadQueue.begin()); it != m_DLCDownloadQueue.end(); ++it) + { + DLCRequest *pCurrent = *it; + + if(pCurrent->eState==e_DLC_ContentState_Idle) + { +#ifdef _DEBUG + app.DebugPrintf("RetrieveNextDLCContent - type = %d\n",pCurrent->dwType); +#endif + + C4JStorage::EDLCStatus status = StorageManager.GetDLCOffers(ProfileManager.GetPrimaryPad(), &CMinecraftApp::DLCOffersReturned, this, pCurrent->dwType); + if(status==C4JStorage::EDLC_Pending) + { + pCurrent->eState=e_DLC_ContentState_Retrieving; + } + else + { + // no content of this type, or some other problem + app.DebugPrintf("RetrieveNextDLCContent - PROBLEM\n"); + pCurrent->eState=e_DLC_ContentState_Retrieved; + } + LeaveCriticalSection(&csDLCDownloadQueue); + return true; + } + } + LeaveCriticalSection(&csDLCDownloadQueue); + + app.DebugPrintf("[Consoles_App] Finished downloading dlc content.\n"); + return false; +} + +#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) +#ifdef _XBOX_ONE +int CMinecraftApp::TMSPPFileReturned(LPVOID pParam,int iPad,int iUserData,LPVOID lpvData, WCHAR* wchFilename) +{ + C4JStorage::PTMSPP_FILEDATA pFileData=(C4JStorage::PTMSPP_FILEDATA)lpvData; +#else +int CMinecraftApp::TMSPPFileReturned(LPVOID pParam,int iPad,int iUserData,C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename) +{ +#endif + + CMinecraftApp* pClass = (CMinecraftApp *) pParam; + + // find the right one in the vector + EnterCriticalSection(&pClass->csTMSPPDownloadQueue); + for(AUTO_VAR(it, pClass->m_TMSPPDownloadQueue.begin()); it != pClass->m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; +#if defined(_XBOX) || defined(_WINDOWS64) + char szFile[MAX_TMSFILENAME_SIZE]; + wcstombs(szFile,pCurrent->wchFilename,MAX_TMSFILENAME_SIZE); + + + if(strcmp(szFilename,szFile)==0) +#elif _XBOX_ONE + if(wcscmp(wchFilename,pCurrent->wchFilename)==0) +#endif + { + // set this to retrieved whether it found it or not + pCurrent->eState=e_TMS_ContentState_Retrieved; + + if(pFileData!=NULL) + { + +#ifdef _XBOX_ONE + + + switch(pCurrent->eType) + { + case e_DLC_TexturePackData: + { + // 4J-PB - we need to allocate memory for the file data and copy into it, since the current data is a reference into the blob download memory + PBYTE pbData = new BYTE [pFileData->dwSize]; + memcpy(pbData,pFileData->pbData,pFileData->dwSize); + + pClass->m_vTMSPPData.push_back(pbData); + app.DebugPrintf("Got texturepack data\n"); + // get the config value for the texture pack + int iConfig=app.GetTPConfigVal(pCurrent->wchFilename); + app.AddMemoryTPDFile(iConfig, pbData, pFileData->dwSize); + } + break; + default: + // 4J-PB - check the data is an image + if(pFileData->pbData[0]==0x89) + { + // 4J-PB - we need to allocate memory for the file data and copy into it, since the current data is a reference into the blob download memory + PBYTE pbData = new BYTE [pFileData->dwSize]; + memcpy(pbData,pFileData->pbData,pFileData->dwSize); + + pClass->m_vTMSPPData.push_back(pbData); + app.DebugPrintf("Got image data - %ls\n",pCurrent->wchFilename); + app.AddMemoryTextureFile(pCurrent->wchFilename, pbData, pFileData->dwSize); + } + else + { + app.DebugPrintf("Got image data, but it's not a png - %ls\n",pCurrent->wchFilename); + } + break; + } + +#else + switch(pCurrent->eType) + { + case e_DLC_TexturePackData: + { + app.DebugPrintf("--- Got texturepack data %ls\n",pCurrent->wchFilename); + // get the config value for the texture pack + int iConfig=app.GetTPConfigVal(pCurrent->wchFilename); + app.AddMemoryTPDFile(iConfig, pFileData->pbData, pFileData->dwSize); + } + break; + default: + app.DebugPrintf("--- Got image data - %ls\n",pCurrent->wchFilename); + app.AddMemoryTextureFile(pCurrent->wchFilename, pFileData->pbData, pFileData->dwSize); + break; + } +#endif + } + else + { +#ifdef _XBOX_ONE + app.DebugPrintf("TMSImageReturned failed (%ls)...\n",wchFilename); +#else + app.DebugPrintf("TMSImageReturned failed (%s)...\n",szFilename); +#endif + } + break; + } + + } + LeaveCriticalSection(&pClass->csTMSPPDownloadQueue); + + return 0; +} +#endif + +bool CMinecraftApp::RetrieveNextTMSPPContent() +{ +#if defined _XBOX || defined _XBOX_ONE + // If there's already a retrieve in progress, quit + // we may have re-ordered the list, so need to check every item + + // is there a primary player and a network connection? + if(ProfileManager.GetPrimaryPad()==-1) return false; + + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())==false) return false; + + EnterCriticalSection(&csTMSPPDownloadQueue); + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(pCurrent->eState==e_TMS_ContentState_Retrieving) + { + app.DebugPrintf("."); + LeaveCriticalSection(&csTMSPPDownloadQueue); + return true; + } + } + + // Now look for the next retrieval + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + if(pCurrent->eState==e_TMS_ContentState_Queued) + { + // 4J-PB - the file may be in the local TMS files, but try to retrieve it from the remote TMS in case it's been changed. If it's not in the list of TMS files, this will + // return right away with a ETMSStatus_Fail_ReadDetailsNotRetrieved +#ifdef _XBOX + char szFilename[MAX_TMSFILENAME_SIZE]; + wcstombs(szFilename,pCurrent->wchFilename,MAX_TMSFILENAME_SIZE); + + app.DebugPrintf("\nRetrieveNextTMSPPContent - type = %d, %s\n",pCurrent->eType,szFilename); + + C4JStorage::ETMSStatus status=StorageManager.TMSPP_ReadFile(ProfileManager.GetPrimaryPad(),pCurrent->eStorageFacility,pCurrent->eFileTypeVal,szFilename,pCurrent->CallbackFunc,this); + switch(status) + { + case C4JStorage::ETMSStatus_Pending: + pCurrent->eState=e_TMS_ContentState_Retrieving; + break; + case C4JStorage::ETMSStatus_Idle: + pCurrent->eState=e_TMS_ContentState_Retrieved; + break; + case C4JStorage::ETMSStatus_Fail_ReadInProgress: + case C4JStorage::ETMSStatus_ReadInProgress: + pCurrent->eState=e_TMS_ContentState_Retrieving; + if(pCurrent->eState==C4JStorage::ETMSStatus_Fail_ReadInProgress) + { + app.DebugPrintf("TMSPP_ReadFile failed - read in progress\n"); + Sleep(50); + LeaveCriticalSection(&csTMSPPDownloadQueue); + return false; + } + break; + default: + pCurrent->eState=e_TMS_ContentState_Retrieved; + break; + } +#else + eTitleStorageState status; + app.DebugPrintf("RetrieveNextTMSPPContent - type = %d, %ls\n",pCurrent->eType,pCurrent->wchFilename); + //eTitleStorageState status=StorageManager.TMSPP_ReadFile(ProfileManager.GetPrimaryPad(),pCurrent->eStorageFacility,pCurrent->eFileTypeVal,pCurrent->wchFilename,pCurrent->CallbackFunc,this,0); + if(0)//wcscmp(pCurrent->wchFilename,L"TP01.png")==0) + { + // TP01 fails because the blob size returned is bigger than the global metadata says it should be + status=eTitleStorage_readerror; + } + else + { + status=StorageManager.TMSPP_ReadFile(ProfileManager.GetPrimaryPad(),pCurrent->eStorageFacility,pCurrent->eFileTypeVal,pCurrent->wchFilename,pCurrent->CallbackFunc,this,0); + } + switch(status) + { + case eTitleStorage_pending: + pCurrent->eState=e_TMS_ContentState_Retrieving; + break; + case eTitleStorage_idle: + pCurrent->eState=e_TMS_ContentState_Retrieved; + break; + case eTitleStorage_busy: + // try again next time + { + app.DebugPrintf("@@@@@@@@@@@@@@@@@ TMSPP_ReadFile failed - busy (probably reading already)\n"); + Sleep(50); + LeaveCriticalSection(&csTMSPPDownloadQueue); + return false; + } + break; + default: + pCurrent->eState=e_TMS_ContentState_Retrieved; + break; + } +#endif + + + + LeaveCriticalSection(&csTMSPPDownloadQueue); + return true; + } + } + + LeaveCriticalSection(&csTMSPPDownloadQueue); + +#endif + return false; +} + +void CMinecraftApp::TickDLCOffersRetrieved() +{ + if(!m_bAllDLCContentRetrieved) + { + if (!app.RetrieveNextDLCContent()) + { + app.DebugPrintf("[Consoles_App] All content retrieved.\n"); + m_bAllDLCContentRetrieved=true; + } + } +} +void CMinecraftApp::ClearAndResetDLCDownloadQueue() +{ + app.DebugPrintf("[Consoles_App] Clear and reset download queue.\n"); + + int iPosition=0; + EnterCriticalSection(&csTMSPPDownloadQueue); + for(AUTO_VAR(it, m_DLCDownloadQueue.begin()); it != m_DLCDownloadQueue.end(); ++it) + { + DLCRequest *pCurrent = *it; + + delete pCurrent; + iPosition++; + } + m_DLCDownloadQueue.clear(); + m_bAllDLCContentRetrieved=true; + LeaveCriticalSection(&csTMSPPDownloadQueue); +} + +void CMinecraftApp::TickTMSPPFilesRetrieved() +{ + if(m_bTickTMSDLCFiles && !m_bAllTMSContentRetrieved) + { + if(app.RetrieveNextTMSPPContent()==false) + { + m_bAllTMSContentRetrieved=true; + } + } +} +void CMinecraftApp::ClearTMSPPFilesRetrieved() +{ + int iPosition=0; + EnterCriticalSection(&csTMSPPDownloadQueue); + for(AUTO_VAR(it, m_TMSPPDownloadQueue.begin()); it != m_TMSPPDownloadQueue.end(); ++it) + { + TMSPPRequest *pCurrent = *it; + + delete pCurrent; + iPosition++; + } + m_TMSPPDownloadQueue.clear(); + m_bAllTMSContentRetrieved=true; + LeaveCriticalSection(&csTMSPPDownloadQueue); +} + +int CMinecraftApp::DLCOffersReturned(void *pParam, int iOfferC, DWORD dwType, int iPad) +{ + CMinecraftApp* pClass = (CMinecraftApp *) pParam; + + // find the right one in the vector + EnterCriticalSection(&pClass->csTMSPPDownloadQueue); + for(AUTO_VAR(it, pClass->m_DLCDownloadQueue.begin()); it != pClass->m_DLCDownloadQueue.end(); ++it) + { + DLCRequest *pCurrent = *it; + + // avatar items are coming back as type Content, so we can't trust the type setting + if(pCurrent->dwType==dwType) + { + pClass->m_iDLCOfferC = iOfferC; + app.DebugPrintf("DLCOffersReturned - type %d, count %d - setting to retrieved\n",dwType,iOfferC); + pCurrent->eState=e_DLC_ContentState_Retrieved; + break; + } + } + LeaveCriticalSection(&pClass->csTMSPPDownloadQueue); + return 0; +} + +eDLCContentType CMinecraftApp::Find_eDLCContentType(DWORD dwType) +{ + for(int i=0;idwType==m_dwContentTypeA[eType]) && (pCurrent->eState==e_DLC_ContentState_Retrieved)) + { + LeaveCriticalSection(&csDLCDownloadQueue); + return true; + } + } + LeaveCriticalSection(&csDLCDownloadQueue); + return false; +} + +void CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, DWORD dwSkinBoxC) +{ + EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); + Model *pModel = renderer->getModel(); + vector *pvModelPart = new vector; + vector *pvSkinBoxes = new vector; + + EnterCriticalSection( &csAdditionalModelParts ); + EnterCriticalSection( &csAdditionalSkinBoxes ); + + app.DebugPrintf("*** SetAdditionalSkinBoxes - Inserting model parts for skin %d from array of Skin Boxes\n",dwSkinID&0x0FFFFFFF); + + // convert the skin boxes into model parts, and add to the humanoid model + for(unsigned int i=0;iAddOrRetrievePart(&SkinBoxA[i]); + pvModelPart->push_back(pModelPart); + pvSkinBoxes->push_back(&SkinBoxA[i]); + } + } + + + m_AdditionalModelParts.insert( std::pair *>(dwSkinID, pvModelPart) ); + m_AdditionalSkinBoxes.insert( std::pair *>(dwSkinID, pvSkinBoxes) ); + + LeaveCriticalSection( &csAdditionalSkinBoxes ); + LeaveCriticalSection( &csAdditionalModelParts ); + +} + +vector * CMinecraftApp::SetAdditionalSkinBoxes(DWORD dwSkinID, vector *pvSkinBoxA) +{ + EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); + Model *pModel = renderer->getModel(); + vector *pvModelPart = new vector; + + EnterCriticalSection( &csAdditionalModelParts ); + EnterCriticalSection( &csAdditionalSkinBoxes ); + app.DebugPrintf("*** SetAdditionalSkinBoxes - Inserting model parts for skin %d from array of Skin Boxes\n",dwSkinID&0x0FFFFFFF); + + // convert the skin boxes into model parts, and add to the humanoid model + for(AUTO_VAR(it, pvSkinBoxA->begin());it != pvSkinBoxA->end(); ++it) + { + if(pModel) + { + ModelPart *pModelPart=pModel->AddOrRetrievePart(*it); + pvModelPart->push_back(pModelPart); + } + } + + m_AdditionalModelParts.insert( std::pair *>(dwSkinID, pvModelPart) ); + m_AdditionalSkinBoxes.insert( std::pair *>(dwSkinID, pvSkinBoxA) ); + + LeaveCriticalSection( &csAdditionalSkinBoxes ); + LeaveCriticalSection( &csAdditionalModelParts ); + return pvModelPart; +} + + +vector *CMinecraftApp::GetAdditionalModelParts(DWORD dwSkinID) +{ + EnterCriticalSection( &csAdditionalModelParts ); + vector *pvModelParts=NULL; + if(m_AdditionalModelParts.size()>0) + { + AUTO_VAR(it, m_AdditionalModelParts.find(dwSkinID)); + if(it!=m_AdditionalModelParts.end()) + { + pvModelParts = (*it).second; + } + } + + LeaveCriticalSection( &csAdditionalModelParts ); + return pvModelParts; +} + +vector *CMinecraftApp::GetAdditionalSkinBoxes(DWORD dwSkinID) +{ + EnterCriticalSection( &csAdditionalSkinBoxes ); + vector *pvSkinBoxes=NULL; + if(m_AdditionalSkinBoxes.size()>0) + { + AUTO_VAR(it,m_AdditionalSkinBoxes.find(dwSkinID)); + if(it!=m_AdditionalSkinBoxes.end()) + { + pvSkinBoxes = (*it).second; + } + } + + LeaveCriticalSection( &csAdditionalSkinBoxes ); + return pvSkinBoxes; +} + +unsigned int CMinecraftApp::GetAnimOverrideBitmask(DWORD dwSkinID) +{ + EnterCriticalSection( &csAnimOverrideBitmask ); + unsigned int uiAnimOverrideBitmask=0L; + + if(m_AnimOverrides.size()>0) + { + AUTO_VAR(it, m_AnimOverrides.find(dwSkinID)); + if(it!=m_AnimOverrides.end()) + { + uiAnimOverrideBitmask = (*it).second; + } + } + + LeaveCriticalSection( &csAnimOverrideBitmask ); + return uiAnimOverrideBitmask; +} + +void CMinecraftApp::SetAnimOverrideBitmask(DWORD dwSkinID,unsigned int uiAnimOverrideBitmask) +{ + // Make thread safe + EnterCriticalSection( &csAnimOverrideBitmask ); + + if(m_AnimOverrides.size()>0) + { + AUTO_VAR(it, m_AnimOverrides.find(dwSkinID)); + if(it!=m_AnimOverrides.end()) + { + LeaveCriticalSection( &csAnimOverrideBitmask ); + return; // already in here + } + } + m_AnimOverrides.insert( std::pair(dwSkinID, uiAnimOverrideBitmask) ); + LeaveCriticalSection( &csAnimOverrideBitmask ); +} + +DWORD CMinecraftApp::getSkinIdFromPath(const wstring &skin) +{ + bool dlcSkin = false; + unsigned int skinId = 0; + + if(skin.size() >= 14) + { + dlcSkin = skin.substr(0,3).compare(L"dlc") == 0; + + wstring skinValue = skin.substr(7,skin.size()); + skinValue = skinValue.substr(0,skinValue.find_first_of(L'.')); + + std::wstringstream ss; + // 4J Stu - dlc skins are numbered using decimal to make it easier for artists/people to number manually + // Everything else is numbered using hex + if(dlcSkin) + ss << std::dec << skinValue.c_str(); + else + ss << std::hex << skinValue.c_str(); + ss >> skinId; + + skinId = MAKE_SKIN_BITMASK(dlcSkin, skinId); + } + return skinId; +} + +wstring CMinecraftApp::getSkinPathFromId(DWORD skinId) +{ + // 4J Stu - This function maps the encoded DWORD we store in the player profile + // to a filename that is stored as a memory texture and shared between systems in game + wchar_t chars[256]; + if( GET_IS_DLC_SKIN_FROM_BITMASK(skinId) ) + { + // 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually + swprintf(chars, 256, L"dlcskin%08d.png", GET_DLC_SKIN_ID_FROM_BITMASK(skinId)); + + } + else + { + DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(skinId); + DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(skinId); + if( ugcSkinIndex == 0 ) + { + swprintf(chars, 256, L"defskin%08X.png",defaultSkinIndex); + } + else + { + swprintf(chars, 256, L"ugcskin%08X.png",ugcSkinIndex); + } + } + return chars; +} + + +int CMinecraftApp::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ +#ifdef _XBOX + if(result!=C4JStorage::EMessage_Cancelled) + { + if(app.GetRequiredTexturePackID()!=0) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + app.GetDLCFullOfferIDForPackID(app.GetRequiredTexturePackID(),&ullOfferID_Full); + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + else // trial version + { + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + } +#endif + return 0; +} + +int CMinecraftApp::getArchiveFileSize(const wstring &filename) +{ + TexturePack *tPack = NULL; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft && pMinecraft->skins) tPack = pMinecraft->skins->getSelected(); + if(tPack && tPack->hasData() && tPack->getArchiveFile() && tPack->getArchiveFile()->hasFile(filename)) + { + return tPack->getArchiveFile()->getFileSize(filename); + } + else return m_mediaArchive->getFileSize(filename); +} + +bool CMinecraftApp::hasArchiveFile(const wstring &filename) +{ + TexturePack *tPack = NULL; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft && pMinecraft->skins) tPack = pMinecraft->skins->getSelected(); + if(tPack && tPack->hasData() && tPack->getArchiveFile() && tPack->getArchiveFile()->hasFile(filename)) return true; + else return m_mediaArchive->hasFile(filename); +} + +byteArray CMinecraftApp::getArchiveFile(const wstring &filename) +{ + TexturePack *tPack = NULL; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft && pMinecraft->skins) tPack = pMinecraft->skins->getSelected(); + if(tPack && tPack->hasData() && tPack->getArchiveFile() && tPack->getArchiveFile()->hasFile(filename)) + { + return tPack->getArchiveFile()->getFile(filename); + } + else return m_mediaArchive->getFile(filename); +} + +// DLC + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) +int CMinecraftApp::GetDLCInfoCount() +{ + return (int)DLCInfo.size(); +} +#elif defined _XBOX_ONE +int CMinecraftApp::GetDLCInfoTrialOffersCount() +{ + return 0; +} + +int CMinecraftApp::GetDLCInfoFullOffersCount() +{ + return (int)DLCInfo_Full.size(); +} +#else +int CMinecraftApp::GetDLCInfoTrialOffersCount() +{ + return (int)DLCInfo_Trial.size(); +} + +int CMinecraftApp::GetDLCInfoFullOffersCount() +{ + return (int)DLCInfo_Full.size(); +} +#endif + +int CMinecraftApp::GetDLCInfoTexturesOffersCount() +{ + return (int)DLCTextures_PackID.size(); +} + +// AUTOSAVE +void CMinecraftApp::SetAutosaveTimerTime(void) +{ +#if defined(_XBOX_ONE) || defined(__ORBIS__) + m_uiAutosaveTimer= GetTickCount()+1000*60; +#else + m_uiAutosaveTimer= GetTickCount()+GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Autosave)*1000*60*15; +#endif +}// value x 15 to get mins, x60 for secs + +bool CMinecraftApp::AutosaveDue(void) +{ + return (GetTickCount()>m_uiAutosaveTimer); +} + +unsigned int CMinecraftApp::SecondsToAutosave() +{ + return (m_uiAutosaveTimer - GetTickCount() ) / 1000; +} + +void CMinecraftApp::SetTrialTimerStart(void) +{ + m_fTrialTimerStart=m_Time.fAppTime; mfTrialPausedTime=0.0f; +} + +float CMinecraftApp::getTrialTimer(void) +{ + return m_Time.fAppTime-m_fTrialTimerStart-mfTrialPausedTime; +} + +bool CMinecraftApp::IsLocalMultiplayerAvailable() +{ + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + bool available = RenderManager.IsHiDef() && connectedControllers > 1; + +#ifdef __ORBIS__ + // Check for remote play + available = available && InputManager.IsLocalMultiplayerAvailable(); +#endif + + return available; + + // Found this in GameNetworkManager? + //#ifdef _DURANGO + // iOtherConnectedControllers = InputManager.GetConnectedGamepadCount(); + // if((InputManager.IsPadConnected(userIndex) || ProfileManager.IsSignedIn(userIndex))) + // { + // --iOtherConnectedControllers; + // } + //#else + // for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + // { + // if( (i!=userIndex) && (InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i)) ) + // { + // iOtherConnectedControllers++; + // } + // } + //#endif +} + + +// 4J-PB - language and locale function + +void CMinecraftApp::getLocale(vector &vecWstrLocales) +{ + vector locales; + + DWORD dwSystemLanguage = XGetLanguage( ); + + // 4J-PB - restrict the 360 language until we're ready to have them in + +#ifdef _XBOX + switch(dwSystemLanguage) + { + case XC_LANGUAGE_FRENCH : + locales.push_back(eMCLang_frFR); + break; + case XC_LANGUAGE_ITALIAN : + locales.push_back(eMCLang_itIT); + break; + case XC_LANGUAGE_GERMAN : + locales.push_back(eMCLang_deDE); + break; + case XC_LANGUAGE_SPANISH : + locales.push_back(eMCLang_esES); + break; + case XC_LANGUAGE_PORTUGUESE : + if(XGetLocale()==XC_LOCALE_BRAZIL) + { + locales.push_back(eMCLang_ptBR); + } + locales.push_back(eMCLang_ptPT); + break; + case XC_LANGUAGE_JAPANESE : + locales.push_back(eMCLang_jaJP); + break; + case XC_LANGUAGE_KOREAN : + locales.push_back(eMCLang_koKR); + break; + case XC_LANGUAGE_TCHINESE : + locales.push_back(eMCLang_zhCHT); + break; + } +#else + switch(dwSystemLanguage) + { + + case XC_LANGUAGE_ENGLISH: + switch(XGetLocale()) + { + case XC_LOCALE_AUSTRALIA: + case XC_LOCALE_CANADA: + case XC_LOCALE_CZECH_REPUBLIC: + case XC_LOCALE_GREECE: + case XC_LOCALE_HONG_KONG: + case XC_LOCALE_HUNGARY: + case XC_LOCALE_INDIA: + case XC_LOCALE_IRELAND: + case XC_LOCALE_ISRAEL: + case XC_LOCALE_NEW_ZEALAND: + case XC_LOCALE_SAUDI_ARABIA: + case XC_LOCALE_SINGAPORE: + case XC_LOCALE_SLOVAK_REPUBLIC: + case XC_LOCALE_SOUTH_AFRICA: + case XC_LOCALE_UNITED_ARAB_EMIRATES: + case XC_LOCALE_GREAT_BRITAIN: + locales.push_back(eMCLang_enGB); + break; + default: //XC_LOCALE_UNITED_STATES + break; + } + break; + case XC_LANGUAGE_JAPANESE : + locales.push_back(eMCLang_jaJP); + break; + case XC_LANGUAGE_GERMAN : + switch(XGetLocale()) + { + case XC_LOCALE_AUSTRIA: + locales.push_back(eMCLang_deAT); + break; + case XC_LOCALE_SWITZERLAND: + locales.push_back(eMCLang_deCH); + break; + default:// XC_LOCALE_GERMANY: + break; + } + locales.push_back(eMCLang_deDE); + break; + case XC_LANGUAGE_FRENCH : + switch(XGetLocale()) + { + case XC_LOCALE_BELGIUM: + locales.push_back(eMCLang_frBE); + break; + case XC_LOCALE_CANADA: + locales.push_back(eMCLang_frCA); + break; + case XC_LOCALE_SWITZERLAND: + locales.push_back(eMCLang_frCH); + break; + default:// XC_LOCALE_FRANCE: + break; + } + locales.push_back(eMCLang_frFR); + break; + case XC_LANGUAGE_SPANISH : + switch(XGetLocale()) + { + case XC_LOCALE_MEXICO: + case XC_LOCALE_ARGENTINA: + case XC_LOCALE_CHILE: + case XC_LOCALE_COLOMBIA: + case XC_LOCALE_UNITED_STATES: + locales.push_back(eMCLang_esMX); + break; + default://XC_LOCALE_SPAIN + break; + } + locales.push_back(eMCLang_esES); + break; + case XC_LANGUAGE_ITALIAN : + locales.push_back(eMCLang_itIT); + break; + case XC_LANGUAGE_KOREAN : + locales.push_back(eMCLang_koKR); + break; + case XC_LANGUAGE_TCHINESE : + switch(XGetLocale()) + { + case XC_LOCALE_HONG_KONG: + locales.push_back(eMCLang_zhHK); + locales.push_back(eMCLang_zhTW); + break; + case XC_LOCALE_TAIWAN: + locales.push_back(eMCLang_zhTW); + locales.push_back(eMCLang_zhHK); + default: + break; + } + locales.push_back(eMCLang_zhCHT); + break; + case XC_LANGUAGE_PORTUGUESE : + if(XGetLocale()==XC_LOCALE_BRAZIL) + { + locales.push_back(eMCLang_ptBR); + } + locales.push_back(eMCLang_ptPT); + break; + case XC_LANGUAGE_POLISH : + locales.push_back(eMCLang_plPL); + break; + case XC_LANGUAGE_RUSSIAN : + locales.push_back(eMCLang_ruRU); + break; + case XC_LANGUAGE_SWEDISH : + locales.push_back(eMCLang_svSV); + locales.push_back(eMCLang_svSE); + break; + case XC_LANGUAGE_TURKISH : + locales.push_back(eMCLang_trTR); + break; + case XC_LANGUAGE_BNORWEGIAN : + locales.push_back(eMCLang_nbNO); + locales.push_back(eMCLang_noNO); + locales.push_back(eMCLang_nnNO); + break; + case XC_LANGUAGE_DUTCH : + switch(XGetLocale()) + { + case XC_LOCALE_BELGIUM: + locales.push_back(eMCLang_nlBE); + break; + default: + break; + } + locales.push_back(eMCLang_nlNL); + break; + case XC_LANGUAGE_SCHINESE : + switch(XGetLocale()) + { + case XC_LOCALE_SINGAPORE: + locales.push_back(eMCLang_zhSG); + break; + default: + break; + } + locales.push_back(eMCLang_zhCHS); + locales.push_back(eMCLang_zhCN); + break; + + +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO + case XC_LANGUAGE_DANISH: + locales.push_back(eMCLang_daDA); + locales.push_back(eMCLang_daDK); + break; + + case XC_LANGUAGE_LATINAMERICANSPANISH: + locales.push_back(eMCLang_laLAS); + locales.push_back(eMCLang_esES); + break; + + case XC_LANGUAGE_FINISH : + locales.push_back(eMCLang_fiFI); + break; + + case XC_LANGUAGE_CZECH : + locales.push_back(eMCLang_csCZ); + locales.push_back(eMCLang_enCZ); + break; + + case XC_LANGUAGE_SLOVAK : + locales.push_back(eMCLang_skSK); + locales.push_back(eMCLang_enSK); + break; + + case XC_LANGUAGE_GREEK : + locales.push_back(eMCLang_elEL); + locales.push_back(eMCLang_elGR); + locales.push_back(eMCLang_enGR); + locales.push_back(eMCLang_enGB); + break; +#endif + } +#endif + + locales.push_back(eMCLang_enUS); + locales.push_back(eMCLang_null); + + for (int i=0; i +#include "..\Common\Tutorial\TutorialEnum.h" + +#ifdef _XBOX +#include "..\Common\XUI\XUI_Helper.h" +#include "..\Common\XUI\XUI_HelpCredits.h" +#endif +#include "UI\UIStructs.h" + +#include "..\..\Minecraft.World\DisconnectPacket.h" +#include + +#include "..\StringTable.h" +#include "..\Common\DLC\DLCManager.h" +#include "..\Common\GameRules\ConsoleGameRulesConstants.h" +#include "..\Common\GameRules\GameRuleManager.h" +#include "..\SkinBox.h" +#include "..\ArchiveFile.h" + +typedef struct _JoinFromInviteData +{ + DWORD dwUserIndex; // dwUserIndex + DWORD dwLocalUsersMask; // dwUserMask + const INVITE_INFO *pInviteInfo; // pInviteInfo +} +JoinFromInviteData; + +class Player; +class Inventory; +class Level; +class FurnaceTileEntity; +class Container; +class DispenserTileEntity; +class SignTileEntity; +class BrewingStandTileEntity; +class LocalPlayer; +class DLCPack; +class LevelRuleset; +class ConsoleSchematicFile; +class Model; +class ModelPart; +class StringTable; +class Merchant; + +class CMinecraftAudio; + +class CMinecraftApp + +#ifdef _XBOX + : public CXuiModule +#endif +{ +private: + static int s_iHTMLFontSizesA[eHTMLSize_COUNT]; + +public: + + CMinecraftApp(); + + static const float fSafeZoneX; // 5% of 1280 + static const float fSafeZoneY; // 5% of 720 + + typedef std::vector VMEMFILES; + typedef std::vector VNOTIFICATIONS; + + // storing skin files + std::vector vSkinNames; + DLCManager m_dlcManager; + + // storing credits text from the DLC + std::vector m_vCreditText; // hold the credit text lines so we can avoid duplicating them + + + // In builds prior to TU5, the size of the GAME_SETTINGS struct was 204 bytes. We added a few new values to the internal struct in TU5, and even though we + // changed the size of the ucUnused array to be decreased by the size of the values we added, the packing of the struct has introduced some extra + // padding that resulted in the GAME_SETTINGS struct being 208 bytes. The knock-on effect from this was that all the stats, which come after the game settings + // in the profile data, we being read offset by 4 bytes. We need to ensure that the GAME_SETTINGS struct does not grow larger than 204 bytes or if we need it + // to then we need to rebuild the profile data completely and increase the profile version. There should be enough free space to grow larger for a few more updates + // as long as we take into account the padding issues and check that settings are still stored at the same positions when we read them + static const int GAME_SETTINGS_PROFILE_DATA_BYTES = 204; + +#ifdef _EXTENDED_ACHIEVEMENTS + /* 4J-JEV: + * We need more space in the profile data because of the new achievements and statistics + * necessary for the new expanded achievement set. + */ + static const int GAME_DEFINED_PROFILE_DATA_BYTES = 2*972; // per user +#else + static const int GAME_DEFINED_PROFILE_DATA_BYTES = 972; // per user +#endif + unsigned int uiGameDefinedDataChangedBitmask; + + void DebugPrintf(const char *szFormat, ...); + void DebugPrintfVerbose(bool bVerbose, const char *szFormat, ...); // Conditional printf + void DebugPrintf(int user, const char *szFormat, ...); + + static const int USER_NONE = 0; // disables printf + static const int USER_GENERAL = 1; + static const int USER_JV = 2; + static const int USER_MH = 3; + static const int USER_PB = 4; + static const int USER_RR = 5; + static const int USER_SR = 6; + static const int USER_UI = 7; // 4J Stu - This also makes it appear on the UI console + + void HandleButtonPresses(); + bool IntroRunning() { return m_bIntroRunning;} + void SetIntroRunning(bool bSet) {m_bIntroRunning=bSet;} +#ifdef _CONTENT_PACKAGE +#ifndef _FINAL_BUILD + bool PartnernetPasswordRunning() { return m_bPartnernetPasswordRunning;} + void SetPartnernetPasswordRunning(bool bSet) {m_bPartnernetPasswordRunning=bSet;} +#endif +#endif + + bool IsAppPaused(); + void SetAppPaused(bool val); + static int DisplaySavingMessage(LPVOID pParam,const C4JStorage::ESavingMessage eMsg, int iPad); + bool GetGameStarted() {return m_bGameStarted;} + void SetGameStarted(bool bVal) { if(bVal) DebugPrintf("SetGameStarted - true\n"); else DebugPrintf("SetGameStarted - false\n"); m_bGameStarted = bVal; m_bIsAppPaused = !bVal;} + int GetLocalPlayerCount(void); + bool LoadInventoryMenu(int iPad,shared_ptr player, bool bNavigateBack=false); + bool LoadCreativeMenu(int iPad,shared_ptr player,bool bNavigateBack=false); + bool LoadEnchantingMenu(int iPad,shared_ptr inventory, int x, int y, int z, Level *level); + bool LoadFurnaceMenu(int iPad,shared_ptr inventory, shared_ptr furnace); + bool LoadBrewingStandMenu(int iPad,shared_ptr inventory, shared_ptr brewingStand); + bool LoadContainerMenu(int iPad,shared_ptr inventory, shared_ptr container); + bool LoadTrapMenu(int iPad,shared_ptr inventory, shared_ptr trap); + bool LoadCrafting2x2Menu(int iPad,shared_ptr player); + bool LoadCrafting3x3Menu(int iPad,shared_ptr player, int x, int y, int z); + bool LoadSignEntryMenu(int iPad,shared_ptr sign); + bool LoadRepairingMenu(int iPad,shared_ptr inventory, Level *level, int x, int y, int z); + bool LoadTradingMenu(int iPad, shared_ptr inventory, shared_ptr trader, Level *level); + + bool GetTutorialMode() { return m_bTutorialMode;} + void SetTutorialMode(bool bSet) {m_bTutorialMode=bSet;} + + void SetSpecialTutorialCompletionFlag(int iPad, int index); + + static LPCWSTR GetString(int iID); + + eGameMode GetGameMode() { return m_eGameMode;} + void SetGameMode(eGameMode eMode) { m_eGameMode=eMode;} + + eXuiAction GetGlobalXuiAction() {return m_eGlobalXuiAction;} + void SetGlobalXuiAction(eXuiAction action) {m_eGlobalXuiAction=action;} + eXuiAction GetXuiAction(int iPad) {return m_eXuiAction[iPad];} + void SetAction(int iPad, eXuiAction action, LPVOID param = NULL); + void SetTMSAction(int iPad, eTMSAction action) {m_eTMSAction[iPad]=action; } + eTMSAction GetTMSAction(int iPad) {return m_eTMSAction[iPad];} + eXuiServerAction GetXuiServerAction(int iPad) {return m_eXuiServerAction[iPad];} + LPVOID GetXuiServerActionParam(int iPad) {return m_eXuiServerActionParam[iPad];} + void SetXuiServerAction(int iPad, eXuiServerAction action, LPVOID param = NULL) {m_eXuiServerAction[iPad]=action; m_eXuiServerActionParam[iPad] = param;} + eXuiServerAction GetGlobalXuiServerAction() {return m_eGlobalXuiServerAction;} + void SetGlobalXuiServerAction(eXuiServerAction action) {m_eGlobalXuiServerAction=action;} + + DisconnectPacket::eDisconnectReason GetDisconnectReason() { return m_disconnectReason; } + void SetDisconnectReason(DisconnectPacket::eDisconnectReason bVal) { m_disconnectReason = bVal; } + + bool GetChangingSessionType() { return m_bChangingSessionType; } + void SetChangingSessionType(bool bVal) { m_bChangingSessionType = bVal; } + + bool GetReallyChangingSessionType() { return m_bReallyChangingSessionType; } + void SetReallyChangingSessionType(bool bVal) { m_bReallyChangingSessionType = bVal; } + + + // 4J Stu - Added so that we can call this when a confirmation box is selected + static void SetActionConfirmed(LPVOID param); + void HandleXuiActions(void); + + // 4J Stu - Functions used for Minecon and other promo work + bool GetLoadSavesFromFolderEnabled() { return m_bLoadSavesFromFolderEnabled; } + void SetLoadSavesFromFolderEnabled(bool bVal) { m_bLoadSavesFromFolderEnabled = bVal; } + + // 4J Stu - Useful for debugging + bool GetWriteSavesToFolderEnabled() { return m_bWriteSavesToFolderEnabled; } + void SetWriteSavesToFolderEnabled(bool bVal) { m_bWriteSavesToFolderEnabled = bVal; } + bool GetMobsDontAttackEnabled() { return m_bMobsDontAttack; } + void SetMobsDontAttackEnabled(bool bVal) { m_bMobsDontAttack = bVal; } + bool GetUseDPadForDebug() { return m_bUseDPadForDebug; } + void SetUseDPadForDebug(bool bVal) { m_bUseDPadForDebug = bVal; } + bool GetMobsDontTickEnabled() { return m_bMobsDontTick; } + void SetMobsDontTickEnabled(bool bVal) { m_bMobsDontTick = bVal; } + + bool GetFreezePlayers() { return m_bFreezePlayers; } + void SetFreezePlayers(bool bVal) { m_bFreezePlayers = bVal; } + + // debug -0 show safe area + void ShowSafeArea(BOOL bShow) + { +#ifdef _XBOX + CXuiSceneBase::ShowSafeArea( bShow ); +#endif + } + // 4J-PB - to capture the social post screenshot + virtual void CaptureScreenshot(int iPad) {}; + //void GetPreviewImage(int iPad,XSOCIAL_PREVIEWIMAGE *preview); + + void InitGameSettings(); + static int OldProfileVersionCallback(LPVOID pParam,unsigned char *pucData, const unsigned short usVersion, const int iPad); + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__ ) + static int DefaultOptionsCallback(LPVOID pParam,C4JStorage::PROFILESETTINGS *pSettings, const int iPad); + int SetDefaultOptions(C4JStorage::PROFILESETTINGS *pSettings,const int iPad,bool bWriteProfile=true); +#ifdef __ORBIS__ + static int OptionsDataCallback(LPVOID pParam,int iPad,unsigned short usVersion,C4JStorage::eOptionsCallback eStatus,int iBlocksRequired); + int GetOptionsBlocksRequired(int iPad); +#else + static int OptionsDataCallback(LPVOID pParam,int iPad,unsigned short usVersion,C4JStorage::eOptionsCallback eStatus); +#endif + + C4JStorage::eOptionsCallback GetOptionsCallbackStatus(int iPad); + + void SetOptionsCallbackStatus(int iPad, C4JStorage::eOptionsCallback eStatus); +#else + static int DefaultOptionsCallback(LPVOID pParam,C_4JProfile::PROFILESETTINGS *pSettings, const int iPad); + int SetDefaultOptions(C_4JProfile::PROFILESETTINGS *pSettings,const int iPad); +#endif + virtual void SetRichPresenceContext(int iPad, int contextId) = 0; + + + void SetGameSettings(int iPad,eGameSetting eVal,unsigned char ucVal); + unsigned char GetGameSettings(int iPad,eGameSetting eVal); + unsigned char GetGameSettings(eGameSetting eVal); // for the primary pad + void SetPlayerSkin(int iPad,const wstring &name); + void SetPlayerSkin(int iPad,DWORD dwSkinId); + void SetPlayerCape(int iPad,const wstring &name); + void SetPlayerCape(int iPad,DWORD dwCapeId); + void SetPlayerFavoriteSkin(int iPad, int iIndex,unsigned int uiSkinID); + unsigned int GetPlayerFavoriteSkin(int iPad,int iIndex); + unsigned char GetPlayerFavoriteSkinsPos(int iPad); + void SetPlayerFavoriteSkinsPos(int iPad,int iPos); + unsigned int GetPlayerFavoriteSkinsCount(int iPad); + void ValidateFavoriteSkins(int iPad); // check the DLC is available for the skins + + // Mash-up pack worlds hide/display + void HideMashupPackWorld(int iPad, unsigned int iMashupPackID); + void EnableMashupPackWorlds(int iPad); + unsigned int GetMashupPackWorlds(int iPad); + + // Minecraft language select + void SetMinecraftLanguage(int iPad, unsigned char ucLanguage); + unsigned char GetMinecraftLanguage(int iPad); + + + // 4J-PB - set a timer when the user navigates the quickselect, so we can bring the opacity back to defaults for a short time + unsigned int GetOpacityTimer(int iPad) { return m_uiOpacityCountDown[iPad]; } + void SetOpacityTimer(int iPad) { m_uiOpacityCountDown[iPad]=120; } // 6 seconds + void TickOpacityTimer(int iPad) { if(m_uiOpacityCountDown[iPad]>0) m_uiOpacityCountDown[iPad]--;} + +public: + wstring GetPlayerSkinName(int iPad); + DWORD GetPlayerSkinId(int iPad); + wstring GetPlayerCapeName(int iPad); + DWORD GetPlayerCapeId(int iPad); + DWORD GetAdditionalModelParts(int iPad); + void CheckGameSettingsChanged(bool bOverride5MinuteTimer=false, int iPad=XUSER_INDEX_ANY); + void ApplyGameSettingsChanged(int iPad); + void ClearGameSettingsChangedFlag(int iPad); + void ActionGameSettings(int iPad,eGameSetting eVal); + unsigned int GetGameSettingsDebugMask(int iPad=-1,bool bOverridePlayer=false); + void SetGameSettingsDebugMask(int iPad, unsigned int uiVal); + void ActionDebugMask(int iPad, bool bSetAllClear=false); + + // + bool IsLocalMultiplayerAvailable(); + + // for sign in change monitoring + static void SignInChangeCallback(LPVOID pParam, bool bVal, unsigned int uiSignInData); + static void ClearSignInChangeUsersMask(); + static int SignoutExitWorldThreadProc( void* lpParameter ); + static int PrimaryPlayerSignedOutReturned(void *pParam, int iPad, const C4JStorage::EMessageResult); + static int EthernetDisconnectReturned(void *pParam, int iPad, const C4JStorage::EMessageResult); + static void ProfileReadErrorCallback(void *pParam); + + // FATAL LOAD ERRORS + virtual void FatalLoadError(); + + // Notifications from the game listener to be passed to the qnet listener + static void NotificationsCallback(LPVOID pParam,DWORD dwNotification, unsigned int uiParam); + + // for the ethernet being disconnected + static void LiveLinkChangeCallback(LPVOID pParam,BOOL bConnected); + bool GetLiveLinkRequired() {return m_bLiveLinkRequired;} + void SetLiveLinkRequired(bool required) {m_bLiveLinkRequired=required;} + + static void UpsellReturnedCallback(LPVOID pParam, eUpsellType type, eUpsellResponse result, int iUserData); + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + static int NowDisplayFullVersionPurchase(void *pParam, bool bContinue, int iPad); + static int MustSignInFullVersionPurchaseReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + static int MustSignInFullVersionPurchaseReturnedExitTrial(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +#ifdef _DEBUG_MENUS_ENABLED + bool DebugSettingsOn() { return m_bDebugOptions;} +#else + bool DebugSettingsOn() { return false;} +#endif + void SetDebugSequence(const char *pchSeq); + static int DebugInputCallback(LPVOID pParam); + //bool UploadFileToGlobalStorage(int iQuadrant, C4JStorage::eGlobalStorage eStorageFacility, wstring *wsFile ); + + // Installed DLC + bool StartInstallDLCProcess(int iPad); + static int DLCInstalledCallback(LPVOID pParam,int iOfferC,int iPad); + void HandleDLCLicenseChange(); + static int DLCMountedCallback(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicenceMask); + void MountNextDLC(int iPad); + //static int DLCReadCallback(LPVOID pParam,C4JStorage::DLC_FILE_DETAILS *pDLCData); + void HandleDLC(DLCPack *pack); + bool DLCInstallPending() {return m_bDLCInstallPending;} + bool DLCInstallProcessCompleted() {return m_bDLCInstallProcessCompleted;} + void ClearDLCInstalled() { m_bDLCInstallProcessCompleted=false;} + static int MarketplaceCountsCallback(LPVOID pParam,C4JStorage::DLC_TMS_DETAILS *,int iPad); + + bool AlreadySeenCreditText(const wstring &wstemp); + + void ClearNewDLCAvailable(void) { m_bNewDLCAvailable=false; m_bSeenNewDLCTip=true;} + bool GetNewDLCAvailable() { return m_bNewDLCAvailable;} + void DisplayNewDLCTipAgain() { m_bSeenNewDLCTip=false;} + bool DisplayNewDLCTip() { if(!m_bSeenNewDLCTip) { m_bSeenNewDLCTip=true; return true;} else return false;} + + // functions to store launch data, and to exit the game - required due to possibly being on a demo disc + virtual void StoreLaunchData(); + virtual void ExitGame(); + + bool isXuidNotch(PlayerUID xuid); + bool isXuidDeadmau5(PlayerUID xuid); + + void AddMemoryTextureFile(const wstring &wName, PBYTE pbData, DWORD dwBytes); + void RemoveMemoryTextureFile(const wstring &wName); + void GetMemFileDetails(const wstring &wName,PBYTE *ppbData,DWORD *pdwBytes); + bool IsFileInMemoryTextures(const wstring &wName); + + // Texture Pack Data files (icon, banner, comparison shot & text) + void AddMemoryTPDFile(int iConfig,PBYTE pbData,DWORD dwBytes); + void RemoveMemoryTPDFile(int iConfig); + bool IsFileInTPD(int iConfig); + void GetTPD(int iConfig,PBYTE *ppbData,DWORD *pdwBytes); + int GetTPDSize() {return m_MEM_TPD.size();} +#ifndef __PS3__ + int GetTPConfigVal(WCHAR *pwchDataFile); +#endif + + bool DefaultCapeExists(); + //void InstallDefaultCape(); // attempt to install the default cape once per game launch + + // invites + //void ProcessInvite(JoinFromInviteData *pJoinData); + void ProcessInvite(DWORD dwUserIndex, DWORD dwLocalUsersMask, const INVITE_INFO * pInviteInfo); + + // Add credits for DLC installed + void AddCreditText(LPCWSTR lpStr); + +private: + PlayerUID m_xuidNotch; +#ifdef _DURANGO + unordered_map m_GTS_Files; +#else + unordered_map m_GTS_Files; +#endif + + // for storing memory textures - player skin + unordered_map m_MEM_Files; + // for storing texture pack data files + unordered_map m_MEM_TPD; + CRITICAL_SECTION csMemFilesLock; // For locking access to the above map + CRITICAL_SECTION csMemTPDLock; // For locking access to the above map + + VNOTIFICATIONS m_vNotifications; + +public: + // launch data + BYTE* m_pLaunchData; + DWORD m_dwLaunchDataSize; + +public: + // BAN LIST + void AddLevelToBannedLevelList(int iPad,PlayerUID xuid, char *pszLevelName, bool bWriteToTMS); + bool IsInBannedLevelList(int iPad, PlayerUID xuid, char *pszLevelName); + void RemoveLevelFromBannedLevelList(int iPad, PlayerUID xuid, char *pszLevelName); + void InvalidateBannedList(int iPad); + void SetUniqueMapName(char *pszUniqueMapName); + char *GetUniqueMapName(void); +#ifdef _XBOX_ONE + void AddLevelToBannedLevelList(int iPad, PBANNEDLISTDATA pBannedListData, bool bWriteToTMS); +#endif + + +public: + bool GetResourcesLoaded() {return m_bResourcesLoaded;} + void SetResourcesLoaded(bool bVal) {m_bResourcesLoaded=bVal;} + +public: + bool m_bGameStarted; + bool m_bIntroRunning; + bool m_bTutorialMode; + bool m_bIsAppPaused; + + bool m_bChangingSessionType; + bool m_bReallyChangingSessionType; + + bool m_bDisplayFullVersionPurchase; // for after signing in during the trial, and trying to unlock full version on an upsell + + void loadMediaArchive(); + void loadStringTable(); + +protected: + ArchiveFile *m_mediaArchive; + StringTable *m_stringTable; + +public: + int getArchiveFileSize(const wstring &filename); + bool hasArchiveFile(const wstring &filename); + byteArray getArchiveFile(const wstring &filename); + +private: + + static int BannedLevelDialogReturned(void *pParam,int iPad,const C4JStorage::EMessageResult); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + VBANNEDLIST *m_vBannedListA[XUSER_MAX_COUNT]; + + void HandleButtonPresses(int iPad); + + bool m_bResourcesLoaded; + + // Global string table for this application. + //CXuiStringTable StringTable; + + + // Container scene for some menu + +// CXuiScene debugContainerScene; + + + //bool m_bSplitScreenEnabled; + + +#ifdef _CONTENT_PACKAGE +#ifndef _FINAL_BUILD + bool m_bPartnernetPasswordRunning; +#endif +#endif + + eGameMode m_eGameMode; // single or multiplayer + + static unsigned int m_uiLastSignInData; + + // We've got sizeof(GAME_SETTINGS) bytes reserved at the start of the gamedefined data per player for settings + GAME_SETTINGS *GameSettingsA[XUSER_MAX_COUNT]; + + // For promo work + bool m_bLoadSavesFromFolderEnabled; + + // For debugging + bool m_bWriteSavesToFolderEnabled; + bool m_bMobsDontAttack; + bool m_bUseDPadForDebug; + bool m_bMobsDontTick; + bool m_bFreezePlayers; + + // 4J : WESTY : For taking screen shots. + //bool m_bInterfaceRenderingOff; + //bool m_bHandRenderingOff; + + DisconnectPacket::eDisconnectReason m_disconnectReason; + +public: + virtual void RunFrame() {}; + + + + static const DWORD m_dwOfferID = 0x00000001; + +// timer + void InitTime(); + void UpdateTime(); + + // trial timer + void SetTrialTimerStart(void); + float getTrialTimer(void); + + // notifications from the game for qnet + VNOTIFICATIONS *GetNotifications() {return &m_vNotifications;} + +private: + + + // To avoid problems with threads being kicked off from xuis that alter things that may be in progress within the run_middle, + // we'll action these at the end of the game loop + eXuiAction m_eXuiAction[XUSER_MAX_COUNT]; + eTMSAction m_eTMSAction[XUSER_MAX_COUNT]; + LPVOID m_eXuiActionParam[XUSER_MAX_COUNT]; + eXuiAction m_eGlobalXuiAction; + eXuiServerAction m_eXuiServerAction[XUSER_MAX_COUNT]; + LPVOID m_eXuiServerActionParam[XUSER_MAX_COUNT]; + eXuiServerAction m_eGlobalXuiServerAction; + + bool m_bLiveLinkRequired; + + static int UnlockFullExitReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int UnlockFullSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int UnlockFullInviteReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TrialOverReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitAndJoinFromInvite(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitAndJoinFromInviteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitAndJoinFromInviteAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitAndJoinFromInviteDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int FatalErrorDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + JoinFromInviteData m_InviteData; + bool m_bDebugOptions; // toggle debug things on or off + + // Trial timer + float m_fTrialTimerStart,mfTrialPausedTime; + typedef struct TimeInfo + { + LARGE_INTEGER qwTime; + LARGE_INTEGER qwAppTime; + + float fAppTime; + float fElapsedTime; + float fSecsPerTick; + } TIMEINFO; + + TimeInfo m_Time; + +protected: + static const int MAX_TIPS_GAMETIP = 50; + static const int MAX_TIPS_TRIVIATIP = 20; + static TIPSTRUCT m_GameTipA[MAX_TIPS_GAMETIP]; + static TIPSTRUCT m_TriviaTipA[MAX_TIPS_TRIVIATIP]; + static Random *TipRandom; +public: + void InitialiseTips(); + UINT GetNextTip(); + int GetHTMLColour(eMinecraftColour colour); + int GetHTMLColor(eMinecraftColour colour) { return GetHTMLColour(colour); } + int GetHTMLFontSize(EHTMLFontSize size); + wstring FormatHTMLString(int iPad, const wstring &desc, int shadowColour = 0xFFFFFFFF); + wstring GetActionReplacement(int iPad, unsigned char ucAction); + wstring GetVKReplacement(unsigned int uiVKey); + wstring GetIconReplacement(unsigned int uiIcon); + + float getAppTime() { return m_Time.fAppTime; } + void UpdateTrialPausedTimer() { mfTrialPausedTime+= m_Time.fElapsedTime;} + + static int RemoteSaveThreadProc( void* lpParameter ); + static void ExitGameFromRemoteSave( LPVOID lpParameter ); + static int ExitGameFromRemoteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +private: + UINT m_TipIDA[MAX_TIPS_GAMETIP+MAX_TIPS_TRIVIATIP]; + UINT m_uiCurrentTip; + static int TipsSortFunction(const void* a, const void* b); + + // XML +public: + + // Hold a vector of terrain feature positions + void AddTerrainFeaturePosition(_eTerrainFeatureType,int,int); + void ClearTerrainFeaturePosition(); + _eTerrainFeatureType IsTerrainFeature(int x,int z); + bool GetTerrainFeaturePosition(_eTerrainFeatureType eType, int *pX, int *pZ); + std::vector m_vTerrainFeatures; + + static HRESULT RegisterMojangData(WCHAR *, PlayerUID, WCHAR *, WCHAR *); + MOJANG_DATA *GetMojangDataForXuid(PlayerUID xuid); + static HRESULT RegisterConfigValues(WCHAR *pType, int iValue); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + HRESULT RegisterDLCData(char *pchDLCName, unsigned int uiSortIndex, char *pchImageURL); + bool GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,ULONGLONG *pullVal); + DLC_INFO *GetDLCInfoForTrialOfferID(ULONGLONG ullOfferID_Trial); + DLC_INFO *GetDLCInfoForFullOfferID(ULONGLONG ullOfferID_Full); +#elif defined(_XBOX_ONE) + static HRESULT RegisterDLCData(eDLCContentType, WCHAR *, WCHAR *, WCHAR *, WCHAR *, int, unsigned int); + //bool GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,WCHAR *pwchProductId); + bool GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,wstring &wsProductId); + DLC_INFO *GetDLCInfoForFullOfferID(WCHAR *pwchProductId); + DLC_INFO *GetDLCInfoForProductName(WCHAR *pwchProductName); +#else + static HRESULT RegisterDLCData(WCHAR *, WCHAR *, int, __uint64, __uint64, WCHAR *, unsigned int, int, WCHAR *pDataFile); + bool GetDLCFullOfferIDForSkinID(const wstring &FirstSkin,ULONGLONG *pullVal); + DLC_INFO *GetDLCInfoForTrialOfferID(ULONGLONG ullOfferID_Trial); + DLC_INFO *GetDLCInfoForFullOfferID(ULONGLONG ullOfferID_Full); +#endif + + unsigned int GetDLCCreditsCount(); + SCreditTextItemDef * GetDLCCredits(int iIndex); + +// TMS + void ReadDLCFileFromTMS(int iPad,eTMSAction action, bool bCallback=false); + void ReadXuidsFileFromTMS(int iPad,eTMSAction action,bool bCallback=false); + + // images for save thumbnail/social post + virtual void CaptureSaveThumbnail() =0; + virtual void GetSaveThumbnail(PBYTE*,DWORD*)=0; + virtual void ReleaseSaveThumbnail()=0; + virtual void GetScreenshot(int iPad,PBYTE *pbData,DWORD *pdwSize)=0; + + virtual void ReadBannedList(int iPad, eTMSAction action=(eTMSAction)0, bool bCallback=false)=0; + +private: + + std::vector vDLCCredits; + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + static unordered_map MojangData; + static unordered_map DLCTextures_PackID; // for mash-up packs & texture packs + static unordered_map DLCInfo; + static unordered_map DLCInfo_SkinName; // skin name, full offer id +#elif defined(_DURANGO) + static unordered_map MojangData; + static unordered_map DLCTextures_PackID; // for mash-up packs & texture packs + //static unordered_map DLCInfo_Trial; // full offerid, dlc_info + static unordered_map DLCInfo_Full; // full offerid, dlc_info + static unordered_map DLCInfo_SkinName; // skin name, full offer id +#else + static unordered_map MojangData; + static unordered_map DLCTextures_PackID; // for mash-up packs & texture packs + static unordered_map DLCInfo_Trial; // full offerid, dlc_info + static unordered_map DLCInfo_Full; // full offerid, dlc_info + static unordered_map DLCInfo_SkinName; // skin name, full offer id +#endif +// bool m_bRead_TMS_XUIDS_XML; // track whether we have already read the TMS xuids.xml file +// bool m_bRead_TMS_DLCINFO_XML; // track whether we have already read the TMS DLC.xml file + + bool m_bDefaultCapeInstallAttempted; // have we attempted to install the default cape from tms + + //bool m_bwasHidingGui; // 4J Stu - Removed 1.8.2 bug fix (TU6) as not needed + bool m_bDLCInstallProcessCompleted; + bool m_bDLCInstallPending; + int m_iTotalDLC; + int m_iTotalDLCInstalled; + +public: + // 4J Stu - We need to be able to detect when a guest player signs in or out causing other guest players to change their xuid + // The simplest way to do this is to check if their guest number has changed, so store the last known one here + // 4J Stu - Now storing the whole XUSER_SIGNIN_INFO so we can detect xuid changes + XUSER_SIGNIN_INFO m_currentSigninInfo[XUSER_MAX_COUNT]; + + //void OverrideFontRenderer(bool set, bool immediate = true); +// void ToggleFontRenderer() { OverrideFontRenderer(!m_bFontRendererOverridden,false); } + BANNEDLIST BannedListA[XUSER_MAX_COUNT]; + +private: +// XUI_FontRenderer *m_fontRenderer; +// bool m_bFontRendererOverridden; +// bool m_bOverrideFontRenderer; + + + bool m_bRead_BannedListA[XUSER_MAX_COUNT]; + char m_pszUniqueMapName[14]; + bool m_BanListCheck[XUSER_MAX_COUNT]; + +public: + void SetBanListCheck(int iPad,bool bVal) {m_BanListCheck[iPad]=bVal;} + bool GetBanListCheck(int iPad) { return m_BanListCheck[iPad];} +// AUTOSAVE +public: + void SetAutosaveTimerTime(void); + bool AutosaveDue(void); + unsigned int SecondsToAutosave(); +private: + unsigned int m_uiAutosaveTimer; + unsigned int m_uiOpacityCountDown[XUSER_MAX_COUNT]; + + // DLC + bool m_bNewDLCAvailable; + bool m_bSeenNewDLCTip; + + // Host options +private: + unsigned int m_uiGameHostSettings; + static unsigned char m_szPNG[8]; + + unsigned int FromBigEndian(unsigned int uiValue); + +public: + + + void SetGameHostOption(eGameHostOption eVal,unsigned int uiVal); + void SetGameHostOption(unsigned int &uiHostSettings, eGameHostOption eVal,unsigned int uiVal); + unsigned int GetGameHostOption(eGameHostOption eVal); + unsigned int GetGameHostOption(unsigned int uiHostSettings, eGameHostOption eVal); + + void SetResetNether(bool bResetNether) {m_bResetNether=bResetNether;} + bool GetResetNether() {return m_bResetNether;} + bool CanRecordStatsAndAchievements(); + + // World seed from png image + void GetImageTextData(PBYTE pbImageData, DWORD dwImageBytes,unsigned char *pszSeed,unsigned int &uiHostOptions,bool &bHostOptionsRead,DWORD &uiTexturePack); + unsigned int CreateImageTextData(PBYTE bTextMetadata, __int64 seed, bool hasSeed, unsigned int uiHostOptions, unsigned int uiTexturePackId); + + // Game rules + GameRuleManager m_gameRules; + +public: + void processSchematics(LevelChunk *levelChunk); + void processSchematicsLighting(LevelChunk *levelChunk); + void loadDefaultGameRules(); + vector *getLevelGenerators() { return m_gameRules.getLevelGenerators(); } + void setLevelGenerationOptions(LevelGenerationOptions *levelGen); + LevelRuleset *getGameRuleDefinitions() { return m_gameRules.getGameRuleDefinitions(); } + LevelGenerationOptions *getLevelGenerationOptions() { return m_gameRules.getLevelGenerationOptions(); } + LPCWSTR GetGameRulesString(const wstring &key); + +private: + BYTE m_playerColours[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's + unsigned int m_playerGamePrivileges[MINECRAFT_NET_MAX_PLAYERS]; + +public: + void UpdatePlayerInfo(BYTE networkSmallId, SHORT playerColourIndex, unsigned int playerGamePrivileges); + short GetPlayerColour(BYTE networkSmallId); + unsigned int GetPlayerPrivileges(BYTE networkSmallId); + + wstring getEntityName(eINSTANCEOF type); + + + + unsigned int AddDLCRequest(eDLCMarketplaceType eContentType, bool bPromote=false); + bool RetrieveNextDLCContent(); + bool CheckTMSDLCCanStop(); + static int DLCOffersReturned(void *pParam, int iOfferC, DWORD dwType, int iPad); + DWORD GetDLCContentType(eDLCContentType eType) { return m_dwContentTypeA[eType];} + eDLCContentType Find_eDLCContentType(DWORD dwType); + int GetDLCOffersCount() { return m_iDLCOfferC;} + bool DLCContentRetrieved(eDLCMarketplaceType eType); + void TickDLCOffersRetrieved(); + void ClearAndResetDLCDownloadQueue(); + bool RetrieveNextTMSPPContent(); + void TickTMSPPFilesRetrieved(); + void ClearTMSPPFilesRetrieved(); + unsigned int AddTMSPPFileTypeRequest(eDLCContentType eType, bool bPromote=false); + int GetDLCInfoTexturesOffersCount(); +#if defined( __PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + DLC_INFO *GetDLCInfo(int iIndex); + DLC_INFO *GetDLCInfo(char *); + DLC_INFO *GetDLCInfoFromTPackID(int iTPID); + bool GetDLCNameForPackID(const int iPackID,char **ppchKeyID); + char * GetDLCInfoTextures(int iIndex); + int GetDLCInfoCount(); +#else + +#ifdef _XBOX_ONE + static int TMSPPFileReturned(LPVOID pParam,int iPad,int iUserData,LPVOID, WCHAR *wchFilename); + unordered_map *GetDLCInfo(); +#else + static int TMSPPFileReturned(LPVOID pParam,int iPad,int iUserData,C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename); +#endif + DLC_INFO *GetDLCInfoTrialOffer(int iIndex); + DLC_INFO *GetDLCInfoFullOffer(int iIndex); + + int GetDLCInfoTrialOffersCount(); + int GetDLCInfoFullOffersCount(); +#ifdef _XBOX_ONE + bool GetDLCFullOfferIDForPackID(const int iPackID,wstring &wsProductId); + wstring GetDLCInfoTexturesFullOffer(int iIndex); + +#else + bool GetDLCFullOfferIDForPackID(const int iPackID,ULONGLONG *pullVal); + ULONGLONG GetDLCInfoTexturesFullOffer(int iIndex); +#endif +#endif + + void SetCorruptSaveDeleted(bool bVal) {m_bCorruptSaveDeleted=bVal;} + bool GetCorruptSaveDeleted(void) {return m_bCorruptSaveDeleted;} + + void EnterSaveNotificationSection(); + void LeaveSaveNotificationSection(); +private: + CRITICAL_SECTION m_saveNotificationCriticalSection; + int m_saveNotificationDepth; + // Download Status + + //Request current_download; + vector m_DLCDownloadQueue; + vector m_TMSPPDownloadQueue; + static DWORD m_dwContentTypeA[e_Marketplace_MAX]; + int m_iDLCOfferC; + bool m_bAllDLCContentRetrieved; + bool m_bAllTMSContentRetrieved; + bool m_bTickTMSDLCFiles; + CRITICAL_SECTION csDLCDownloadQueue; + CRITICAL_SECTION csTMSPPDownloadQueue; + CRITICAL_SECTION csAdditionalModelParts; + CRITICAL_SECTION csAdditionalSkinBoxes; + CRITICAL_SECTION csAnimOverrideBitmask; + bool m_bCorruptSaveDeleted; + + DWORD m_dwAdditionalModelParts[XUSER_MAX_COUNT]; + + BYTE *m_pBannedListFileBuffer; + DWORD m_dwBannedListFileSize; + +public: + DWORD m_dwDLCFileSize; + BYTE *m_pDLCFileBuffer; + +// static int CallbackReadXuidsFileFromTMS(LPVOID lpParam, WCHAR *wchFilename, int iPad, bool bResult, int iAction); +// static int CallbackDLCFileFromTMS(LPVOID lpParam, WCHAR *wchFilename, int iPad, bool bResult, int iAction); +// static int CallbackBannedListFileFromTMS(LPVOID lpParam, WCHAR *wchFilename, int iPad, bool bResult, int iAction); + + // Storing additional model parts per skin texture + void SetAdditionalSkinBoxes(DWORD dwSkinID, SKIN_BOX *SkinBoxA, DWORD dwSkinBoxC); + vector * SetAdditionalSkinBoxes(DWORD dwSkinID, vector *pvSkinBoxA); + vector *GetAdditionalModelParts(DWORD dwSkinID); + vector *GetAdditionalSkinBoxes(DWORD dwSkinID); + void SetAnimOverrideBitmask(DWORD dwSkinID,unsigned int uiAnimOverrideBitmask); + unsigned int GetAnimOverrideBitmask(DWORD dwSkinID); + + static DWORD getSkinIdFromPath(const wstring &skin); + static wstring getSkinPathFromId(DWORD skinId); + + virtual int LoadLocalTMSFile(WCHAR *wchTMSFile)=0; + virtual int LoadLocalTMSFile(WCHAR *wchTMSFile, eFileExtensionType eExt)=0; + virtual void FreeLocalTMSFiles(eTMSFileType eType)=0; + virtual int GetLocalTMSFileIndex(WCHAR *wchTMSFile,bool bFilenameIncludesExtension,eFileExtensionType eEXT)=0; + + virtual bool GetTMSGlobalFileListRead() { return true;} + virtual bool GetTMSDLCInfoRead() { return true;} + virtual bool GetTMSXUIDsFileRead() { return true;} + + bool GetBanListRead(int iPad) { return m_bRead_BannedListA[iPad];} + void SetBanListRead(int iPad,bool bVal) { m_bRead_BannedListA[iPad]=bVal;} + void ClearBanList(int iPad) { BannedListA[iPad].pBannedList=NULL;BannedListA[iPad].dwBytes=0;} + + DWORD GetRequiredTexturePackID() {return m_dwRequiredTexturePackID;} + void SetRequiredTexturePackID(DWORD dwID) {m_dwRequiredTexturePackID=dwID;} + + virtual void GetFileFromTPD(eTPDFileType eType,PBYTE pbData,DWORD dwBytes,PBYTE *ppbData,DWORD *pdwBytes ) {*ppbData = NULL; *pdwBytes = 0;} + + //XTITLE_DEPLOYMENT_TYPE getDeploymentType() { return m_titleDeploymentType; } + +private: + // vector of additional skin model parts, indexed by the skin texture id + unordered_map *> m_AdditionalModelParts; + unordered_map *> m_AdditionalSkinBoxes; + unordered_map m_AnimOverrides; + + + bool m_bResetNether; + DWORD m_dwRequiredTexturePackID; +#ifdef _XBOX_ONE + vector m_vTMSPPData; +#endif + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + C4JStorage::eOptionsCallback m_eOptionsStatusA[XUSER_MAX_COUNT]; + +#ifdef __ORBIS__ + int m_eOptionsBlocksRequiredA[XUSER_MAX_COUNT]; +#endif +#endif + + + // 4J-PB - language and locale functions +public: + + void LocaleAndLanguageInit(); + void getLocale(vector &vecWstrLocales); + DWORD get_eMCLang(WCHAR *pwchLocale); + DWORD get_xcLang(WCHAR *pwchLocale); + + void SetTickTMSDLCFiles(bool bVal); + + wstring getFilePath(DWORD packId, wstring filename, bool bAddDataFolder); + +private: + unordered_mapm_localeA; + unordered_mapm_eMCLangA; + unordered_mapm_xcLangA; + wstring getRootPath(DWORD packId, bool allowOverride, bool bAddDataFolder); +public: + +#ifdef _XBOX +// bool m_bTransferSavesToXboxOne; +// unsigned int m_uiTransferSlotC; + +#elif defined (__PS3__) + +#elif defined _DURANGO + +#elif defined _WINDOWS64 + //CMinecraftAudio audio; +#else // PS4 + +#endif + +#ifdef _XBOX_ONE +public: + void SetReachedMainMenu(); + bool HasReachedMainMenu(); +private: + bool m_hasReachedMainMenu; +#endif +}; + +//singleton +//extern CMinecraftApp app; diff --git a/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.cpp new file mode 100644 index 0000000..eabc140 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.enchantment.h" +#include "AddEnchantmentRuleDefinition.h" + +AddEnchantmentRuleDefinition::AddEnchantmentRuleDefinition() +{ + m_enchantmentId = m_enchantmentLevel = 0; +} + +void AddEnchantmentRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + GameRuleDefinition::writeAttributes(dos, numAttributes + 2); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_enchantmentId); + dos->writeUTF( _toString( m_enchantmentId ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_enchantmentLevel); + dos->writeUTF( _toString( m_enchantmentLevel ) ); +} + +void AddEnchantmentRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"enchantmentId") == 0) + { + int value = _fromString(attributeValue); + if(value < 0) value = 0; + if(value >= 256) value = 255; + m_enchantmentId = value; + app.DebugPrintf("AddEnchantmentRuleDefinition: Adding parameter enchantmentId=%d\n",m_enchantmentId); + } + else if(attributeName.compare(L"enchantmentLevel") == 0) + { + int value = _fromString(attributeValue); + if(value < 0) value = 0; + m_enchantmentLevel = value; + app.DebugPrintf("AddEnchantmentRuleDefinition: Adding parameter enchantmentLevel=%d\n",m_enchantmentLevel); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool AddEnchantmentRuleDefinition::enchantItem(shared_ptr item) +{ + bool enchanted = false; + if (item != NULL) + { + // 4J-JEV: Ripped code from enchantmenthelpers + // Maybe we want to add an addEnchantment method to EnchantmentHelpers + if (item->id == Item::enchantedBook_Id) + { + Item::enchantedBook->addEnchantment( item, new EnchantmentInstance(m_enchantmentId, m_enchantmentLevel) ); + } + else if (item->isEnchantable()) + { + Enchantment *e = Enchantment::enchantments[m_enchantmentId]; + + if(e != NULL && e->category->canEnchant(item->getItem())) + { + int level = min(e->getMaxLevel(), m_enchantmentLevel); + item->enchant(e, m_enchantmentLevel); + enchanted = true; + } + } + } + return enchanted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.h b/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.h new file mode 100644 index 0000000..3beece1 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/AddEnchantmentRuleDefinition.h @@ -0,0 +1,23 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class ItemInstance; + +class AddEnchantmentRuleDefinition : public GameRuleDefinition +{ +private: + int m_enchantmentId; + int m_enchantmentLevel; + +public: + AddEnchantmentRuleDefinition(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_AddEnchantment; } + + virtual void writeAttributes(DataOutputStream *, UINT numAttrs); + + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool enchantItem(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.cpp new file mode 100644 index 0000000..0d14884 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "AddItemRuleDefinition.h" +#include "AddEnchantmentRuleDefinition.h" + +AddItemRuleDefinition::AddItemRuleDefinition() +{ + m_itemId = m_quantity = m_auxValue = m_dataTag = 0; + m_slot = -1; +} + +void AddItemRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_itemId); + dos->writeUTF( _toString( m_itemId ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_quantity); + dos->writeUTF( _toString( m_quantity ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_auxValue); + dos->writeUTF( _toString( m_auxValue ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_dataTag); + dos->writeUTF( _toString( m_dataTag ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_slot); + dos->writeUTF( _toString( m_slot ) ); +} + +void AddItemRuleDefinition::getChildren(vector *children) +{ + GameRuleDefinition::getChildren( children ); + for (AUTO_VAR(it, m_enchantments.begin()); it != m_enchantments.end(); it++) + children->push_back( *it ); +} + +GameRuleDefinition *AddItemRuleDefinition::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_AddEnchantment) + { + rule = new AddEnchantmentRuleDefinition(); + m_enchantments.push_back((AddEnchantmentRuleDefinition *)rule); + } + else + { +#ifndef _CONTENT_PACKAGE + //wprintf(L"AddItemRuleDefinition: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + return rule; +} + +void AddItemRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"itemId") == 0) + { + int value = _fromString(attributeValue); + m_itemId = value; + //app.DebugPrintf(2,"AddItemRuleDefinition: Adding parameter itemId=%d\n",m_itemId); + } + else if(attributeName.compare(L"quantity") == 0) + { + int value = _fromString(attributeValue); + m_quantity = value; + //app.DebugPrintf(2,"AddItemRuleDefinition: Adding parameter quantity=%d\n",m_quantity); + } + else if(attributeName.compare(L"auxValue") == 0) + { + int value = _fromString(attributeValue); + m_auxValue = value; + //app.DebugPrintf(2,"AddItemRuleDefinition: Adding parameter auxValue=%d\n",m_auxValue); + } + else if(attributeName.compare(L"dataTag") == 0) + { + int value = _fromString(attributeValue); + m_dataTag = value; + //app.DebugPrintf(2,"AddItemRuleDefinition: Adding parameter dataTag=%d\n",m_dataTag); + } + else if(attributeName.compare(L"slot") == 0) + { + int value = _fromString(attributeValue); + m_slot = value; + //app.DebugPrintf(2,"AddItemRuleDefinition: Adding parameter slot=%d\n",m_slot); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool AddItemRuleDefinition::addItemToContainer(shared_ptr container, int slotId) +{ + bool added = false; + if(Item::items[m_itemId] != NULL) + { + int quantity = min(m_quantity, Item::items[m_itemId]->getMaxStackSize()); + shared_ptr newItem = shared_ptr(new ItemInstance(m_itemId,quantity,m_auxValue) ); + newItem->set4JData(m_dataTag); + + for(AUTO_VAR(it, m_enchantments.begin()); it != m_enchantments.end(); ++it) + { + (*it)->enchantItem(newItem); + } + + if(m_slot >= 0 && m_slot < container->getContainerSize() ) + { + container->setItem( m_slot, newItem ); + added = true; + } + else if(slotId >= 0 && slotId < container->getContainerSize() ) + { + container->setItem( slotId, newItem ); + added = true; + } + else if(dynamic_pointer_cast(container) != NULL) + { + added = dynamic_pointer_cast(container)->add(newItem); + } + } + return added; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.h b/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.h new file mode 100644 index 0000000..602f2d8 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/AddItemRuleDefinition.h @@ -0,0 +1,30 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class Container; +class AddEnchantmentRuleDefinition; + +class AddItemRuleDefinition : public GameRuleDefinition +{ +private: + int m_itemId; + int m_quantity; + int m_auxValue; + int m_dataTag; + int m_slot; + vector m_enchantments; + +public: + AddItemRuleDefinition(); + + virtual void writeAttributes(DataOutputStream *, UINT numAttributes); + virtual void getChildren(vector *children); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_AddItem; } + + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool addItemToContainer(shared_ptr container, int slotId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.cpp new file mode 100644 index 0000000..aa4a8fb --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.cpp @@ -0,0 +1,249 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "ApplySchematicRuleDefinition.h" +#include "LevelGenerationOptions.h" +#include "ConsoleSchematicFile.h" + +ApplySchematicRuleDefinition::ApplySchematicRuleDefinition(LevelGenerationOptions *levelGenOptions) +{ + m_levelGenOptions = levelGenOptions; + m_location = Vec3::newPermanent(0,0,0); + m_locationBox = NULL; + m_totalBlocksChanged = 0; + m_totalBlocksChangedLighting = 0; + m_rotation = ConsoleSchematicFile::eSchematicRot_0; + m_completed = false; + m_dimension = 0; + m_schematic = NULL; +} + +ApplySchematicRuleDefinition::~ApplySchematicRuleDefinition() +{ + app.DebugPrintf("Deleting ApplySchematicRuleDefinition.\n"); + if(!m_completed) m_levelGenOptions->releaseSchematicFile(m_schematicName); + m_schematic = NULL; + delete m_location; +} + +void ApplySchematicRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_filename); + dos->writeUTF(m_schematicName); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x); + dos->writeUTF(_toString(m_location->x)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y); + dos->writeUTF(_toString(m_location->y)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z); + dos->writeUTF(_toString(m_location->z)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_rot); + + switch (m_rotation) + { + case ConsoleSchematicFile::eSchematicRot_0: dos->writeUTF(_toString( 0 )); break; + case ConsoleSchematicFile::eSchematicRot_90: dos->writeUTF(_toString( 90 )); break; + case ConsoleSchematicFile::eSchematicRot_180: dos->writeUTF(_toString( 180 )); break; + case ConsoleSchematicFile::eSchematicRot_270: dos->writeUTF(_toString( 270 )); break; + } +} + +void ApplySchematicRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"filename") == 0) + { + m_schematicName = attributeValue; + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter filename=%s\n",m_schematicName.c_str()); + + if(!m_schematicName.empty()) + { + if(m_schematicName.substr( m_schematicName.length() - 4, m_schematicName.length()).compare(L".sch") != 0) + { + m_schematicName.append(L".sch"); + } + m_schematic = m_levelGenOptions->getSchematicFile(m_schematicName); + } + } + else if(attributeName.compare(L"x") == 0) + { + m_location->x = _fromString(attributeValue); + if( ((int)abs(m_location->x))%2 != 0) m_location->x -=1; + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter x=%f\n",m_location->x); + } + else if(attributeName.compare(L"y") == 0) + { + m_location->y = _fromString(attributeValue); + if( ((int)abs(m_location->y))%2 != 0) m_location->y -= 1; + if(m_location->y < 0) m_location->y = 0; + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter y=%f\n",m_location->y); + } + else if(attributeName.compare(L"z") == 0) + { + m_location->z = _fromString(attributeValue); + if(((int)abs(m_location->z))%2 != 0) m_location->z -= 1; + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter z=%f\n",m_location->z); + } + else if(attributeName.compare(L"rot") == 0) + { + int degrees = _fromString(attributeValue); + + while(degrees < 0) degrees += 360; + while(degrees >= 360) degrees -= 360; + float quad = degrees/90; + degrees = (int)(quad + 0.5f); + switch(degrees) + { + case 1: + m_rotation = ConsoleSchematicFile::eSchematicRot_90; + break; + case 2: + m_rotation = ConsoleSchematicFile::eSchematicRot_180; + break; + case 3: + case 4: + m_rotation = ConsoleSchematicFile::eSchematicRot_270; + break; + case 0: + default: + m_rotation = ConsoleSchematicFile::eSchematicRot_0; + break; + }; + + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter rot=%d\n",m_rotation); + } + else if(attributeName.compare(L"dim") == 0) + { + m_dimension = _fromString(attributeValue); + if(m_dimension > 1 || m_dimension < -1) m_dimension = 0; + //app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter dimension=%d\n",m_dimension); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +void ApplySchematicRuleDefinition::updateLocationBox() +{ + if(m_schematic == NULL) m_schematic = m_levelGenOptions->getSchematicFile(m_schematicName); + + m_locationBox = AABB::newPermanent(0,0,0,0,0,0); + + m_locationBox->x0 = m_location->x; + m_locationBox->y0 = m_location->y; + m_locationBox->z0 = m_location->z; + + m_locationBox->y1 = m_location->y + m_schematic->getYSize(); + + switch(m_rotation) + { + case ConsoleSchematicFile::eSchematicRot_90: + case ConsoleSchematicFile::eSchematicRot_270: + m_locationBox->x1 = m_location->x + m_schematic->getZSize(); + m_locationBox->z1 = m_location->z + m_schematic->getXSize(); + break; + case ConsoleSchematicFile::eSchematicRot_0: + case ConsoleSchematicFile::eSchematicRot_180: + default: + m_locationBox->x1 = m_location->x + m_schematic->getXSize(); + m_locationBox->z1 = m_location->z + m_schematic->getZSize(); + break; + }; +} + +void ApplySchematicRuleDefinition::processSchematic(AABB *chunkBox, LevelChunk *chunk) +{ + if( m_completed ) return; + if(chunk->level->dimension->id != m_dimension) return; + + PIXBeginNamedEvent(0, "Processing ApplySchematicRuleDefinition"); + if(m_schematic == NULL) m_schematic = m_levelGenOptions->getSchematicFile(m_schematicName); + + if(m_locationBox == NULL) updateLocationBox(); + if(chunkBox->intersects( m_locationBox )) + { + m_locationBox->y1 = min((double)Level::maxBuildHeight, m_locationBox->y1 ); + +#ifdef _DEBUG + app.DebugPrintf("Applying schematic %ls to chunk (%d,%d)\n",m_schematicName.c_str(),chunk->x, chunk->z); +#endif + PIXBeginNamedEvent(0,"Applying blocks and data"); + m_totalBlocksChanged += m_schematic->applyBlocksAndData(chunk, chunkBox, m_locationBox, m_rotation); + PIXEndNamedEvent(); + + // Add the tileEntities + PIXBeginNamedEvent(0,"Applying tile entities"); + m_schematic->applyTileEntities(chunk, chunkBox, m_locationBox, m_rotation); + PIXEndNamedEvent(); + + // TODO This does not take into account things that go outside the bounds of the world + int targetBlocks = (m_locationBox->x1 - m_locationBox->x0) + * (m_locationBox->y1 - m_locationBox->y0) + * (m_locationBox->z1 - m_locationBox->z0); + if( (m_totalBlocksChanged == targetBlocks) && (m_totalBlocksChangedLighting == targetBlocks) ) + { + m_completed = true; + //m_levelGenOptions->releaseSchematicFile(m_schematicName); + //m_schematic = NULL; + } + } + PIXEndNamedEvent(); +} + +void ApplySchematicRuleDefinition::processSchematicLighting(AABB *chunkBox, LevelChunk *chunk) +{ + if( m_completed ) return; + if(chunk->level->dimension->id != m_dimension) return; + + PIXBeginNamedEvent(0, "Processing ApplySchematicRuleDefinition (lighting)"); + if(m_schematic == NULL) m_schematic = m_levelGenOptions->getSchematicFile(m_schematicName); + + if(m_locationBox == NULL) updateLocationBox(); + if(chunkBox->intersects( m_locationBox )) + { + m_locationBox->y1 = min((double)Level::maxBuildHeight, m_locationBox->y1 ); + +#ifdef _DEBUG + app.DebugPrintf("Applying schematic %ls to chunk (%d,%d)\n",m_schematicName.c_str(),chunk->x, chunk->z); +#endif + PIXBeginNamedEvent(0,"Patching lighting"); + m_totalBlocksChangedLighting += m_schematic->applyLighting(chunk, chunkBox, m_locationBox, m_rotation); + PIXEndNamedEvent(); + + // TODO This does not take into account things that go outside the bounds of the world + int targetBlocks = (m_locationBox->x1 - m_locationBox->x0) + * (m_locationBox->y1 - m_locationBox->y0) + * (m_locationBox->z1 - m_locationBox->z0); + if( (m_totalBlocksChanged == targetBlocks) && (m_totalBlocksChangedLighting == targetBlocks) ) + { + m_completed = true; + //m_levelGenOptions->releaseSchematicFile(m_schematicName); + //m_schematic = NULL; + } + } + PIXEndNamedEvent(); +} + +bool ApplySchematicRuleDefinition::checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1) +{ + if( m_locationBox == NULL ) updateLocationBox(); + return m_locationBox->intersects(x0,y0,z0,x1,y1,z1); +} + +int ApplySchematicRuleDefinition::getMinY() +{ + if( m_locationBox == NULL ) updateLocationBox(); + return m_locationBox->y0; +} + +void ApplySchematicRuleDefinition::reset() +{ + m_totalBlocksChanged = 0; + m_totalBlocksChangedLighting = 0; + m_completed = false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.h b/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.h new file mode 100644 index 0000000..21c42de --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ApplySchematicRuleDefinition.h @@ -0,0 +1,51 @@ +#pragma once +#include "GameRuleDefinition.h" +#include "ConsoleSchematicFile.h" + +class AABB; +class Vec3; +class LevelChunk; +class LevelGenerationOptions; +class GRFObject; + +class ApplySchematicRuleDefinition : public GameRuleDefinition +{ +private: + LevelGenerationOptions *m_levelGenOptions; + wstring m_schematicName; + ConsoleSchematicFile *m_schematic; + Vec3 *m_location; + AABB *m_locationBox; + ConsoleSchematicFile::ESchematicRotation m_rotation; + int m_dimension; + + __int64 m_totalBlocksChanged; + __int64 m_totalBlocksChangedLighting; + bool m_completed; + + void updateLocationBox(); +public: + ApplySchematicRuleDefinition(LevelGenerationOptions *levelGenOptions); + ~ApplySchematicRuleDefinition(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_ApplySchematic; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + void processSchematic(AABB *chunkBox, LevelChunk *chunk); + void processSchematicLighting(AABB *chunkBox, LevelChunk *chunk); + + bool checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1); + int getMinY(); + + bool isComplete() { return m_completed; } + + wstring getSchematicName() { return m_schematicName; } + + /** 4J-JEV: + * This GameRuleDefinition contains limited game state. + * Reset any state to how it should be before a new game. + */ + void reset(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/BiomeOverride.cpp b/Minecraft.Client/Common/GameRules/BiomeOverride.cpp new file mode 100644 index 0000000..22cc0c7 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/BiomeOverride.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "BiomeOverride.h" + +BiomeOverride::BiomeOverride() +{ + m_tile = 0; + m_topTile = 0; + m_biomeId = 0; +} + +void BiomeOverride::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 3); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_biomeId); + dos->writeUTF(_toString(m_biomeId)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_tileId); + dos->writeUTF(_toString(m_tile)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_topTileId); + dos->writeUTF(_toString(m_topTile)); +} + +void BiomeOverride::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"tileId") == 0) + { + int value = _fromString(attributeValue); + m_tile = value; + app.DebugPrintf("BiomeOverride: Adding parameter tileId=%d\n",m_tile); + } + else if(attributeName.compare(L"topTileId") == 0) + { + int value = _fromString(attributeValue); + m_topTile = value; + app.DebugPrintf("BiomeOverride: Adding parameter topTileId=%d\n",m_topTile); + } + else if(attributeName.compare(L"biomeId") == 0) + { + int value = _fromString(attributeValue); + m_biomeId = value; + app.DebugPrintf("BiomeOverride: Adding parameter biomeId=%d\n",m_biomeId); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool BiomeOverride::isBiome(int id) +{ + return m_biomeId == id; +} + +void BiomeOverride::getTileValues(BYTE &tile, BYTE &topTile) +{ + if(m_tile != 0) tile = (BYTE)m_tile; + if(m_topTile != 0) topTile = (BYTE)m_topTile; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/BiomeOverride.h b/Minecraft.Client/Common/GameRules/BiomeOverride.h new file mode 100644 index 0000000..5ad9263 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/BiomeOverride.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "GameRuleDefinition.h" + +class BiomeOverride : public GameRuleDefinition +{ +private: + BYTE m_topTile; + BYTE m_tile; + int m_biomeId; + +public: + BiomeOverride(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_BiomeOverride; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool isBiome(int id); + void getTileValues(BYTE &tile, BYTE &topTile); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.cpp new file mode 100644 index 0000000..66abefb --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "..\..\WstringLookup.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "CollectItemRuleDefinition.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\Connection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +CollectItemRuleDefinition::CollectItemRuleDefinition() +{ + m_itemId = 0; + m_auxValue = 0; + m_quantity = 0; +} + +CollectItemRuleDefinition::~CollectItemRuleDefinition() +{ +} + +void CollectItemRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + GameRuleDefinition::writeAttributes(dos, numAttributes + 3); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_itemId); + dos->writeUTF( _toString( m_itemId ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_auxValue); + dos->writeUTF( _toString( m_auxValue ) ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_quantity); + dos->writeUTF( _toString( m_quantity ) ); +} + +void CollectItemRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"itemId") == 0) + { + m_itemId = _fromString(attributeValue); + app.DebugPrintf("CollectItemRule: Adding parameter itemId=%d\n",m_itemId); + } + else if(attributeName.compare(L"auxValue") == 0) + { + m_auxValue = _fromString(attributeValue); + app.DebugPrintf("CollectItemRule: Adding parameter m_auxValue=%d\n",m_auxValue); + } + else if(attributeName.compare(L"quantity") == 0) + { + m_quantity = _fromString(attributeValue); + app.DebugPrintf("CollectItemRule: Adding parameter m_quantity=%d\n",m_quantity); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +int CollectItemRuleDefinition::getGoal() +{ + return m_quantity; +} + +int CollectItemRuleDefinition::getProgress(GameRule *rule) +{ + GameRule::ValueType value = rule->getParameter(L"iQuantity"); + return value.i; +} + +void CollectItemRuleDefinition::populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule) +{ + GameRule::ValueType value; + value.i = 0; + rule->setParameter(L"iQuantity",value); + + GameRuleDefinition::populateGameRule(type, rule); +} + +bool CollectItemRuleDefinition::onCollectItem(GameRule *rule, shared_ptr item) +{ + bool statusChanged = false; + if(item != NULL && item->id == m_itemId && item->getAuxValue() == m_auxValue && item->get4JData() == m_4JDataValue) + { + if(!getComplete(rule)) + { + GameRule::ValueType value = rule->getParameter(L"iQuantity"); + int quantityCollected = (value.i += item->count); + rule->setParameter(L"iQuantity",value); + + statusChanged = true; + + if(quantityCollected >= m_quantity) + { + setComplete(rule, true); + app.DebugPrintf("Completed CollectItemRule with info - itemId:%d, auxValue:%d, quantity:%d, dataTag:%d\n", m_itemId,m_auxValue,m_quantity,m_4JDataValue); + + if(rule->getConnection() != NULL) + { + rule->getConnection()->send( shared_ptr( new UpdateGameRuleProgressPacket(getActionType(), this->m_descriptionId, m_itemId, m_auxValue, this->m_4JDataValue,NULL,0))); + } + } + } + } + return statusChanged; +} + +wstring CollectItemRuleDefinition::generateXml(shared_ptr item) +{ + // 4J Stu - This should be kept in sync with the GameRulesDefinition.xsd + wstring xml = L""; + if(item != NULL) + { + xml = L"(item->id) + L"\" quantity=\"SET\" descriptionName=\"OPTIONAL\" promptName=\"OPTIONAL\""; + if(item->getAuxValue() != 0) xml += L" auxValue=\"" + _toString(item->getAuxValue()) + L"\""; + if(item->get4JData() != 0) xml += L" dataTag=\"" + _toString(item->get4JData()) + L"\""; + xml += L"/>\n"; + } + return xml; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.h b/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.h new file mode 100644 index 0000000..5ee6f4c --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CollectItemRuleDefinition.h @@ -0,0 +1,40 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class Pos; +class UseTileRuleDefinition; +class ItemInstance; + +class CollectItemRuleDefinition : public GameRuleDefinition +{ +private: + // These values should map directly to the xsd definition for this Rule + int m_itemId; + unsigned char m_auxValue; + int m_quantity; + +public: + CollectItemRuleDefinition(); + ~CollectItemRuleDefinition(); + + ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_CollectItemRule; } + + virtual void writeAttributes(DataOutputStream *, UINT numAttributes); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + virtual int getGoal(); + virtual int getProgress(GameRule *rule); + + virtual int getIcon() { return m_itemId; } + virtual int getAuxValue() { return m_auxValue; } + + void populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule); + + bool onCollectItem(GameRule *rule, shared_ptr item); + + static wstring generateXml(shared_ptr item); + +private: + //static wstring generateXml(CollectItemRuleDefinition *ruleDef); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.cpp new file mode 100644 index 0000000..adaf70c --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "CompleteAllRuleDefinition.h" +#include "ConsoleGameRules.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Connection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +void CompleteAllRuleDefinition::getChildren(vector *children) +{ + CompoundGameRuleDefinition::getChildren(children); +} + +bool CompleteAllRuleDefinition::onUseTile(GameRule *rule, int tileId, int x, int y, int z) +{ + bool statusChanged = CompoundGameRuleDefinition::onUseTile(rule,tileId,x,y,z); + if(statusChanged) updateStatus(rule); + return statusChanged; +} + +bool CompleteAllRuleDefinition::onCollectItem(GameRule *rule, shared_ptr item) +{ + bool statusChanged = CompoundGameRuleDefinition::onCollectItem(rule,item); + if(statusChanged) updateStatus(rule); + return statusChanged; +} + +void CompleteAllRuleDefinition::updateStatus(GameRule *rule) +{ + int goal = 0; + int progress = 0; + for(AUTO_VAR(it, rule->m_parameters.begin()); it != rule->m_parameters.end(); ++it) + { + if(it->second.isPointer) + { + goal += it->second.gr->getGameRuleDefinition()->getGoal(); + progress += it->second.gr->getGameRuleDefinition()->getProgress(it->second.gr); + } + } + if(rule->getConnection() != NULL) + { + PacketData data; + data.goal = goal; + data.progress = progress; + + int icon = -1; + int auxValue = 0; + + if(m_lastRuleStatusChanged != NULL) + { + icon = m_lastRuleStatusChanged->getIcon(); + auxValue = m_lastRuleStatusChanged->getAuxValue(); + m_lastRuleStatusChanged = NULL; + } + rule->getConnection()->send( shared_ptr( new UpdateGameRuleProgressPacket(getActionType(), this->m_descriptionId,icon, auxValue, 0,&data,sizeof(PacketData)))); + } + app.DebugPrintf("Updated CompleteAllRule - Completed %d of %d\n", progress, goal); +} + +wstring CompleteAllRuleDefinition::generateDescriptionString(const wstring &description, void *data, int dataLength) +{ + PacketData *values = (PacketData *)data; + wstring newDesc = description; + newDesc = replaceAll(newDesc,L"{*progress*}",_toString(values->progress)); + newDesc = replaceAll(newDesc,L"{*goal*}",_toString(values->goal)); + return newDesc; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.h b/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.h new file mode 100644 index 0000000..b2cb884 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CompleteAllRuleDefinition.h @@ -0,0 +1,26 @@ +#pragma once + +#include "CompoundGameRuleDefinition.h" + +class CompleteAllRuleDefinition : public CompoundGameRuleDefinition +{ +private: + typedef struct _packetData + { + int goal; + int progress; + } PacketData; + +public: + ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_CompleteAllRule; } + + virtual void getChildren(vector *children); + + virtual bool onUseTile(GameRule *rule, int tileId, int x, int y, int z); + virtual bool onCollectItem(GameRule *rule, shared_ptr item); + + static wstring generateDescriptionString(const wstring &description, void *data, int dataLength); + +private: + void updateStatus(GameRule *rule); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.cpp new file mode 100644 index 0000000..0481a54 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "CompoundGameRuleDefinition.h" +#include "ConsoleGameRules.h" + +CompoundGameRuleDefinition::CompoundGameRuleDefinition() +{ + m_lastRuleStatusChanged = NULL; +} + +CompoundGameRuleDefinition::~CompoundGameRuleDefinition() +{ + for(AUTO_VAR(it, m_children.begin()); it != m_children.end(); ++it) + { + delete (*it); + } +} + +void CompoundGameRuleDefinition::getChildren(vector *children) +{ + GameRuleDefinition::getChildren(children); + for (AUTO_VAR(it, m_children.begin()); it != m_children.end(); it++) + children->push_back(*it); +} + +GameRuleDefinition *CompoundGameRuleDefinition::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_CompleteAllRule) + { + rule = new CompleteAllRuleDefinition(); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_CollectItemRule) + { + rule = new CollectItemRuleDefinition(); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_UseTileRule) + { + rule = new UseTileRuleDefinition(); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_UpdatePlayerRule) + { + rule = new UpdatePlayerRuleDefinition(); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"CompoundGameRuleDefinition: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + if(rule != NULL) m_children.push_back(rule); + return rule; +} + +void CompoundGameRuleDefinition::populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule) +{ + GameRule *newRule = NULL; + int i = 0; + for(AUTO_VAR(it, m_children.begin()); it != m_children.end(); ++it) + { + newRule = new GameRule(*it, rule->getConnection() ); + (*it)->populateGameRule(type,newRule); + + GameRule::ValueType value; + value.gr = newRule; + value.isPointer = true; + + // Somehow add the newRule to the current rule + rule->setParameter(L"rule" + _toString(i),value); + ++i; + } + GameRuleDefinition::populateGameRule(type, rule); +} + +bool CompoundGameRuleDefinition::onUseTile(GameRule *rule, int tileId, int x, int y, int z) +{ + bool statusChanged = false; + for(AUTO_VAR(it, rule->m_parameters.begin()); it != rule->m_parameters.end(); ++it) + { + if(it->second.isPointer) + { + bool changed = it->second.gr->getGameRuleDefinition()->onUseTile(it->second.gr,tileId,x,y,z); + if(!statusChanged && changed) + { + m_lastRuleStatusChanged = it->second.gr->getGameRuleDefinition(); + statusChanged = true; + } + } + } + return statusChanged; +} + +bool CompoundGameRuleDefinition::onCollectItem(GameRule *rule, shared_ptr item) +{ + bool statusChanged = false; + for(AUTO_VAR(it, rule->m_parameters.begin()); it != rule->m_parameters.end(); ++it) + { + if(it->second.isPointer) + { + bool changed = it->second.gr->getGameRuleDefinition()->onCollectItem(it->second.gr,item); + if(!statusChanged && changed) + { + m_lastRuleStatusChanged = it->second.gr->getGameRuleDefinition(); + statusChanged = true; + } + } + } + return statusChanged; +} + +void CompoundGameRuleDefinition::postProcessPlayer(shared_ptr player) +{ + for(AUTO_VAR(it, m_children.begin()); it != m_children.end(); ++it) + { + (*it)->postProcessPlayer(player); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.h b/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.h new file mode 100644 index 0000000..bfedfd0 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/CompoundGameRuleDefinition.h @@ -0,0 +1,23 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class CompoundGameRuleDefinition : public GameRuleDefinition +{ +protected: + vector m_children; +protected: + GameRuleDefinition *m_lastRuleStatusChanged; +public: + CompoundGameRuleDefinition(); + virtual ~CompoundGameRuleDefinition(); + + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + + virtual void populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule); + + virtual bool onUseTile(GameRule *rule, int tileId, int x, int y, int z); + virtual bool onCollectItem(GameRule *rule, shared_ptr item); + virtual void postProcessPlayer(shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ConsoleGameRules.h b/Minecraft.Client/Common/GameRules/ConsoleGameRules.h new file mode 100644 index 0000000..41c5e55 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleGameRules.h @@ -0,0 +1,32 @@ +#pragma once +#include "ConsoleGameRulesConstants.h" + +#include "GameRuleManager.h" + +#include "GameRule.h" + +#include "GameRuleDefinition.h" + +#include "LevelRuleset.h" +#include "NamedAreaRuleDefinition.h" + +#include "CollectItemRuleDefinition.h" +#include "CompleteAllRuleDefinition.h" +#include "CompoundGameRuleDefinition.h" +#include "UseTileRuleDefinition.h" +#include "UpdatePlayerRuleDefinition.h" +#include "AddItemRuleDefinition.h" +#include "AddEnchantmentRuleDefinition.h" + +#include "LevelGenerationOptions.h" +#include "ApplySchematicRuleDefinition.h" +#include "ConsoleGenerateStructure.h" +#include "ConsoleGenerateStructureAction.h" +#include "XboxStructureActionGenerateBox.h" +#include "XboxStructureActionPlaceBlock.h" +#include "XboxStructureActionPlaceContainer.h" +#include "XboxStructureActionPlaceSpawner.h" +#include "BiomeOverride.h" +#include "StartFeature.h" + +#include "GameRulesInstance.h" diff --git a/Minecraft.Client/Common/GameRules/ConsoleGameRulesConstants.h b/Minecraft.Client/Common/GameRules/ConsoleGameRulesConstants.h new file mode 100644 index 0000000..a7111f0 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleGameRulesConstants.h @@ -0,0 +1,119 @@ +#pragma once + +//#include " + +class ConsoleGameRules +{ +public: + enum EGameRuleType + { + eGameRuleType_Invalid = -1, + eGameRuleType_Root = 0, // This is the top level rule that defines a game mode, this is used to generate data for new players + + eGameRuleType_LevelGenerationOptions, + eGameRuleType_ApplySchematic, + eGameRuleType_GenerateStructure, + eGameRuleType_GenerateBox, + eGameRuleType_PlaceBlock, + eGameRuleType_PlaceContainer, + eGameRuleType_PlaceSpawner, + eGameRuleType_BiomeOverride, + eGameRuleType_StartFeature, + + eGameRuleType_AddItem, + eGameRuleType_AddEnchantment, + + eGameRuleType_LevelRules, + eGameRuleType_NamedArea, + + eGameRuleType_UseTileRule, + eGameRuleType_CollectItemRule, + eGameRuleType_CompleteAllRule, + eGameRuleType_UpdatePlayerRule, + + eGameRuleType_Count + }; + + enum EGameRuleAttr + { + eGameRuleAttr_Invalid = -1, + + eGameRuleAttr_descriptionName = 0, + eGameRuleAttr_promptName, + eGameRuleAttr_dataTag, + + eGameRuleAttr_enchantmentId, + eGameRuleAttr_enchantmentLevel, + + eGameRuleAttr_itemId, + eGameRuleAttr_quantity, + eGameRuleAttr_auxValue, + eGameRuleAttr_slot, + + eGameRuleAttr_name, + + eGameRuleAttr_food, + eGameRuleAttr_health, + + eGameRuleAttr_tileId, + eGameRuleAttr_useCoords, + + eGameRuleAttr_seed, + eGameRuleAttr_flatworld, + + eGameRuleAttr_filename, + eGameRuleAttr_rot, + + eGameRuleAttr_data, + eGameRuleAttr_block, + eGameRuleAttr_entity, + + eGameRuleAttr_facing, + + eGameRuleAttr_edgeTile, + eGameRuleAttr_fillTile, + eGameRuleAttr_skipAir, + + eGameRuleAttr_x, + eGameRuleAttr_x0, + eGameRuleAttr_x1, + + eGameRuleAttr_y, + eGameRuleAttr_y0, + eGameRuleAttr_y1, + + eGameRuleAttr_z, + eGameRuleAttr_z0, + eGameRuleAttr_z1, + + eGameRuleAttr_chunkX, + eGameRuleAttr_chunkZ, + + eGameRuleAttr_yRot, + + eGameRuleAttr_spawnX, + eGameRuleAttr_spawnY, + eGameRuleAttr_spawnZ, + + eGameRuleAttr_orientation, + eGameRuleAttr_dimension, + + eGameRuleAttr_topTileId, + eGameRuleAttr_biomeId, + + eGameRuleAttr_feature, + + eGameRuleAttr_Count + }; + + static void write(DataOutputStream *dos, ConsoleGameRules::EGameRuleType eType) + { + dos->writeInt(eType); + } + + static void write(DataOutputStream *dos, ConsoleGameRules::EGameRuleAttr eAttr) + { + dos->writeInt( eGameRuleType_Count + eAttr ); + } + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.cpp b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.cpp new file mode 100644 index 0000000..0476b0e --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" +#include "ConsoleGenerateStructure.h" +#include "ConsoleGameRules.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.levelgen.structure.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.h" + +ConsoleGenerateStructure::ConsoleGenerateStructure() : StructurePiece(0) +{ + m_x = m_y = m_z = 0; + boundingBox = NULL; + orientation = Direction::NORTH; + m_dimension = 0; +} + +void ConsoleGenerateStructure::getChildren(vector *children) +{ + GameRuleDefinition::getChildren(children); + + for(AUTO_VAR(it, m_actions.begin()); it != m_actions.end(); it++) + children->push_back( *it ); +} + +GameRuleDefinition *ConsoleGenerateStructure::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_GenerateBox) + { + rule = new XboxStructureActionGenerateBox(); + m_actions.push_back((XboxStructureActionGenerateBox *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_PlaceBlock) + { + rule = new XboxStructureActionPlaceBlock(); + m_actions.push_back((XboxStructureActionPlaceBlock *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_PlaceContainer) + { + rule = new XboxStructureActionPlaceContainer(); + m_actions.push_back((XboxStructureActionPlaceContainer *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_PlaceSpawner) + { + rule = new XboxStructureActionPlaceSpawner(); + m_actions.push_back((XboxStructureActionPlaceSpawner *)rule); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"ConsoleGenerateStructure: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + return rule; +} + +void ConsoleGenerateStructure::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x); + dos->writeUTF(_toString(m_x)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y); + dos->writeUTF(_toString(m_y)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z); + dos->writeUTF(_toString(m_z)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_orientation); + dos->writeUTF(_toString(orientation)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_dimension); + dos->writeUTF(_toString(m_dimension)); +} + +void ConsoleGenerateStructure::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"x") == 0) + { + int value = _fromString(attributeValue); + m_x = value; + app.DebugPrintf("ConsoleGenerateStructure: Adding parameter x=%d\n",m_x); + } + else if(attributeName.compare(L"y") == 0) + { + int value = _fromString(attributeValue); + m_y = value; + app.DebugPrintf("ConsoleGenerateStructure: Adding parameter y=%d\n",m_y); + } + else if(attributeName.compare(L"z") == 0) + { + int value = _fromString(attributeValue); + m_z = value; + app.DebugPrintf("ConsoleGenerateStructure: Adding parameter z=%d\n",m_z); + } + else if(attributeName.compare(L"orientation") == 0) + { + int value = _fromString(attributeValue); + orientation = value; + app.DebugPrintf("ConsoleGenerateStructure: Adding parameter orientation=%d\n",orientation); + } + else if(attributeName.compare(L"dim") == 0) + { + m_dimension = _fromString(attributeValue); + if(m_dimension > 1 || m_dimension < -1) m_dimension = 0; + app.DebugPrintf("ApplySchematicRuleDefinition: Adding parameter dimension=%d\n",m_dimension); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +BoundingBox* ConsoleGenerateStructure::getBoundingBox() +{ + if(boundingBox == NULL) + { + // Find the max bounds + int maxX, maxY, maxZ; + maxX = maxY = maxZ = 1; + for(AUTO_VAR(it, m_actions.begin()); it != m_actions.end(); ++it) + { + ConsoleGenerateStructureAction *action = *it; + maxX = max(maxX,action->getEndX()); + maxY = max(maxY,action->getEndY()); + maxZ = max(maxZ,action->getEndZ()); + } + + boundingBox = new BoundingBox(m_x, m_y, m_z, m_x + maxX, m_y + maxY, m_z + maxZ); + } + return boundingBox; +} + +bool ConsoleGenerateStructure::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if(level->dimension->id != m_dimension) return false; + + for(AUTO_VAR(it, m_actions.begin()); it != m_actions.end(); ++it) + { + ConsoleGenerateStructureAction *action = *it; + + switch(action->getActionType()) + { + case ConsoleGameRules::eGameRuleType_GenerateBox: + { + XboxStructureActionGenerateBox *genBox = (XboxStructureActionGenerateBox *)action; + genBox->generateBoxInLevel(this,level,chunkBB); + } + break; + case ConsoleGameRules::eGameRuleType_PlaceBlock: + { + XboxStructureActionPlaceBlock *pPlaceBlock = (XboxStructureActionPlaceBlock *)action; + pPlaceBlock->placeBlockInLevel(this,level,chunkBB); + } + break; + case ConsoleGameRules::eGameRuleType_PlaceContainer: + { + XboxStructureActionPlaceContainer *pPlaceContainer = (XboxStructureActionPlaceContainer *)action; + pPlaceContainer->placeContainerInLevel(this,level,chunkBB); + } + break; + case ConsoleGameRules::eGameRuleType_PlaceSpawner: + { + XboxStructureActionPlaceSpawner *pPlaceSpawner = (XboxStructureActionPlaceSpawner *)action; + pPlaceSpawner->placeSpawnerInLevel(this,level,chunkBB); + } + break; + }; + } + + return false; +} + +bool ConsoleGenerateStructure::checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1) +{ + return getBoundingBox()->intersects(x0,y0,z0,x1,y1,z1); +} + +int ConsoleGenerateStructure::getMinY() +{ + return getBoundingBox()->y0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.h b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.h new file mode 100644 index 0000000..5b97b10 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructure.h @@ -0,0 +1,38 @@ +#pragma once +#include "GameRuleDefinition.h" +#include "..\..\..\Minecraft.World\StructurePiece.h" + +class Level; +class Random; +class BoundingBox; +class ConsoleGenerateStructureAction; +class XboxStructureActionPlaceContainer; +class GRFObject; + +class ConsoleGenerateStructure : public GameRuleDefinition, public StructurePiece +{ +private: + int m_x, m_y, m_z; + vector m_actions; + int m_dimension; +public: + ConsoleGenerateStructure(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_GenerateStructure; } + + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + // StructurePiece + virtual BoundingBox *getBoundingBox(); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + void createContainer(XboxStructureActionPlaceContainer *action, Level *level, BoundingBox *chunkBB); + + bool checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1); + + virtual int getMinY(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ConsoleGenerateStructureAction.h b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructureAction.h new file mode 100644 index 0000000..14eb2fd --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleGenerateStructureAction.h @@ -0,0 +1,11 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class ConsoleGenerateStructureAction : public GameRuleDefinition +{ +public: + virtual int getEndX() = 0; + virtual int getEndY() = 0; + virtual int getEndZ() = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp new file mode 100644 index 0000000..4a4e27b --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.cpp @@ -0,0 +1,1020 @@ +#include "stdafx.h" +#include +#include "..\..\..\Minecraft.World\com.mojang.nbt.h" +#include "..\..\..\Minecraft.World\System.h" +#include "ConsoleSchematicFile.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\compression.h" + +ConsoleSchematicFile::ConsoleSchematicFile() +{ + m_xSize = m_ySize = m_zSize = 0; + m_refCount = 1; + m_data.data = NULL; +} + +ConsoleSchematicFile::~ConsoleSchematicFile() +{ + app.DebugPrintf("Deleting schematic file\n"); + if(m_data.data != NULL) delete [] m_data.data; +} + +void ConsoleSchematicFile::save(DataOutputStream *dos) +{ + if(dos != NULL) + { + dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); + + dos->writeByte(APPROPRIATE_COMPRESSION_TYPE); + + dos->writeInt(m_xSize); + dos->writeInt(m_ySize); + dos->writeInt(m_zSize); + + byteArray ba(new BYTE[ m_data.length ], m_data.length); + Compression::getCompression()->CompressLZXRLE( ba.data, &ba.length, + m_data.data, m_data.length); + + dos->writeInt(ba.length); + dos->write(ba); + + save_tags(dos); + + delete [] ba.data; + } +} + +void ConsoleSchematicFile::load(DataInputStream *dis) +{ + if(dis != NULL) + { + // VERSION CHECK // + int version = dis->readInt(); + + Compression::ECompressionTypes compressionType = Compression::eCompressionType_LZXRLE; + + if (version > XBOX_SCHEMATIC_ORIGINAL_VERSION) // Or later versions + { + compressionType = (Compression::ECompressionTypes)dis->readByte(); + } + + if (version > XBOX_SCHEMATIC_CURRENT_VERSION) + assert(false && "Unrecognised schematic version!!"); + + m_xSize = dis->readInt(); + m_ySize = dis->readInt(); + m_zSize = dis->readInt(); + + int compressedSize = dis->readInt(); + byteArray compressedBuffer(compressedSize); + dis->readFully(compressedBuffer); + + if(m_data.data != NULL) + { + delete [] m_data.data; + m_data.data = NULL; + } + + if(compressionType == Compression::eCompressionType_None) + { + m_data = compressedBuffer; + } + else + { + unsigned int outputSize = m_xSize * m_ySize * m_zSize * 3/2; + m_data = byteArray(outputSize); + + switch(compressionType) + { + case Compression::eCompressionType_RLE: + Compression::getCompression()->DecompressRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); + break; + case APPROPRIATE_COMPRESSION_TYPE: + Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); + break; + default: + app.DebugPrintf("Unrecognized compression type for Schematic file (%d)\n", (int)compressionType); + Compression::getCompression()->SetDecompressionType( (Compression::ECompressionTypes)compressionType ); + Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); + Compression::getCompression()->SetDecompressionType( APPROPRIATE_COMPRESSION_TYPE ); + }; + + delete [] compressedBuffer.data; + } + + // READ TAGS // + CompoundTag *tag = NbtIo::read(dis); + ListTag *tileEntityTags = (ListTag *) tag->getList(L"TileEntities"); + if (tileEntityTags != NULL) + { + for (int i = 0; i < tileEntityTags->size(); i++) + { + CompoundTag *teTag = tileEntityTags->get(i); + shared_ptr te = TileEntity::loadStatic(teTag); + + if(te == NULL) + { +#ifndef _CONTENT_PACKAGE + app.DebugPrintf("ConsoleSchematicFile has read a NULL tile entity\n"); + __debugbreak(); +#endif + } + else + { + m_tileEntities.push_back(te); + } + } + } + ListTag *entityTags = (ListTag *) tag->getList(L"Entities"); + if (entityTags != NULL) + { + for (int i = 0; i < entityTags->size(); i++) + { + CompoundTag *eTag = entityTags->get(i); + eINSTANCEOF type = EntityIO::getType(eTag->getString(L"id")); + ListTag *pos = (ListTag *) eTag->getList(L"Pos"); + + double x = pos->get(0)->data; + double y = pos->get(1)->data; + double z = pos->get(2)->data; + + if( type == eTYPE_PAINTING || type == eTYPE_ITEM_FRAME ) + { + x = ((IntTag *) eTag->get(L"TileX") )->data; + y = ((IntTag *) eTag->get(L"TileY") )->data; + z = ((IntTag *) eTag->get(L"TileZ") )->data; + } +#ifdef _DEBUG + //app.DebugPrintf(1,"Loaded entity type %d at (%f,%f,%f)\n",(int)type,x,y,z); +#endif + m_entities.push_back( pair(Vec3::newPermanent(x,y,z),(CompoundTag *)eTag->copy())); + } + } + delete tag; + } +} + +void ConsoleSchematicFile::save_tags(DataOutputStream *dos) +{ + CompoundTag *tag = new CompoundTag(); + + ListTag *tileEntityTags = new ListTag(); + tag->put(L"TileEntities", tileEntityTags); + + for (AUTO_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end(); it++) + { + CompoundTag *cTag = new CompoundTag(); + (*it)->save(cTag); + tileEntityTags->add(cTag); + } + + ListTag *entityTags = new ListTag(); + tag->put(L"Entities", entityTags); + + for (AUTO_VAR(it, m_entities.begin()); it != m_entities.end(); it++) + entityTags->add( (CompoundTag *)(*it).second->copy() ); + + NbtIo::write(tag,dos); + delete tag; +} + +__int64 ConsoleSchematicFile::applyBlocksAndData(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) +{ + int xStart = max(destinationBox->x0, (double)chunk->x*16); + int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); + + int yStart = destinationBox->y0; + int yEnd = destinationBox->y1; + if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; + + int zStart = max(destinationBox->z0, (double)chunk->z*16); + int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 16); + +#ifdef _DEBUG + app.DebugPrintf("Range is (%d,%d,%d) to (%d,%d,%d)\n",xStart,yStart,zStart,xEnd-1,yEnd-1,zEnd-1); +#endif + + int rowBlocksIncluded = (yEnd-yStart)*(zEnd-zStart); + int blocksIncluded = (xEnd-xStart)*rowBlocksIncluded; + + int rowBlockCount = getYSize() * getZSize(); + int totalBlockCount = getXSize() * rowBlockCount; + + byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); + PIXBeginNamedEvent(0,"Getting block data"); + chunk->getBlockData(blockData); + PIXEndNamedEvent(); + byteArray dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + PIXBeginNamedEvent(0,"Getting Data data"); + chunk->getDataData(dataData); + PIXEndNamedEvent(); + + // Ignore light data + int blockLightP = -1; + int skyLightP = -1; + if( rot == eSchematicRot_90 || rot == eSchematicRot_180 || rot == eSchematicRot_270 ) + { + int schematicXRow = 0; + int schematicZRow = 0; + int blocksP = 0; + int dataP = 0; + + for(int x = xStart; x < xEnd; ++x) + { + int x0 = x - chunk->x*16; + int x1 = x0 + 1; + + for(int z = zStart; z < zEnd; ++z) + { + int z0 = z - chunk->z*16; + int z1 = z0 + 1; + + chunkCoordToSchematicCoord(destinationBox, x, z, rot, schematicXRow, schematicZRow); + blocksP = (schematicXRow*rowBlockCount) + (schematicZRow*getYSize()); + dataP = totalBlockCount + (blocksP)/2; + + ConsoleSchematicFile::setBlocksAndData(chunk,blockData,dataData,m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); + } + } + } + else if( rot == eSchematicRot_0 ) + { + // The initial pointer offsets for the different data types + int schematicXRow = xStart - destinationBox->x0; + int schematicZRow = zStart - destinationBox->z0; + int blocksP = (schematicXRow*rowBlockCount) + (schematicZRow*getYSize()); + int dataP = totalBlockCount + (schematicXRow*rowBlockCount + (schematicZRow*getYSize()))/2; + + for(int x = xStart; x < xEnd; ++x) + { + int x0 = x - chunk->x*16; + int x1 = x0 + 1; + + int z0 = zStart - chunk->z*16; + int z1 = zEnd - chunk->z*16; + + ConsoleSchematicFile::setBlocksAndData(chunk,blockData,dataData,m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); + // update all pointer positions + // For z start to z end + // Set blocks and data + // increment z by the right amount + blocksP += (rowBlockCount-rowBlocksIncluded); + dataP += (rowBlockCount-rowBlocksIncluded)/2; + } + } + else + { + app.DebugPrintf("ERROR: Rotation of block and data not implemented!!\n"); + } + + // 4J Stu - Hack for ME pack to replace sand with end stone in schematics + //for(int i = 0; i < blockData.length; ++i) + //{ + // if(blockData[i] == Tile::sand_Id || blockData[i] == Tile::sandStone_Id) + // { + // blockData[i] = Tile::whiteStone_Id; + // } + //} + + PIXBeginNamedEvent(0,"Setting Block data"); + chunk->setBlockData(blockData); + PIXEndNamedEvent(); + delete blockData.data; + chunk->recalcHeightmapOnly(); + PIXBeginNamedEvent(0,"Setting Data data"); + chunk->setDataData(dataData); + PIXEndNamedEvent(); + delete dataData.data; + + // A basic pass through to roughly do the lighting. At this point of post-processing, we don't have all the neighbouring chunks loaded in, + // so any lighting here should be things that won't propagate out of this chunk. + for( int xx = xStart ; xx < xEnd; xx++ ) + for( int y = yStart ; y < yEnd; y++ ) + for( int zz = zStart ; zz < zEnd; zz++ ) + { + int x = xx - chunk->x * 16; + int z = zz - chunk->z * 16; + chunk->setBrightness(LightLayer::Block,x,y,z,0); + if( chunk->getTile(x,y,z) ) + { + chunk->setBrightness(LightLayer::Sky,x,y,z,0); + } + else + { + if( chunk->isSkyLit(x,y,z) ) + { + chunk->setBrightness(LightLayer::Sky,x,y,z,15); + } + else + { + chunk->setBrightness(LightLayer::Sky,x,y,z,0); + } + } + } + + return blocksIncluded; +} + +// At the point that this is called, we have all the neighbouring chunks loaded in (and generally post-processed, apart from this lighting pass), so +// we can do the sort of lighting that might propagate out of the chunk. +__int64 ConsoleSchematicFile::applyLighting(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) +{ + int xStart = max(destinationBox->x0, (double)chunk->x*16); + int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); + + int yStart = destinationBox->y0; + int yEnd = destinationBox->y1; + if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; + + int zStart = max(destinationBox->z0, (double)chunk->z*16); + int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 16); + + int rowBlocksIncluded = (yEnd-yStart)*(zEnd-zStart); + int blocksIncluded = (xEnd-xStart)*rowBlocksIncluded; + + // Now actually do a checkLight on blocks that might need it, which should more accurately put everything in place + for( int xx = xStart ; xx < xEnd; xx++ ) + for( int y = yStart ; y < yEnd; y++ ) + for( int zz = zStart ; zz < zEnd; zz++ ) + { + int x = xx - chunk->x * 16; + int z = zz - chunk->z * 16; + + if( y <= chunk->getHeightmap( x, z ) ) + { + chunk->level->checkLight(LightLayer::Sky, xx, y, zz, true); + } + if( Tile::lightEmission[chunk->getTile(x,y,z)] ) + { + // Note that this lighting passes a rootOnlyEmissive flag of true, which means that only the location xx/y/zz is considered + // as possibly being a source of emissive light, not other tiles that we might encounter whilst propagating the light from + // the start location. If we don't do this, and Do encounter another emissive source in the radius of influence that the first + // light source had, then we'll start also lighting from that tile but won't actually be able to progatate that second light + // fully since checkLight only has a finite radius of 17 from the start position that it can light. Then when we do a checkLight + // on the second light later, it won't bother doing anything because the light level at the location of the tile itself will be correct. + chunk->level->checkLight(LightLayer::Block, xx, y, zz, true, true); + } + } + + return blocksIncluded; +} + +void ConsoleSchematicFile::chunkCoordToSchematicCoord(AABB *destinationBox, int chunkX, int chunkZ, ESchematicRotation rot, int &schematicX, int &schematicZ) +{ + switch(rot) + { + case eSchematicRot_90: + // schematicX decreases as chunkZ increases + // schematicZ increases as chunkX increases + schematicX = chunkZ - destinationBox->z0; + schematicZ = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); + break; + case eSchematicRot_180: + // schematicX decreases as chunkX increases + // schematicZ decreases as chunkZ increases + schematicX = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); + schematicZ = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); + break; + case eSchematicRot_270: + // schematicX increases as chunkZ increases + // shcematicZ decreases as chunkX increases + schematicX = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); + schematicZ = chunkX - destinationBox->x0; + break; + case eSchematicRot_0: + default: + // schematicX increases as chunkX increases + // schematicZ increases as chunkZ increases + schematicX = chunkX - destinationBox->x0; + schematicZ = chunkZ - destinationBox->z0; + break; + }; +} + +void ConsoleSchematicFile::schematicCoordToChunkCoord(AABB *destinationBox, double schematicX, double schematicZ, ESchematicRotation rot, double &chunkX, double &chunkZ) +{ + switch(rot) + { + case eSchematicRot_90: + // schematicX decreases as chunkZ increases + // schematicZ increases as chunkX increases + chunkX = (destinationBox->x1 - 1 - schematicZ); + chunkZ = schematicX + destinationBox->z0; + break; + case eSchematicRot_180: + // schematicX decreases as chunkX increases + // schematicZ decreases as chunkZ increases + chunkX = (destinationBox->x1 - 1 - schematicX); + chunkZ = (destinationBox->z1 - 1 - schematicZ); + break; + case eSchematicRot_270: + // schematicX increases as chunkZ increases + // shcematicZ decreases as chunkX increases + chunkX = schematicZ + destinationBox->x0; + chunkZ = (destinationBox->z1 - 1 - schematicX); + break; + case eSchematicRot_0: + default: + // schematicX increases as chunkX increases + // schematicZ increases as chunkZ increases + chunkX = schematicX + destinationBox->x0; + chunkZ = schematicZ + destinationBox->z0; + break; + }; +} + +void ConsoleSchematicFile::applyTileEntities(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) +{ + for(AUTO_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end();++it) + { + shared_ptr te = *it; + + double targetX = te->x; + double targetY = te->y + destinationBox->y0; + double targetZ = te->z; + + schematicCoordToChunkCoord(destinationBox, te->x, te->z, rot, targetX, targetZ); + + Vec3 *pos = Vec3::newTemp(targetX,targetY,targetZ); + if( chunkBox->containsIncludingLowerBound(pos) ) + { + shared_ptr teCopy = chunk->getTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15 ); + + if ( teCopy != NULL ) + { + CompoundTag *teData = new CompoundTag(); + te->save(teData); + + teCopy->load(teData); + + delete teData; + + // Adjust the tileEntity position to world coords from schematic co-ords + teCopy->x = targetX; + teCopy->y = targetY; + teCopy->z = targetZ; + + // Remove the current tile entity + //chunk->removeTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15 ); + } + else + { + teCopy = te->clone(); + + // Adjust the tileEntity position to world coords from schematic co-ords + teCopy->x = targetX; + teCopy->y = targetY; + teCopy->z = targetZ; + chunk->addTileEntity(teCopy); + } + + teCopy->setChanged(); + } + } + for(AUTO_VAR(it, m_entities.begin()); it != m_entities.end();) + { + Vec3 *source = it->first; + + double targetX = source->x; + double targetY = source->y + destinationBox->y0; + double targetZ = source->z; + schematicCoordToChunkCoord(destinationBox, source->x, source->z, rot, targetX, targetZ); + + // Add 0.01 as the AABB::contains function returns false if a value is <= the lower bound + Vec3 *pos = Vec3::newTemp(targetX+0.01,targetY+0.01,targetZ+0.01); + if( !chunkBox->containsIncludingLowerBound(pos) ) + { + ++it; + continue; + } + + CompoundTag *eTag = it->second; + shared_ptr e = EntityIO::loadStatic(eTag, NULL); + + if( e->GetType() == eTYPE_PAINTING ) + { + shared_ptr painting = dynamic_pointer_cast(e); + + double tileX = painting->xTile; + double tileZ = painting->zTile; + schematicCoordToChunkCoord(destinationBox, painting->xTile, painting->zTile, rot, tileX, tileZ); + + painting->yTile += destinationBox->y0; + painting->xTile = tileX; + painting->zTile = tileZ; + painting->setDir(painting->dir); + } + else if( e->GetType() == eTYPE_ITEM_FRAME ) + { + shared_ptr frame = dynamic_pointer_cast(e); + + double tileX = frame->xTile; + double tileZ = frame->zTile; + schematicCoordToChunkCoord(destinationBox, frame->xTile, frame->zTile, rot, tileX, tileZ); + + frame->yTile += destinationBox->y0; + frame->xTile = tileX; + frame->zTile = tileZ; + frame->setDir(frame->dir); + } + else + { + e->absMoveTo(targetX, targetY, targetZ,e->yRot,e->xRot); + } +#ifdef _DEBUG + app.DebugPrintf("Adding entity type %d at (%f,%f,%f)\n",e->GetType(),e->x,e->y,e->z); +#endif + e->setLevel(chunk->level); + e->resetSmallId(); + e->setDespawnProtected(); // default to being protected against despawning + chunk->level->addEntity(e); + + // 4J Stu - Until we can copy every type of entity, remove them from this vector + // This means that the entities will only exist in the first use of the schematic that is processed + //it = m_entities.erase(it); + ++it; + } +} + +void ConsoleSchematicFile::generateSchematicFile(DataOutputStream *dos, Level *level, int xStart, int yStart, int zStart, int xEnd, int yEnd, int zEnd, bool bSaveMobs, Compression::ECompressionTypes compressionType) +{ + assert(xEnd > xStart); + assert(yEnd > yStart); + assert(zEnd > zStart); + // 4J Stu - Enforce even numbered positions to start with to avoid problems with half-bytes in data + + // We want the start to be even + if(xStart > 0 && xStart%2 != 0) + xStart-=1; + else if(xStart < 0 && xStart%2 !=0) + xStart-=1; + if(yStart < 0) yStart = 0; + else if(yStart > 0 && yStart%2 != 0) + yStart-=1; + if(zStart > 0 && zStart%2 != 0) + zStart-=1; + else if(zStart < 0 && zStart%2 !=0) + zStart-=1; + + // We want the end to be odd to have a total size that is even + if(xEnd > 0 && xEnd%2 == 0) + xEnd+=1; + else if(xEnd < 0 && xEnd%2 ==0) + xEnd+=1; + if(yEnd > Level::maxBuildHeight) + yEnd = Level::maxBuildHeight; + else if(yEnd > 0 && yEnd%2 == 0) + yEnd+=1; + else if(yEnd < 0 && yEnd%2 ==0) + yEnd+=1; + if(zEnd > 0 && zEnd%2 == 0) + zEnd+=1; + else if(zEnd < 0 && zEnd%2 ==0) + zEnd+=1; + + int xSize = xEnd - xStart + 1; + int ySize = yEnd - yStart + 1; + int zSize = zEnd - zStart + 1; + + app.DebugPrintf("Generating schematic file for area (%d,%d,%d) to (%d,%d,%d), %dx%dx%d\n",xStart,yStart,zStart,xEnd,yEnd,zEnd,xSize,ySize,zSize); + + if(dos != NULL) dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); + + if(dos != NULL) dos->writeByte(compressionType); + + //Write xSize + if(dos != NULL) dos->writeInt(xSize); + + //Write ySize + if(dos != NULL) dos->writeInt(ySize); + + //Write zSize + if(dos != NULL) dos->writeInt(zSize); + + //byteArray rawBuffer = level->getBlocksAndData(xStart, yStart, zStart, xSize, ySize, zSize, false); + int xRowSize = ySize * zSize; + int blockCount = xSize * xRowSize; + byteArray result( blockCount * 3 / 2 ); + + // Position pointers into the data when not ordered by chunk + int p = 0; + int dataP = blockCount; + int blockLightP = -1; + int skyLightP = -1; + + int y0 = yStart; + int y1 = yStart + ySize; + if (y0 < 0) y0 = 0; + if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight; + + // Every x is a whole row + for(int xPos = xStart; xPos < xStart + xSize; ++xPos) + { + int xc = xPos >> 4; + + int x0 = xPos - xc * 16; + if (x0 < 0) x0 = 0; + int x1 = x0 + 1; + if (x1 > 16) x1 = 16; + + for(int zPos = zStart; zPos < zStart + zSize;) + { + int zc = zPos >> 4; + + int z0 = zStart - zc * 16; + int z1 = zStart + zSize - zc * 16; + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + getBlocksAndData(level->getChunk(xc, zc), &result, x0, y0, z0, x1, y1, z1, p, dataP, blockLightP, skyLightP); + zPos += (z1-z0); + } + } + +#ifndef _CONTENT_PACKAGE + if(p!=blockCount) __debugbreak(); +#endif + + // We don't know how this will compress - just make a fixed length buffer to initially decompress into + // Some small sets of blocks can end up compressing into something bigger than their source + unsigned int inputSize = blockCount * 3 / 2; + unsigned char *ucTemp = new unsigned char[inputSize]; + + switch(compressionType) + { + case Compression::eCompressionType_LZXRLE: + Compression::getCompression()->CompressLZXRLE( ucTemp, &inputSize, result.data, (unsigned int) result.length ); + break; + case Compression::eCompressionType_RLE: + Compression::getCompression()->CompressRLE( ucTemp, &inputSize, result.data, (unsigned int) result.length ); + break; + case Compression::eCompressionType_None: + default: + memcpy( ucTemp, result.data, inputSize ); + break; + }; + + delete [] result.data; + byteArray buffer = byteArray(ucTemp,inputSize); + + if(dos != NULL) dos->writeInt(inputSize); + if(dos != NULL) dos->write(buffer); + delete [] buffer.data; + + CompoundTag tag; + ListTag *tileEntitiesTag = new ListTag(L"tileEntities"); + + int xc0 = xStart >> 4; + int zc0 = zStart >> 4; + int xc1 = (xStart + xSize - 1) >> 4; + int zc1 = (zStart + zSize - 1) >> 4; + + for (int xc = xc0; xc <= xc1; xc++) + { + for (int zc = zc0; zc <= zc1; zc++) + { + vector > *tileEntities = getTileEntitiesInRegion(level->getChunk(xc, zc), xStart, yStart, zStart, xStart + xSize, yStart + ySize, zStart + zSize); + for(AUTO_VAR(it, tileEntities->begin()); it != tileEntities->end(); ++it) + { + shared_ptr te = *it; + CompoundTag *teTag = new CompoundTag(); + shared_ptr teCopy = te->clone(); + + // Adjust the tileEntity position to schematic coords from world co-ords + teCopy->x -= xStart; + teCopy->y -= yStart; + teCopy->z -= zStart; + teCopy->save(teTag); + tileEntitiesTag->add(teTag); + } + delete tileEntities; + } + } + tag.put(L"TileEntities", tileEntitiesTag); + + AABB *bb = AABB::newTemp(xStart,yStart,zStart,xEnd,yEnd,zEnd); + vector > *entities = level->getEntities(nullptr, bb); + ListTag *entitiesTag = new ListTag(L"entities"); + + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr e = *it; + + bool mobCanBeSaved = false; + if(bSaveMobs) + { + if( ( e->GetType() & eTYPE_MONSTER ) || ( e->GetType() & eTYPE_WATERANIMAL ) || ( e->GetType() & eTYPE_ANIMAL ) || + ( e->GetType() == eTYPE_CHICKEN ) || ( e->GetType() == eTYPE_WOLF ) || ( e->GetType() == eTYPE_VILLAGER) || ( e->GetType() == eTYPE_MUSHROOMCOW ) ) + { + mobCanBeSaved = true; + } + } + if(mobCanBeSaved || e->GetType() == eTYPE_MINECART || e->GetType() == eTYPE_BOAT || e->GetType() == eTYPE_PAINTING || e->GetType() == eTYPE_ITEM_FRAME) + { + CompoundTag *eTag = new CompoundTag(); + if( e->save(eTag) ) + { + ListTag *pos = (ListTag *) eTag->getList(L"Pos"); + + pos->get(0)->data -= xStart; + pos->get(1)->data -= yStart; + pos->get(2)->data -= zStart; + + if( e->GetType() == eTYPE_PAINTING || e->GetType() == eTYPE_ITEM_FRAME ) + { + ((IntTag *) eTag->get(L"TileX") )->data -= xStart; + ((IntTag *) eTag->get(L"TileY") )->data -= yStart; + ((IntTag *) eTag->get(L"TileZ") )->data -= zStart; + } + + entitiesTag->add(eTag); + } + } + } + + tag.put(L"Entities", entitiesTag); + + if(dos != NULL) NbtIo::write(&tag,dos); +} + +void ConsoleSchematicFile::getBlocksAndData(LevelChunk *chunk, byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP) +{ + // 4J Stu - Needs updated to work with higher worlds, should still work with non-optimised version below + //int xs = x1 - x0; + //int ys = y1 - y0; + //int zs = z1 - z0; + //if (xs * ys * zs == LevelChunk::BLOCKS_LENGTH) + //{ + // byteArray blockData = byteArray(data->data + blocksP, Level::CHUNK_TILE_COUNT); + // chunk->getBlockData(blockData); + // blocksP += blockData.length; + + // byteArray dataData = byteArray(data->data + dataP, 16384); + // chunk->getBlockLightData(dataData); + // dataP += dataData.length; + + // byteArray blockLightData = byteArray(data->data + blockLightP, 16384); + // chunk->getBlockLightData(blockLightData); + // blockLightP += blockLightData.length; + + // byteArray skyLightData = byteArray(data->data + skyLightP, 16384); + // chunk->getSkyLightData(skyLightData); + // skyLightP += skyLightData.length; + // return; + //} + + bool bHasLower, bHasUpper; + bHasLower = bHasUpper = false; + int lowerY0, lowerY1, upperY0, upperY1; + lowerY0 = upperY0 = y0; + lowerY1 = upperY1 = y1; + + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + lowerY0 = y0; + lowerY1 = min(y1, compressedHeight); + bHasLower = true; + } + if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + upperY0 = max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + bHasUpper = true; + } + + byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); + chunk->getBlockData(blockData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; + int len = lowerY1 - lowerY0; + System::arraycopy(blockData, slot, data, blocksP, len); + blocksP += len; + } + if(bHasUpper) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; + int len = upperY1 - upperY0; + System::arraycopy(blockData, slot, data, blocksP, len); + blocksP += len; + } + } + delete blockData.data; + + byteArray dataData = byteArray(Level::CHUNK_TILE_COUNT); + chunk->getDataData(dataData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(dataData, slot, data, dataP, len); + dataP += len; + } + if(bHasUpper) + { + int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; + int len = (upperY1 - upperY0) / 2; + System::arraycopy(dataData, slot, data, dataP, len); + dataP += len; + } + } + delete dataData.data; + + // 4J Stu - Allow ignoring light data + if(blockLightP > -1) + { + byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + chunk->getBlockLightData(blockLightData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(blockLightData, slot, data, blockLightP, len); + blockLightP += len; + } + if(bHasUpper) + { + int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); + int len = (upperY1 - upperY0) / 2; + System::arraycopy(blockLightData, slot, data, blockLightP, len); + blockLightP += len; + } + } + delete blockLightData.data; + } + + + // 4J Stu - Allow ignoring light data + if(skyLightP > -1) + { + byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + chunk->getSkyLightData(skyLightData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(skyLightData, slot, data, skyLightP, len); + skyLightP += len; + } + if(bHasUpper) + { + int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); + int len = (upperY1 - upperY0) / 2; + System::arraycopy(skyLightData, slot, data, skyLightP, len); + skyLightP += len; + } + } + delete skyLightData.data; + } + + return; +} + +void ConsoleSchematicFile::setBlocksAndData(LevelChunk *chunk, byteArray blockData, byteArray dataData, byteArray inputData, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP) +{ + bool bHasLower, bHasUpper; + bHasLower = bHasUpper = false; + int lowerY0, lowerY1, upperY0, upperY1; + lowerY0 = upperY0 = y0; + lowerY1 = upperY1 = y1; + + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + lowerY0 = y0; + lowerY1 = min(y1, compressedHeight); + bHasLower = true; + } + if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + upperY0 = max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + bHasUpper = true; + } + PIXBeginNamedEvent(0,"Applying block data"); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; + int len = lowerY1 - lowerY0; + System::arraycopy(inputData, blocksP, &blockData, slot, len); + blocksP += len; + } + if(bHasUpper) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; + int len = upperY1 - upperY0; + System::arraycopy(inputData, blocksP, &blockData, slot, len); + blocksP += len; + } + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Applying Data data"); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(inputData, dataP, &dataData, slot, len); + dataP += len; + } + if(bHasUpper) + { + int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; + int len = (upperY1 - upperY0) / 2; + System::arraycopy(inputData, dataP, &dataData, slot, len); + dataP += len; + } + } + PIXEndNamedEvent(); + // 4J Stu - Allow ignoring light data + if(blockLightP > -1) + { + byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + chunk->getBlockLightData(blockLightData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); + blockLightP += len; + } + if(bHasUpper) + { + int slot = ( (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); + int len = (upperY1 - upperY0) / 2; + System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); + blockLightP += len; + } + } + chunk->setBlockLightData(blockLightData); + delete blockLightData.data; + } + + // 4J Stu - Allow ignoring light data + if(skyLightP > -1) + { + byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + chunk->getSkyLightData(skyLightData); + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + if(bHasLower) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; + int len = (lowerY1 - lowerY0) / 2; + System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); + skyLightP += len; + } + if(bHasUpper) + { + int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); + int len = (upperY1 - upperY0) / 2; + System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); + skyLightP += len; + } + } + chunk->setSkyLightData(skyLightData); + delete skyLightData.data; + } +} + +vector > *ConsoleSchematicFile::getTileEntitiesInRegion(LevelChunk *chunk, int x0, int y0, int z0, int x1, int y1, int z1) +{ + vector > *result = new vector >; + for (AUTO_VAR(it, chunk->tileEntities.begin()); it != chunk->tileEntities.end(); ++it) + { + shared_ptr te = it->second; + if (te->x >= x0 && te->y >= y0 && te->z >= z0 && te->x < x1 && te->y < y1 && te->z < z1) + { + result->push_back(te); + } + } + return result; +} diff --git a/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.h b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.h new file mode 100644 index 0000000..f37a605 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/ConsoleSchematicFile.h @@ -0,0 +1,90 @@ +#pragma once +using namespace std; + +#define XBOX_SCHEMATIC_ORIGINAL_VERSION 1 +#define XBOX_SCHEMATIC_CURRENT_VERSION 2 + +#include "..\..\..\Minecraft.World\ArrayWithLength.h" + +class Level; +class DataOutputStream; +class DataInputStream; +class TileEntity; +class LevelChunk; +class AABB; +class Vec3; + +class ConsoleSchematicFile +{ +public: + enum ESchematicRotation + { + eSchematicRot_0, + eSchematicRot_90, + eSchematicRot_180, + eSchematicRot_270 + }; +private: + int m_refCount; + +public: + void incrementRefCount() { ++m_refCount; } + void decrementRefCount() { --m_refCount; } + bool shouldDelete() { return m_refCount <= 0; } + + typedef struct _XboxSchematicInitParam + { + wchar_t name[64]; + int startX; + int startY; + int startZ; + int endX; + int endY; + int endZ; + bool bSaveMobs; + + Compression::ECompressionTypes compressionType; + + _XboxSchematicInitParam() + { + ZeroMemory(name,64*(sizeof(wchar_t))); + startX = startY = startZ = endX = endY = endZ = 0; + bSaveMobs = false; + compressionType = Compression::eCompressionType_None; + } + } XboxSchematicInitParam; +private: + int m_xSize, m_ySize, m_zSize; + vector > m_tileEntities; + vector< pair > m_entities; + +public: + byteArray m_data; + +public: + ConsoleSchematicFile(); + ~ConsoleSchematicFile(); + + int getXSize() { return m_xSize; } + int getYSize() { return m_ySize; } + int getZSize() { return m_zSize; } + + void save(DataOutputStream *dos); + void load(DataInputStream *dis); + + __int64 applyBlocksAndData(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot); + __int64 applyLighting(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot); + void applyTileEntities(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot); + + static void generateSchematicFile(DataOutputStream *dos, Level *level, int xStart, int yStart, int zStart, int xEnd, int yEnd, int zEnd, bool bSaveMobs, Compression::ECompressionTypes); + static void setBlocksAndData(LevelChunk *chunk, byteArray blockData, byteArray dataData, byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP); +private: + void save_tags(DataOutputStream *dos); + void load_tags(DataInputStream *dis); + + static void getBlocksAndData(LevelChunk *chunk, byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP); + static vector > *getTileEntitiesInRegion(LevelChunk *chunk, int x0, int y0, int z0, int x1, int y1, int z1); + + void chunkCoordToSchematicCoord(AABB *destinationBox, int chunkX, int chunkZ, ESchematicRotation rot, int &schematicX, int &schematicZ); + void schematicCoordToChunkCoord(AABB *destinationBox, double schematicX, double schematicZ, ESchematicRotation rot, double &chunkX, double &chunkZ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/GameRule.cpp b/Minecraft.Client/Common/GameRules/GameRule.cpp new file mode 100644 index 0000000..34d6196 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRule.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include "ConsoleGameRules.h" + +GameRule::GameRule(GameRuleDefinition *definition, Connection *connection) +{ + m_definition = definition; + m_connection = connection; +} + +GameRule::~GameRule() +{ + for(AUTO_VAR(it, m_parameters.begin()); it != m_parameters.end(); ++it) + { + if(it->second.isPointer) + { + delete it->second.gr; + } + } +} + +GameRule::ValueType GameRule::getParameter(const wstring ¶meterName) +{ + if(m_parameters.find(parameterName) == m_parameters.end()) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"WARNING: Parameter %ls was not set before being fetched\n", parameterName.c_str()); + __debugbreak(); +#endif + } + return m_parameters[parameterName]; +} + +void GameRule::setParameter(const wstring ¶meterName,ValueType value) +{ + if(m_parameters.find(parameterName) == m_parameters.end()) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Adding parameter %ls to GameRule\n", parameterName.c_str()); +#endif + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Setting parameter %ls for GameRule\n", parameterName.c_str()); +#endif + } + m_parameters[parameterName] = value; +} + +GameRuleDefinition *GameRule::getGameRuleDefinition() +{ + return m_definition; +} + +void GameRule::onUseTile(int tileId, int x, int y, int z) { m_definition->onUseTile(this,tileId,x,y,z); } +void GameRule::onCollectItem(shared_ptr item) { m_definition->onCollectItem(this,item); } + +void GameRule::write(DataOutputStream *dos) +{ + // Find required parameters. + dos->writeInt(m_parameters.size()); + for (AUTO_VAR(it, m_parameters.begin()); it != m_parameters.end(); it++) + { + wstring pName = (*it).first; + ValueType vType = (*it).second; + + dos->writeUTF( (*it).first ); + dos->writeBoolean( vType.isPointer ); + + if (vType.isPointer) + vType.gr->write(dos); + else + dos->writeLong( vType.i64 ); + } +} + +void GameRule::read(DataInputStream *dis) +{ + int savedParams = dis->readInt(); + for (int i = 0; i < savedParams; i++) + { + wstring pNames = dis->readUTF(); + + ValueType vType = getParameter(pNames); + + if (dis->readBoolean()) + { + vType.gr->read(dis); + } + else + { + vType.isPointer = false; + vType.i64 = dis->readLong(); + setParameter(pNames, vType); + } + } +} diff --git a/Minecraft.Client/Common/GameRules/GameRule.h b/Minecraft.Client/Common/GameRules/GameRule.h new file mode 100644 index 0000000..bdc2cef --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRule.h @@ -0,0 +1,62 @@ +#pragma once +using namespace std; + +#include + +class CompoundTag; +class GameRuleDefinition; +class Connection; + +// A game rule maintains the state for one particular definition +class GameRule +{ +public: + typedef struct _ValueType + { + union{ + __int64 i64; + int i; + char c; + bool b; + float f; + double d; + GameRule *gr; + }; + bool isPointer; + + _ValueType() + { + i64 = 0; + isPointer = false; + } + } ValueType; + +private: + GameRuleDefinition *m_definition; + Connection *m_connection; + +public: + typedef unordered_map stringValueMapType; + stringValueMapType m_parameters; // These are the members of this rule that maintain it's state + +public: + GameRule(GameRuleDefinition *definition, Connection *connection = NULL); + virtual ~GameRule(); + + Connection *getConnection() { return m_connection; } + + ValueType getParameter(const wstring ¶meterName); + void setParameter(const wstring ¶meterName,ValueType value); + GameRuleDefinition *getGameRuleDefinition(); + + // All the hooks go here + void onUseTile(int tileId, int x, int y, int z); + void onCollectItem(shared_ptr item); + + // 4J-JEV: For saving. + //CompoundTag *toTags(unordered_map *map); + //static GameRule *fromTags(Connection *c, CompoundTag *cTag, vector *grds); + + void write(DataOutputStream *dos); + void read(DataInputStream *dos); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/GameRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/GameRuleDefinition.cpp new file mode 100644 index 0000000..b63687c --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRuleDefinition.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" +#include "..\..\WstringLookup.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "ConsoleGameRules.h" + +GameRuleDefinition::GameRuleDefinition() +{ + m_descriptionId = L""; + m_promptId = L""; + m_4JDataValue = 0; +} + +void GameRuleDefinition::write(DataOutputStream *dos) +{ + // Write EGameRuleType. + ConsoleGameRules::EGameRuleType eType = getActionType(); + assert( eType != ConsoleGameRules::eGameRuleType_Invalid ); + ConsoleGameRules::write(dos, eType); // stringID + + writeAttributes(dos, 0); + + // 4J-JEV: Get children. + vector *children = new vector(); + getChildren( children ); + + // Write children. + dos->writeInt( children->size() ); + for (AUTO_VAR(it, children->begin()); it != children->end(); it++) + (*it)->write(dos); +} + +void GameRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + dos->writeInt(numAttributes + 3); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_descriptionName); + dos->writeUTF(m_descriptionId); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_promptName); + dos->writeUTF(m_promptId); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_dataTag); + dos->writeUTF(_toString(m_4JDataValue)); +} + +void GameRuleDefinition::getChildren(vector *children) {} + +GameRuleDefinition *GameRuleDefinition::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ +#ifndef _CONTENT_PACKAGE + wprintf(L"GameRuleDefinition: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + return NULL; +} + +void GameRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"descriptionName") == 0) + { + m_descriptionId = attributeValue; +#ifndef _CONTENT_PACKAGE + wprintf(L"GameRuleDefinition: Adding parameter descriptionId=%ls\n",m_descriptionId.c_str()); +#endif + } + else if(attributeName.compare(L"promptName") == 0) + { + m_promptId = attributeValue; +#ifndef _CONTENT_PACKAGE + wprintf(L"GameRuleDefinition: Adding parameter m_promptId=%ls\n",m_promptId.c_str()); +#endif + } + else if(attributeName.compare(L"dataTag") == 0) + { + m_4JDataValue = _fromString(attributeValue); + app.DebugPrintf("GameRuleDefinition: Adding parameter m_4JDataValue=%d\n",m_4JDataValue); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"GameRuleDefinition: Attempted to add invalid attribute: %ls\n", attributeName.c_str()); +#endif + } +} + +void GameRuleDefinition::populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule) +{ + GameRule::ValueType value; + value.b = false; + rule->setParameter(L"bComplete",value); +} + +bool GameRuleDefinition::getComplete(GameRule *rule) +{ + GameRule::ValueType value; + value = rule->getParameter(L"bComplete"); + return value.b; +} + +void GameRuleDefinition::setComplete(GameRule *rule, bool val) +{ + GameRule::ValueType value; + value = rule->getParameter(L"bComplete"); + value.b = val; + rule->setParameter(L"bComplete",value); +} + +vector *GameRuleDefinition::enumerate() +{ + // Get Vector. + vector *gRules; + gRules = new vector(); + gRules->push_back(this); + getChildren(gRules); + return gRules; +} + +unordered_map *GameRuleDefinition::enumerateMap() +{ + unordered_map *out + = new unordered_map(); + + int i = 0; + vector *gRules = enumerate(); + for (AUTO_VAR(it, gRules->begin()); it != gRules->end(); it++) + out->insert( pair( *it, i++ ) ); + + return out; +} + +GameRulesInstance *GameRuleDefinition::generateNewGameRulesInstance(GameRulesInstance::EGameRulesInstanceType type, LevelRuleset *rules, Connection *connection) +{ + GameRulesInstance *manager = new GameRulesInstance(rules, connection); + + rules->populateGameRule(type, manager); + + return manager; +} + +wstring GameRuleDefinition::generateDescriptionString(ConsoleGameRules::EGameRuleType defType, const wstring &description, void *data, int dataLength) +{ + wstring formatted = description; + switch(defType) + { + case ConsoleGameRules::eGameRuleType_CompleteAllRule: + formatted = CompleteAllRuleDefinition::generateDescriptionString(description,data,dataLength); + break; + default: + break; + }; + return formatted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/GameRuleDefinition.h b/Minecraft.Client/Common/GameRules/GameRuleDefinition.h new file mode 100644 index 0000000..afec8fb --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRuleDefinition.h @@ -0,0 +1,66 @@ +#pragma once +using namespace std; +#include +#include + +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "ConsoleGameRulesConstants.h" + +#include "GameRulesInstance.h" + +class GameRule; +class LevelRuleset; +class Player; +class WstringLookup; + +class GameRuleDefinition +{ +private: + // Owner type defines who this rule applies to + GameRulesInstance::EGameRulesInstanceType m_ownerType; + +protected: + // These attributes should map to those in the XSD GameRuleType + wstring m_descriptionId; + wstring m_promptId; + int m_4JDataValue; + +public: + GameRuleDefinition(); + + virtual ConsoleGameRules::EGameRuleType getActionType() = 0; + + void setOwnerType(GameRulesInstance::EGameRulesInstanceType ownerType) { m_ownerType = ownerType;} + + virtual void write(DataOutputStream *); + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + virtual void getChildren(vector *); + + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + virtual void populateGameRule(GameRulesInstance::EGameRulesInstanceType type, GameRule *rule); + + bool getComplete(GameRule *rule); + void setComplete(GameRule *rule, bool val); + + virtual int getGoal() { return 0; } + virtual int getProgress(GameRule *rule) { return 0; } + + virtual int getIcon() { return -1; } + virtual int getAuxValue() { return 0; } + + // Here we should have functions for all the hooks, with a GameRule* as the first parameter + virtual bool onUseTile(GameRule *rule, int tileId, int x, int y, int z) { return false; } + virtual bool onCollectItem(GameRule *rule, shared_ptr item) { return false; } + virtual void postProcessPlayer(shared_ptr player) { } + + vector *enumerate(); + unordered_map *enumerateMap(); + + // Static functions + static GameRulesInstance *generateNewGameRulesInstance(GameRulesInstance::EGameRulesInstanceType type, LevelRuleset *rules, Connection *connection); + static wstring generateDescriptionString(ConsoleGameRules::EGameRuleType defType, const wstring &description, void *data = NULL, int dataLength = 0); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/GameRuleManager.cpp b/Minecraft.Client/Common/GameRules/GameRuleManager.cpp new file mode 100644 index 0000000..0c6a780 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRuleManager.cpp @@ -0,0 +1,767 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\compression.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\File.h" +#include "..\..\..\Minecraft.World\compression.h" +#include "..\DLC\DLCPack.h" +#include "..\DLC\DLCLocalisationFile.h" +#include "..\DLC\DLCGameRulesFile.h" +#include "..\DLC\DLCGameRules.h" +#include "..\DLC\DLCGameRulesHeader.h" +#include "..\..\StringTable.h" +#include "ConsoleGameRules.h" +#include "GameRuleManager.h" + +WCHAR *GameRuleManager::wchTagNameA[] = +{ + L"", // eGameRuleType_Root + L"MapOptions", // eGameRuleType_LevelGenerationOptions + L"ApplySchematic", // eGameRuleType_ApplySchematic + L"GenerateStructure", // eGameRuleType_GenerateStructure + L"GenerateBox", // eGameRuleType_GenerateBox + L"PlaceBlock", // eGameRuleType_PlaceBlock + L"PlaceContainer", // eGameRuleType_PlaceContainer + L"PlaceSpawner", // eGameRuleType_PlaceSpawner + L"BiomeOverride", // eGameRuleType_BiomeOverride + L"StartFeature", // eGameRuleType_StartFeature + L"AddItem", // eGameRuleType_AddItem + L"AddEnchantment", // eGameRuleType_AddEnchantment + L"LevelRules", // eGameRuleType_LevelRules + L"NamedArea", // eGameRuleType_NamedArea + L"UseTile", // eGameRuleType_UseTileRule + L"CollectItem", // eGameRuleType_CollectItemRule + L"CompleteAll", // eGameRuleType_CompleteAllRule + L"UpdatePlayer", // eGameRuleType_UpdatePlayerRule +}; + +WCHAR *GameRuleManager::wchAttrNameA[] = +{ + L"descriptionName", // eGameRuleAttr_descriptionName + L"promptName", // eGameRuleAttr_promptName + L"dataTag", // eGameRuleAttr_dataTag + L"enchantmentId", // eGameRuleAttr_enchantmentId + L"enchantmentLevel", // eGameRuleAttr_enchantmentLevel + L"itemId", // eGameRuleAttr_itemId + L"quantity", // eGameRuleAttr_quantity + L"auxValue", // eGameRuleAttr_auxValue + L"slot", // eGameRuleAttr_slot + L"name", // eGameRuleAttr_name + L"food", // eGameRuleAttr_food + L"health", // eGameRuleAttr_health + L"tileId", // eGameRuleAttr_tileId + L"useCoords", // eGameRuleAttr_useCoords + L"seed", // eGameRuleAttr_seed + L"flatworld", // eGameRuleAttr_flatworld + L"filename", // eGameRuleAttr_filename + L"rot", // eGameRuleAttr_rot + L"data", // eGameRuleAttr_data + L"block", // eGameRuleAttr_block + L"entity", // eGameRuleAttr_entity + L"facing", // eGameRuleAttr_facing + L"edgeTile", // eGameRuleAttr_edgeTile + L"fillTile", // eGameRuleAttr_fillTile + L"skipAir", // eGameRuleAttr_skipAir + L"x", // eGameRuleAttr_x + L"x0", // eGameRuleAttr_x0 + L"x1", // eGameRuleAttr_x1 + L"y", // eGameRuleAttr_y + L"y0", // eGameRuleAttr_y0 + L"y1", // eGameRuleAttr_y1 + L"z", // eGameRuleAttr_z + L"z0", // eGameRuleAttr_z0 + L"z1", // eGameRuleAttr_z1 + L"chunkX", // eGameRuleAttr_chunkX + L"chunkZ", // eGameRuleAttr_chunkZ + L"yRot", // eGameRuleAttr_yRot + L"spawnX", // eGameRuleAttr_spawnX + L"spawnY", // eGameRuleAttr_spawnY + L"spawnZ", // eGameRuleAttr_spawnZ + L"orientation", + L"dimension", + L"topTileId", // eGameRuleAttr_topTileId + L"biomeId", // eGameRuleAttr_biomeId + L"feature", // eGameRuleAttr_feature +}; + +GameRuleManager::GameRuleManager() +{ + m_currentGameRuleDefinitions = NULL; + m_currentLevelGenerationOptions = NULL; +} + +void GameRuleManager::loadGameRules(DLCPack *pack) +{ + StringTable *strings = NULL; + + if(pack->doesPackContainFile(DLCManager::e_DLCType_LocalisationData,L"languages.loc")) + { + DLCLocalisationFile *localisationFile = (DLCLocalisationFile *)pack->getFile(DLCManager::e_DLCType_LocalisationData, L"languages.loc"); + strings = localisationFile->getStringTable(); + } + + int gameRulesCount = pack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader); + for(int i = 0; i < gameRulesCount; ++i) + { + DLCGameRulesHeader *dlcHeader = (DLCGameRulesHeader *)pack->getFile(DLCManager::e_DLCType_GameRulesHeader, i); + DWORD dSize; + byte *dData = dlcHeader->getData(dSize); + + LevelGenerationOptions *createdLevelGenerationOptions = new LevelGenerationOptions(); + // = loadGameRules(dData, dSize); //, strings); + + createdLevelGenerationOptions->setGrSource( dlcHeader ); + + readRuleFile(createdLevelGenerationOptions, dData, dSize, strings); + + createdLevelGenerationOptions->setSrc( LevelGenerationOptions::eSrc_fromDLC ); + + + //createdLevelGenerationOptions->setSrc( LevelGenerationOptions::eSrc_fromDLC ); + dlcHeader->lgo = createdLevelGenerationOptions; + } + + gameRulesCount = pack->getDLCItemsCount(DLCManager::e_DLCType_GameRules); + for (int i = 0; i < gameRulesCount; ++i) + { + DLCGameRulesFile *dlcFile = (DLCGameRulesFile *)pack->getFile(DLCManager::e_DLCType_GameRules, i); + + DWORD dSize; + byte *dData = dlcFile->getData(dSize); + + LevelGenerationOptions *createdLevelGenerationOptions = new LevelGenerationOptions(); + // = loadGameRules(dData, dSize); //, strings); + + createdLevelGenerationOptions->setGrSource( new JustGrSource() ); + readRuleFile(createdLevelGenerationOptions, dData, dSize, strings); + + createdLevelGenerationOptions->setSrc( LevelGenerationOptions::eSrc_tutorial ); + + //createdLevelGenerationOptions->set_DLCGameRulesFile( dlcFile ); + + createdLevelGenerationOptions->setLoadedData(); + } +} + +LevelGenerationOptions *GameRuleManager::loadGameRules(byte *dIn, UINT dSize) +{ + LevelGenerationOptions *lgo = new LevelGenerationOptions(); + lgo->setGrSource( new JustGrSource() ); + lgo->setSrc( LevelGenerationOptions::eSrc_fromSave ); + loadGameRules(lgo, dIn, dSize); + lgo->setLoadedData(); + return lgo; +} + +// 4J-JEV: Reverse of saveGameRules. +void GameRuleManager::loadGameRules(LevelGenerationOptions *lgo, byte *dIn, UINT dSize) +{ + app.DebugPrintf("GameRuleManager::LoadingGameRules:\n"); + + ByteArrayInputStream bais( byteArray(dIn,dSize) ); + DataInputStream dis(&bais); + + // Read file header. + + //dis.readInt(); // File Size + + short version = dis.readShort(); + assert( 0x1 == version ); + app.DebugPrintf("\tversion=%d.\n", version); + + for (int i = 0; i < 8; i++) dis.readByte(); + + BYTE compression_type = dis.readByte(); + + app.DebugPrintf("\tcompressionType=%d.\n", compression_type); + + UINT compr_len, decomp_len; + compr_len = dis.readInt(); + decomp_len = dis.readInt(); + + app.DebugPrintf("\tcompr_len=%d.\n\tdecomp_len=%d.\n", compr_len, decomp_len); + + + // Decompress File Body + + byteArray content(new BYTE[decomp_len], decomp_len), + compr_content(new BYTE[compr_len], compr_len); + dis.read(compr_content); + + Compression::getCompression()->SetDecompressionType( (Compression::ECompressionTypes)compression_type ); + Compression::getCompression()->DecompressLZXRLE( content.data, &content.length, + compr_content.data, compr_content.length); + Compression::getCompression()->SetDecompressionType( SAVE_FILE_PLATFORM_LOCAL ); + + dis.close(); + bais.close(); + + delete [] compr_content.data; + + ByteArrayInputStream bais2( content ); + DataInputStream dis2( &bais2 ); + + // Read StringTable. + byteArray bStringTable; + bStringTable.length = dis2.readInt(); + bStringTable.data = new BYTE[ bStringTable.length ]; + dis2.read(bStringTable); + StringTable *strings = new StringTable(bStringTable.data, bStringTable.length); + + // Read RuleFile. + byteArray bRuleFile; + bRuleFile.length = content.length - bStringTable.length; + bRuleFile.data = new BYTE[ bRuleFile.length ]; + dis2.read(bRuleFile); + + // 4J-JEV: I don't believe that the path-name is ever used. + //DLCGameRulesFile *dlcgr = new DLCGameRulesFile(L"__PLACEHOLDER__"); + //dlcgr->addData(bRuleFile.data,bRuleFile.length); + + if (readRuleFile(lgo, bRuleFile.data, bRuleFile.length, strings)) + { + // Set current gen options and ruleset. + //createdLevelGenerationOptions->setFromSaveGame(true); + lgo->setSrc(LevelGenerationOptions::eSrc_fromSave); + setLevelGenerationOptions( lgo ); + //m_currentGameRuleDefinitions = lgo->getRequiredGameRules(); + } + else + { + delete lgo; + } + + //delete [] content.data; + + // Close and return. + dis2.close(); + bais2.close(); + + return ; +} + +// 4J-JEV: Reverse of loadGameRules. +void GameRuleManager::saveGameRules(byte **dOut, UINT *dSize) +{ + if (m_currentGameRuleDefinitions == NULL && + m_currentLevelGenerationOptions == NULL) + { + app.DebugPrintf("GameRuleManager:: Nothing here to save."); + *dOut = NULL; + *dSize = 0; + return; + } + + app.DebugPrintf("GameRuleManager::saveGameRules:\n"); + + // Initialise output stream. + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + // Write header. + + // VERSION NUMBER + dos.writeShort( 0x1 ); // version_number + + // Write 8 bytes of empty space in case we need them later. + // Mainly useful for the ones we save embedded in game saves. + for (UINT i = 0; i < 8; i++) + dos.writeByte(0x0); + + dos.writeByte(APPROPRIATE_COMPRESSION_TYPE); // m_compressionType + + // -- START COMPRESSED -- // + ByteArrayOutputStream compr_baos; + DataOutputStream compr_dos(&compr_baos); + + if (m_currentGameRuleDefinitions == NULL) + { + compr_dos.writeInt( 0 ); // numStrings for StringTable + compr_dos.writeInt( version_number ); + compr_dos.writeByte(Compression::eCompressionType_None); // compression type + for (int i=0; i<2; i++) compr_dos.writeByte(0x0); // Padding. + compr_dos.writeInt( 0 ); // StringLookup.length + compr_dos.writeInt( 0 ); // SchematicFiles.length + compr_dos.writeInt( 0 ); // XmlObjects.length + } + else + { + StringTable *st = m_currentGameRuleDefinitions->getStringTable(); + + if (st == NULL) + { + app.DebugPrintf("GameRuleManager::saveGameRules: StringTable == NULL!"); + } + else + { + // Write string table. + byteArray stba; + m_currentGameRuleDefinitions->getStringTable()->getData(&stba.data, &stba.length); + compr_dos.writeInt( stba.length ); + compr_dos.write( stba ); + + // Write game rule file to second + // buffer and generate string lookup. + writeRuleFile(&compr_dos); + } + } + + // Compress compr_dos and write to dos. + byteArray compr_ba(new BYTE[ compr_baos.buf.length ], compr_baos.buf.length); + Compression::getCompression()->CompressLZXRLE( compr_ba.data, &compr_ba.length, + compr_baos.buf.data, compr_baos.buf.length ); + + app.DebugPrintf("\tcompr_ba.length=%d.\n\tcompr_baos.buf.length=%d.\n", + compr_ba.length, compr_baos.buf.length ); + + dos.writeInt( compr_ba.length ); // Write length + dos.writeInt( compr_baos.buf.length ); + dos.write(compr_ba); + + delete [] compr_ba.data; + + compr_dos.close(); + compr_baos.close(); + // -- END COMPRESSED -- // + + // return + *dSize = baos.buf.length; + *dOut = baos.buf.data; + + baos.buf.data = NULL; + + dos.close(); baos.close(); +} + +// 4J-JEV: Reverse of readRuleFile. +void GameRuleManager::writeRuleFile(DataOutputStream *dos) +{ + // Write Header + dos->writeShort(version_number); // Version number. + dos->writeByte(Compression::eCompressionType_None); // compression type + for (int i=0; i<8; i++) dos->writeBoolean(false); // Padding. + + // Write string lookup. + int numStrings = ConsoleGameRules::eGameRuleType_Count + ConsoleGameRules::eGameRuleAttr_Count; + dos->writeInt(numStrings); + for (int i = 0; i < ConsoleGameRules::eGameRuleType_Count; i++) dos->writeUTF( wchTagNameA[i] ); + for (int i = 0; i < ConsoleGameRules::eGameRuleAttr_Count; i++) dos->writeUTF( wchAttrNameA[i] ); + + // Write schematic files. + unordered_map *files; + files = getLevelGenerationOptions()->getUnfinishedSchematicFiles(); + dos->writeInt( files->size() ); + for (AUTO_VAR(it, files->begin()); it != files->end(); it++) + { + wstring filename = it->first; + ConsoleSchematicFile *file = it->second; + + ByteArrayOutputStream fileBaos; + DataOutputStream fileDos(&fileBaos); + file->save(&fileDos); + + dos->writeUTF(filename); + //dos->writeInt(file->m_data.length); + dos->writeInt(fileBaos.buf.length); + dos->write((byteArray)fileBaos.buf); + + fileDos.close(); fileBaos.close(); + } + + // Write xml objects. + dos->writeInt( 2 ); // numChildren + m_currentLevelGenerationOptions->write(dos); + m_currentGameRuleDefinitions->write(dos); +} + +bool GameRuleManager::readRuleFile(LevelGenerationOptions *lgo, byte *dIn, UINT dSize, StringTable *strings) //(DLCGameRulesFile *dlcFile, StringTable *strings) +{ + bool levelGenAdded = false; + bool gameRulesAdded = false; + LevelGenerationOptions *levelGenerator = lgo;//new LevelGenerationOptions(); + LevelRuleset *gameRules = new LevelRuleset(); + + //DWORD dwLen = 0; + //PBYTE pbData = dlcFile->getData(dwLen); + //byteArray data(pbData,dwLen); + + byteArray data(dIn, dSize); + ByteArrayInputStream bais(data); + DataInputStream dis(&bais); + + // Read File. + + // version_number + __int64 version = dis.readShort(); + unsigned char compressionType = 0; + if(version == 0) + { + for (int i = 0; i < 14; i++) dis.readByte(); // Read padding. + } + else + { + compressionType = dis.readByte(); + + // Read the spare bytes we inserted for future use + for(int i = 0; i < 8; ++i) dis.readBoolean(); + } + + ByteArrayInputStream *contentBais = NULL; + DataInputStream *contentDis = NULL; + + if(compressionType == Compression::eCompressionType_None) + { + // No compression + // No need to read buffer size, as we can read the stream as it is; + app.DebugPrintf("De-compressing game rules with: None\n"); + contentDis = &dis; + } + else + { + unsigned int uncompressedSize = dis.readInt(); + unsigned int compressedSize = dis.readInt(); + byteArray compressedBuffer(compressedSize); + dis.read(compressedBuffer); + + byteArray decompressedBuffer = byteArray(uncompressedSize); + + switch(compressionType) + { + case Compression::eCompressionType_None: + memcpy(decompressedBuffer.data, compressedBuffer.data, uncompressedSize); + break; + + case Compression::eCompressionType_RLE: + app.DebugPrintf("De-compressing game rules with: RLE\n"); + Compression::getCompression()->Decompress( decompressedBuffer.data, &decompressedBuffer.length, compressedBuffer.data, compressedSize); + break; + + default: + app.DebugPrintf("De-compressing game rules."); +#ifndef _CONTENT_PACKAGE + assert( compressionType == APPROPRIATE_COMPRESSION_TYPE ); +#endif + // 4J-JEV: DecompressLZXRLE uses the correct platform specific compression type. (need to assert that the data is compressed with it though). + Compression::getCompression()->DecompressLZXRLE(decompressedBuffer.data, &decompressedBuffer.length, compressedBuffer.data, compressedSize); + break; +/* 4J-JEV: + Each platform has only 1 method of compression, 'compression.h' file deals with it. + + case Compression::eCompressionType_LZXRLE: + app.DebugPrintf("De-compressing game rules with: LZX+RLE\n"); + Compression::getCompression()->DecompressLZXRLE( decompressedBuffer.data, &uncompressedSize, compressedBuffer.data, compressedSize); + break; + default: + app.DebugPrintf("Invalid compression type %d found\n", compressionType); + __debugbreak(); + + delete [] compressedBuffer.data; delete [] decompressedBuffer.data; + dis.close(); bais.reset(); + + if(!gameRulesAdded) delete gameRules; + return false; + */ + }; + + delete [] compressedBuffer.data; + + contentBais = new ByteArrayInputStream(decompressedBuffer); + contentDis = new DataInputStream(contentBais); + } + + // string lookup. + UINT numStrings = contentDis->readInt(); + vector tagsAndAtts; + for (UINT i = 0; i < numStrings; i++) + tagsAndAtts.push_back( contentDis->readUTF() ); + + unordered_map tagIdMap; + for(int type = (int)ConsoleGameRules::eGameRuleType_Root; type < (int)ConsoleGameRules::eGameRuleType_Count; ++type) + { + for(UINT i = 0; i < numStrings; ++i) + { + if(tagsAndAtts[i].compare(wchTagNameA[type]) == 0) + { + tagIdMap.insert( unordered_map::value_type(i, (ConsoleGameRules::EGameRuleType)type) ); + break; + } + } + } + + // 4J-JEV: TODO: As yet unused. + /* + unordered_map attrIdMap; + for(int attr = (int)ConsoleGameRules::eGameRuleAttr_descriptionName; attr < (int)ConsoleGameRules::eGameRuleAttr_Count; ++attr) + { + for (UINT i = 0; i < numStrings; i++) + { + if (tagsAndAtts[i].compare(wchAttrNameA[attr]) == 0) + { + tagIdMap.insert( unordered_map::value_type(i , (ConsoleGameRules::EGameRuleAttr)attr) ); + break; + } + } + }*/ + + // subfile + UINT numFiles = contentDis->readInt(); + for (UINT i = 0; i < numFiles; i++) + { + wstring sFilename = contentDis->readUTF(); + int length = contentDis->readInt(); + byteArray ba( length ); + + contentDis->read(ba); + + levelGenerator->loadSchematicFile(sFilename, ba.data, ba.length); + + } + + LEVEL_GEN_ID lgoID = LEVEL_GEN_ID_NULL; + + // xml objects + UINT numObjects = contentDis->readInt(); + for(UINT i = 0; i < numObjects; ++i) + { + int tagId = contentDis->readInt(); + ConsoleGameRules::EGameRuleType tagVal = ConsoleGameRules::eGameRuleType_Invalid; + AUTO_VAR(it,tagIdMap.find(tagId)); + if(it != tagIdMap.end()) tagVal = it->second; + + GameRuleDefinition *rule = NULL; + + if(tagVal == ConsoleGameRules::eGameRuleType_LevelGenerationOptions) + { + rule = levelGenerator; + levelGenAdded = true; + //m_levelGenerators.addLevelGenerator(L"",levelGenerator); + lgoID = addLevelGenerationOptions(levelGenerator); + levelGenerator->loadStringTable(strings); + } + else if(tagVal == ConsoleGameRules::eGameRuleType_LevelRules) + { + rule = gameRules; + gameRulesAdded = true; + m_levelRules.addLevelRule(L"",gameRules); + levelGenerator->setRequiredGameRules(gameRules); + gameRules->loadStringTable(strings); + } + + readAttributes(contentDis, &tagsAndAtts, rule); + readChildren(contentDis, &tagsAndAtts, &tagIdMap, rule); + } + + if(compressionType != 0) + { + // Not default + contentDis->close(); + if(contentBais != NULL) delete contentBais; + delete contentDis; + } + + dis.close(); + bais.reset(); + + //if(!levelGenAdded) { delete levelGenerator; levelGenerator = NULL; } + if(!gameRulesAdded) delete gameRules; + + return true; + //return levelGenerator; +} + +LevelGenerationOptions *GameRuleManager::readHeader(DLCGameRulesHeader *grh) +{ + LevelGenerationOptions *out = + new LevelGenerationOptions(); + + + out->setSrc(LevelGenerationOptions::eSrc_fromDLC); + out->setGrSource(grh); + addLevelGenerationOptions(out); + + return out; +} + +void GameRuleManager::readAttributes(DataInputStream *dis, vector *tagsAndAtts, GameRuleDefinition *rule) +{ + int numAttrs = dis->readInt(); + for (UINT att = 0; att < numAttrs; ++att) + { + int attID = dis->readInt(); + wstring value = dis->readUTF(); + + if(rule != NULL) rule->addAttribute(tagsAndAtts->at(attID),value); + } +} + +void GameRuleManager::readChildren(DataInputStream *dis, vector *tagsAndAtts, unordered_map *tagIdMap, GameRuleDefinition *rule) +{ + int numChildren = dis->readInt(); + for(UINT child = 0; child < numChildren; ++child) + { + int tagId = dis->readInt(); + ConsoleGameRules::EGameRuleType tagVal = ConsoleGameRules::eGameRuleType_Invalid; + AUTO_VAR(it,tagIdMap->find(tagId)); + if(it != tagIdMap->end()) tagVal = it->second; + + GameRuleDefinition *childRule = NULL; + if(rule != NULL) childRule = rule->addChild(tagVal); + + readAttributes(dis,tagsAndAtts,childRule); + readChildren(dis,tagsAndAtts,tagIdMap,childRule); + } +} + +void GameRuleManager::processSchematics(LevelChunk *levelChunk) +{ + if(getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = getLevelGenerationOptions(); + levelGenOptions->processSchematics(levelChunk); + } +} + +void GameRuleManager::processSchematicsLighting(LevelChunk *levelChunk) +{ + if(getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = getLevelGenerationOptions(); + levelGenOptions->processSchematicsLighting(levelChunk); + } +} + +void GameRuleManager::loadDefaultGameRules() +{ +#ifdef _XBOX +#ifdef _TU_BUILD + wstring fileRoot = L"UPDATE:\\res\\GameRules\\Tutorial.pck"; +#else + wstring fileRoot = L"GAME:\\res\\TitleUpdate\\GameRules\\Tutorial.pck"; +#endif + File packedTutorialFile(fileRoot); + if(loadGameRulesPack(&packedTutorialFile)) + { + m_levelGenerators.getLevelGenerators()->at(0)->setWorldName(app.GetString(IDS_PLAY_TUTORIAL)); + //m_levelGenerators.getLevelGenerators()->at(0)->setDefaultSaveName(L"Tutorial"); + m_levelGenerators.getLevelGenerators()->at(0)->setDefaultSaveName(app.GetString(IDS_TUTORIALSAVENAME)); + } + +#ifndef _CONTENT_PACKAGE + // 4J Stu - Remove these just now + //File testRulesPath(L"GAME:\\GameRules"); + //vector *packFiles = testRulesPath.listFiles(); + + //for(AUTO_VAR(it,packFiles->begin()); it != packFiles->end(); ++it) + //{ + // loadGameRulesPack(*it); + //} + //delete packFiles; +#endif + +#else // _XBOX + + wstring fpTutorial = L"Tutorial.pck"; + if(app.getArchiveFileSize(fpTutorial) >= 0) + { + DLCPack *pack = new DLCPack(L"",0xffffffff); + DWORD dwFilesProcessed = 0; + if ( app.m_dlcManager.readDLCDataFile(dwFilesProcessed,fpTutorial,pack,true) ) + { + app.m_dlcManager.addPack(pack); + m_levelGenerators.getLevelGenerators()->at(0)->setWorldName(app.GetString(IDS_PLAY_TUTORIAL)); + m_levelGenerators.getLevelGenerators()->at(0)->setDefaultSaveName(app.GetString(IDS_TUTORIALSAVENAME)); + } + else delete pack; + } + /*StringTable *strings = new StringTable(baStrings.data, baStrings.length); + LevelGenerationOptions *lgo = new LevelGenerationOptions(); + lgo->setGrSource( new JustGrSource() ); + lgo->setSrc( LevelGenerationOptions::eSrc_tutorial ); + readRuleFile(lgo, tutorial.data, tutorial.length, strings); + lgo->setLoadedData();*/ + +#endif +} + +bool GameRuleManager::loadGameRulesPack(File *path) +{ + bool success = false; +#ifdef _XBOX + if(path->exists()) + { + DLCPack *pack = new DLCPack(L"",0xffffffff); + DWORD dwFilesProcessed = 0; + if( app.m_dlcManager.readDLCDataFile(dwFilesProcessed, path->getPath(),pack)) + { + app.m_dlcManager.addPack(pack); + success = true; + } + else + { + delete pack; + } + } +#endif + return success; +} + +void GameRuleManager::setLevelGenerationOptions(LevelGenerationOptions *levelGen) +{ + m_currentGameRuleDefinitions = NULL; + m_currentLevelGenerationOptions = levelGen; + + if(m_currentLevelGenerationOptions != NULL && m_currentLevelGenerationOptions->requiresGameRules() ) + { + m_currentGameRuleDefinitions = m_currentLevelGenerationOptions->getRequiredGameRules(); + } + + if(m_currentLevelGenerationOptions != NULL) + m_currentLevelGenerationOptions->reset_start(); +} + +LPCWSTR GameRuleManager::GetGameRulesString(const wstring &key) +{ + if(m_currentGameRuleDefinitions != NULL && !key.empty() ) + { + return m_currentGameRuleDefinitions->getString(key); + } + else + { + return L""; + } +} + +LEVEL_GEN_ID GameRuleManager::addLevelGenerationOptions(LevelGenerationOptions *lgo) +{ + vector *lgs = m_levelGenerators.getLevelGenerators(); + + for (int i = 0; isize(); i++) + if (lgs->at(i) == lgo) + return i; + + lgs->push_back(lgo); + return lgs->size() - 1; +} + +void GameRuleManager::unloadCurrentGameRules() +{ + if (m_currentLevelGenerationOptions != NULL) + { + if (m_currentGameRuleDefinitions != NULL + && m_currentLevelGenerationOptions->isFromSave()) + m_levelRules.removeLevelRule( m_currentGameRuleDefinitions ); + + if (m_currentLevelGenerationOptions->isFromSave()) + { + m_levelGenerators.removeLevelGenerator( m_currentLevelGenerationOptions ); + + delete m_currentLevelGenerationOptions; + } + else if (m_currentLevelGenerationOptions->isFromDLC()) + { + m_currentLevelGenerationOptions->reset_finish(); + } + } + + m_currentGameRuleDefinitions = NULL; + m_currentLevelGenerationOptions = NULL; +} diff --git a/Minecraft.Client/Common/GameRules/GameRuleManager.h b/Minecraft.Client/Common/GameRules/GameRuleManager.h new file mode 100644 index 0000000..e9e983b --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRuleManager.h @@ -0,0 +1,80 @@ +#pragma once +using namespace std; + +#include "LevelGenerators.h" +#include "LevelRules.h" +class LevelGenerationOptions; +class RootGameRulesDefinition; +class LevelChunk; +class DLCPack; +class DLCGameRulesFile; +class DLCGameRulesHeader; +class StringTable; +class GameRuleDefinition; +class DataInputStream; +class DataOutputStream; +class WstringLookup; + +#define GAME_RULE_SAVENAME L"requiredGameRules.grf" + +// 4J-JEV: +#define LEVEL_GEN_ID int +#define LEVEL_GEN_ID_NULL 0 + +class GameRuleManager +{ +public: + static WCHAR *wchTagNameA[ConsoleGameRules::eGameRuleType_Count]; + static WCHAR *wchAttrNameA[ConsoleGameRules::eGameRuleAttr_Count]; + + static const short version_number = 2; + +private: + LevelGenerationOptions *m_currentLevelGenerationOptions; + LevelRuleset *m_currentGameRuleDefinitions; + LevelGenerators m_levelGenerators; + LevelRules m_levelRules; + +public: + GameRuleManager(); + + void loadGameRules(DLCPack *); + + LevelGenerationOptions *loadGameRules(byte *dIn, UINT dSize); + void loadGameRules(LevelGenerationOptions *lgo, byte *dIn, UINT dSize); + + void saveGameRules(byte **dOut, UINT *dSize); + +private: + LevelGenerationOptions *readHeader(DLCGameRulesHeader *grh); + + void writeRuleFile(DataOutputStream *dos); + +public: + bool readRuleFile(LevelGenerationOptions *lgo, byte *dIn, UINT dSize, StringTable *strings); //(DLCGameRulesFile *dlcFile, StringTable *strings); + +private: + void readAttributes(DataInputStream *dis, vector *tagsAndAtts, GameRuleDefinition *rule); + void readChildren(DataInputStream *dis, vector *tagsAndAtts, unordered_map *tagIdMap, GameRuleDefinition *rule); + +public: + void processSchematics(LevelChunk *levelChunk); + void processSchematicsLighting(LevelChunk *levelChunk); + void loadDefaultGameRules(); + +private: + bool loadGameRulesPack(File *path); + + LEVEL_GEN_ID addLevelGenerationOptions(LevelGenerationOptions *); + +public: + vector *getLevelGenerators() { return m_levelGenerators.getLevelGenerators(); } + void setLevelGenerationOptions(LevelGenerationOptions *levelGen); + LevelRuleset *getGameRuleDefinitions() { return m_currentGameRuleDefinitions; } + LevelGenerationOptions *getLevelGenerationOptions() { return m_currentLevelGenerationOptions; } + LPCWSTR GetGameRulesString(const wstring &key); + + // 4J-JEV: + // Properly cleans-up and unloads the current set of gameRules. + void unloadCurrentGameRules(); +}; diff --git a/Minecraft.Client/Common/GameRules/GameRulesInstance.h b/Minecraft.Client/Common/GameRules/GameRulesInstance.h new file mode 100644 index 0000000..064e086 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/GameRulesInstance.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; +#include +#include "GameRule.h" + +class GameRuleDefinition; + +// The game rule manager belongs to a player/server or other object, and maintains their current state for each of +// the rules that apply to them +class GameRulesInstance : public GameRule +{ +public: + // These types are used by the GameRuleDefinition to know which rules to add to this GameRulesInstance + enum EGameRulesInstanceType + { + eGameRulesInstanceType_ServerPlayer, + eGameRulesInstanceType_Server, + eGameRulesInstanceType_Count + }; + +public: + GameRulesInstance(GameRuleDefinition *definition, Connection *connection) : GameRule(definition,connection) {} + // Functions for all the hooks should go here +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelGenerationOptions.cpp b/Minecraft.Client/Common/GameRules/LevelGenerationOptions.cpp new file mode 100644 index 0000000..717b066 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelGenerationOptions.cpp @@ -0,0 +1,514 @@ +#include "stdafx.h" + +#include + +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Pos.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\..\StringTable.h" +#include "LevelGenerationOptions.h" +#include "ConsoleGameRules.h" + +JustGrSource::JustGrSource() +{ + m_displayName = L"Default_DisplayName"; + m_worldName= L"Default_WorldName"; + m_defaultSaveName = L"Default_DefaultSaveName"; + m_bRequiresTexturePack = false; + m_requiredTexturePackId = 0; + m_grfPath = L"__NO_GRF_PATH__"; + m_bRequiresBaseSave = false; +} + +bool JustGrSource::requiresTexturePack() {return m_bRequiresTexturePack;} +UINT JustGrSource::getRequiredTexturePackId() {return m_requiredTexturePackId;} +wstring JustGrSource::getDefaultSaveName() {return m_defaultSaveName;} +LPCWSTR JustGrSource::getWorldName() {return m_worldName.c_str();} +LPCWSTR JustGrSource::getDisplayName() {return m_displayName.c_str();} +wstring JustGrSource::getGrfPath() {return m_grfPath;} +bool JustGrSource::requiresBaseSave() { return m_bRequiresBaseSave; }; +wstring JustGrSource::getBaseSavePath() { return m_baseSavePath; }; + +void JustGrSource::setRequiresTexturePack(bool x) {m_bRequiresTexturePack = x;} +void JustGrSource::setRequiredTexturePackId(UINT x) {m_requiredTexturePackId = x;} +void JustGrSource::setDefaultSaveName(const wstring &x) {m_defaultSaveName = x;} +void JustGrSource::setWorldName(const wstring &x) {m_worldName = x;} +void JustGrSource::setDisplayName(const wstring &x) {m_displayName = x;} +void JustGrSource::setGrfPath(const wstring &x) {m_grfPath = x;} +void JustGrSource::setBaseSavePath(const wstring &x) { m_baseSavePath = x; m_bRequiresBaseSave = true; } + +bool JustGrSource::ready() { return true; } + +LevelGenerationOptions::LevelGenerationOptions() +{ + m_spawnPos = NULL; + m_stringTable = NULL; + + m_hasLoadedData = false; + + m_seed = 0; + m_useFlatWorld = false; + m_bHaveMinY = false; + m_minY = INT_MAX; + m_bRequiresGameRules = false; + + m_pbBaseSaveData = NULL; + m_dwBaseSaveSize = 0; +} + +LevelGenerationOptions::~LevelGenerationOptions() +{ + clearSchematics(); + if(m_spawnPos != NULL) delete m_spawnPos; + for(AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end(); ++it) + { + delete *it; + } + for(AUTO_VAR(it, m_structureRules.begin()); it != m_structureRules.end(); ++it) + { + delete *it; + } + + for(AUTO_VAR(it, m_biomeOverrides.begin()); it != m_biomeOverrides.end(); ++it) + { + delete *it; + } + + for(AUTO_VAR(it, m_features.begin()); it != m_features.end(); ++it) + { + delete *it; + } + + if (m_stringTable) + if (!isTutorial()) + delete m_stringTable; + + if (isFromSave()) delete m_pSrc; +} + +ConsoleGameRules::EGameRuleType LevelGenerationOptions::getActionType() { return ConsoleGameRules::eGameRuleType_LevelGenerationOptions; } + +void LevelGenerationOptions::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnX); + dos->writeUTF(_toString(m_spawnPos->x)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnY); + dos->writeUTF(_toString(m_spawnPos->y)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnZ); + dos->writeUTF(_toString(m_spawnPos->z)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_seed); + dos->writeUTF(_toString(m_seed)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_flatworld); + dos->writeUTF(_toString(m_useFlatWorld)); +} + +void LevelGenerationOptions::getChildren(vector *children) +{ + GameRuleDefinition::getChildren(children); + + vector used_schematics; + for (AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end(); it++) + if ( !(*it)->isComplete() ) + used_schematics.push_back( *it ); + + for(AUTO_VAR(it, m_structureRules.begin()); it!=m_structureRules.end(); it++) + children->push_back( *it ); + for(AUTO_VAR(it, used_schematics.begin()); it!=used_schematics.end(); it++) + children->push_back( *it ); + for(AUTO_VAR(it, m_biomeOverrides.begin()); it != m_biomeOverrides.end(); ++it) + children->push_back( *it ); + for(AUTO_VAR(it, m_features.begin()); it != m_features.end(); ++it) + children->push_back( *it ); +} + +GameRuleDefinition *LevelGenerationOptions::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_ApplySchematic) + { + rule = new ApplySchematicRuleDefinition(this); + m_schematicRules.push_back((ApplySchematicRuleDefinition *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_GenerateStructure) + { + rule = new ConsoleGenerateStructure(); + m_structureRules.push_back((ConsoleGenerateStructure *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_BiomeOverride) + { + rule = new BiomeOverride(); + m_biomeOverrides.push_back((BiomeOverride *)rule); + } + else if(ruleType == ConsoleGameRules::eGameRuleType_StartFeature) + { + rule = new StartFeature(); + m_features.push_back((StartFeature *)rule); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"LevelGenerationOptions: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + return rule; +} + +void LevelGenerationOptions::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"seed") == 0) + { + m_seed = _fromString<__int64>(attributeValue); + app.DebugPrintf("LevelGenerationOptions: Adding parameter m_seed=%I64d\n",m_seed); + } + else if(attributeName.compare(L"spawnX") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->x = value; + app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnX=%d\n",value); + } + else if(attributeName.compare(L"spawnY") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->y = value; + app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnY=%d\n",value); + } + else if(attributeName.compare(L"spawnZ") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->z = value; + app.DebugPrintf("LevelGenerationOptions: Adding parameter spawnZ=%d\n",value); + } + else if(attributeName.compare(L"flatworld") == 0) + { + if(attributeValue.compare(L"true") == 0) m_useFlatWorld = true; + app.DebugPrintf("LevelGenerationOptions: Adding parameter flatworld=%s\n",m_useFlatWorld?"TRUE":"FALSE"); + } + else if(attributeName.compare(L"saveName") == 0) + { + wstring string(getString(attributeValue)); + if(!string.empty()) setDefaultSaveName( string ); + else setDefaultSaveName( attributeValue ); + app.DebugPrintf("LevelGenerationOptions: Adding parameter saveName=%ls\n", getDefaultSaveName().c_str()); + } + else if(attributeName.compare(L"worldName") == 0) + { + wstring string(getString(attributeValue)); + if(!string.empty()) setWorldName( string ); + else setWorldName( attributeValue ); + app.DebugPrintf("LevelGenerationOptions: Adding parameter worldName=%ls\n", getWorldName()); + } + else if(attributeName.compare(L"displayName") == 0) + { + wstring string(getString(attributeValue)); + if(!string.empty()) setDisplayName( string ); + else setDisplayName( attributeValue ); + app.DebugPrintf("LevelGenerationOptions: Adding parameter displayName=%ls\n", getDisplayName()); + } + else if(attributeName.compare(L"texturePackId") == 0) + { + setRequiredTexturePackId( _fromString(attributeValue) ); + setRequiresTexturePack( true ); + app.DebugPrintf("LevelGenerationOptions: Adding parameter texturePackId=%0x\n", getRequiredTexturePackId()); + } + else if(attributeName.compare(L"isTutorial") == 0) + { + if(attributeValue.compare(L"true") == 0) setSrc(eSrc_tutorial); + app.DebugPrintf("LevelGenerationOptions: Adding parameter isTutorial=%s\n",isTutorial()?"TRUE":"FALSE"); + } + else if(attributeName.compare(L"baseSaveName") == 0) + { + setBaseSavePath( attributeValue ); + app.DebugPrintf("LevelGenerationOptions: Adding parameter baseSaveName=%ls\n", getBaseSavePath().c_str()); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +void LevelGenerationOptions::processSchematics(LevelChunk *chunk) +{ + PIXBeginNamedEvent(0,"Processing schematics for chunk (%d,%d)", chunk->x, chunk->z); + AABB *chunkBox = AABB::newTemp(chunk->x*16,0,chunk->z*16,chunk->x*16 + 16,Level::maxBuildHeight,chunk->z*16 + 16); + for( AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end();++it) + { + ApplySchematicRuleDefinition *rule = *it; + rule->processSchematic(chunkBox, chunk); + } + + int cx = (chunk->x << 4); + int cz = (chunk->z << 4); + + for( AUTO_VAR(it, m_structureRules.begin()); it != m_structureRules.end(); it++ ) + { + ConsoleGenerateStructure *structureStart = *it; + + if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15, cz + 15)) + { + BoundingBox *bb = new BoundingBox(cx, cz, cx + 15, cz + 15); + structureStart->postProcess(chunk->level, NULL, bb); + delete bb; + } + } + PIXEndNamedEvent(); +} + +void LevelGenerationOptions::processSchematicsLighting(LevelChunk *chunk) +{ + PIXBeginNamedEvent(0,"Processing schematics (lighting) for chunk (%d,%d)", chunk->x, chunk->z); + AABB *chunkBox = AABB::newTemp(chunk->x*16,0,chunk->z*16,chunk->x*16 + 16,Level::maxBuildHeight,chunk->z*16 + 16); + for( AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end();++it) + { + ApplySchematicRuleDefinition *rule = *it; + rule->processSchematicLighting(chunkBox, chunk); + } + PIXEndNamedEvent(); +} + +bool LevelGenerationOptions::checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1) +{ + PIXBeginNamedEvent(0,"Check Intersects"); + + // As an optimisation, we can quickly discard things below a certain y which makes most ore checks faster due to + // a) ores generally being below ground/sea level and b) tutorial world additions generally being above ground/sea level + if(!m_bHaveMinY) + { + for(AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end();++it) + { + ApplySchematicRuleDefinition *rule = *it; + int minY = rule->getMinY(); + if(minY < m_minY) m_minY = minY; + } + + for( AUTO_VAR(it, m_structureRules.begin()); it != m_structureRules.end(); it++ ) + { + ConsoleGenerateStructure *structureStart = *it; + int minY = structureStart->getMinY(); + if(minY < m_minY) m_minY = minY; + } + + m_bHaveMinY = true; + } + + // 4J Stu - We DO NOT intersect if our upper bound is below the lower bound for all schematics + if( y1 < m_minY ) return false; + + bool intersects = false; + for(AUTO_VAR(it, m_schematicRules.begin()); it != m_schematicRules.end();++it) + { + ApplySchematicRuleDefinition *rule = *it; + intersects = rule->checkIntersects(x0,y0,z0,x1,y1,z1); + if(intersects) break; + } + + if(!intersects) + { + for( AUTO_VAR(it, m_structureRules.begin()); it != m_structureRules.end(); it++ ) + { + ConsoleGenerateStructure *structureStart = *it; + intersects = structureStart->checkIntersects(x0,y0,z0,x1,y1,z1); + if(intersects) break; + } + } + PIXEndNamedEvent(); + return intersects; +} + +void LevelGenerationOptions::clearSchematics() +{ + for(AUTO_VAR(it, m_schematics.begin()); it != m_schematics.end(); ++it) + { + delete it->second; + } + m_schematics.clear(); +} + +ConsoleSchematicFile *LevelGenerationOptions::loadSchematicFile(const wstring &filename, PBYTE pbData, DWORD dwLen) +{ + // If we have already loaded this, just return + AUTO_VAR(it, m_schematics.find(filename)); + if(it != m_schematics.end()) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"We have already loaded schematic file %ls\n", filename.c_str() ); +#endif + it->second->incrementRefCount(); + return it->second; + } + + ConsoleSchematicFile *schematic = NULL; + byteArray data(pbData,dwLen); + ByteArrayInputStream bais(data); + DataInputStream dis(&bais); + schematic = new ConsoleSchematicFile(); + schematic->load(&dis); + m_schematics[filename] = schematic; + bais.reset(); + return schematic; +} + +ConsoleSchematicFile *LevelGenerationOptions::getSchematicFile(const wstring &filename) +{ + ConsoleSchematicFile *schematic = NULL; + // If we have already loaded this, just return + AUTO_VAR(it, m_schematics.find(filename)); + if(it != m_schematics.end()) + { + schematic = it->second; + } + return schematic; +} + +void LevelGenerationOptions::releaseSchematicFile(const wstring &filename) +{ + // 4J Stu - We don't want to delete them when done, but probably want to keep a set of active schematics for the current world + //AUTO_VAR(it, m_schematics.find(filename)); + //if(it != m_schematics.end()) + //{ + // ConsoleSchematicFile *schematic = it->second; + // schematic->decrementRefCount(); + // if(schematic->shouldDelete()) + // { + // delete schematic; + // m_schematics.erase(it); + // } + //} +} + +void LevelGenerationOptions::loadStringTable(StringTable *table) +{ + m_stringTable = table; +} + +LPCWSTR LevelGenerationOptions::getString(const wstring &key) +{ + if(m_stringTable == NULL) + { + return L""; + } + else + { + return m_stringTable->getString(key); + } +} + +void LevelGenerationOptions::getBiomeOverride(int biomeId, BYTE &tile, BYTE &topTile) +{ + for(AUTO_VAR(it, m_biomeOverrides.begin()); it != m_biomeOverrides.end(); ++it) + { + BiomeOverride *bo = *it; + if(bo->isBiome(biomeId)) + { + bo->getTileValues(tile,topTile); + break; + } + } +} + +bool LevelGenerationOptions::isFeatureChunk(int chunkX, int chunkZ, StructureFeature::EFeatureTypes feature) +{ + bool isFeature = false; + + for(AUTO_VAR(it, m_features.begin()); it != m_features.end(); ++it) + { + StartFeature *sf = *it; + if(sf->isFeatureChunk(chunkX, chunkZ, feature)) + { + isFeature = true; + break; + } + } + return isFeature; +} + +unordered_map *LevelGenerationOptions::getUnfinishedSchematicFiles() +{ + // Clean schematic rules. + unordered_set usedFiles = unordered_set(); + for (AUTO_VAR(it, m_schematicRules.begin()); it!=m_schematicRules.end(); it++) + if ( !(*it)->isComplete() ) + usedFiles.insert( (*it)->getSchematicName() ); + + // Clean schematic files. + unordered_map *out + = new unordered_map(); + for (AUTO_VAR(it, usedFiles.begin()); it!=usedFiles.end(); it++) + out->insert( pair(*it, getSchematicFile(*it)) ); + + return out; +} + +void LevelGenerationOptions::reset_start() +{ + for ( AUTO_VAR( it, m_schematicRules.begin()); + it != m_schematicRules.end(); + it++ ) + { + (*it)->reset(); + } +} + +void LevelGenerationOptions::reset_finish() +{ + //if (m_spawnPos) { delete m_spawnPos; m_spawnPos = NULL; } + //if (m_stringTable) { delete m_stringTable; m_stringTable = NULL; } + + if (isFromDLC()) + { + m_hasLoadedData = false; + } +} + + +GrSource *LevelGenerationOptions::info() { return m_pSrc; } +void LevelGenerationOptions::setSrc(eSrc src) { m_src = src; } +LevelGenerationOptions::eSrc LevelGenerationOptions::getSrc() { return m_src; } + +bool LevelGenerationOptions::isTutorial() { return getSrc() == eSrc_tutorial; } +bool LevelGenerationOptions::isFromSave() { return getSrc() == eSrc_fromSave; } +bool LevelGenerationOptions::isFromDLC() { return getSrc() == eSrc_fromDLC; } + +bool LevelGenerationOptions::requiresTexturePack() { return info()->requiresTexturePack(); } +UINT LevelGenerationOptions::getRequiredTexturePackId() { return info()->getRequiredTexturePackId(); } +wstring LevelGenerationOptions::getDefaultSaveName() { return info()->getDefaultSaveName(); } +LPCWSTR LevelGenerationOptions::getWorldName() { return info()->getWorldName(); } +LPCWSTR LevelGenerationOptions::getDisplayName() { return info()->getDisplayName(); } +wstring LevelGenerationOptions::getGrfPath() { return info()->getGrfPath(); } +bool LevelGenerationOptions::requiresBaseSave() { return info()->requiresBaseSave(); } +wstring LevelGenerationOptions::getBaseSavePath() { return info()->getBaseSavePath(); } + +void LevelGenerationOptions::setGrSource(GrSource *grs) { m_pSrc = grs; } + +void LevelGenerationOptions::setRequiresTexturePack(bool x) { info()->setRequiresTexturePack(x); } +void LevelGenerationOptions::setRequiredTexturePackId(UINT x) { info()->setRequiredTexturePackId(x); } +void LevelGenerationOptions::setDefaultSaveName(const wstring &x) { info()->setDefaultSaveName(x); } +void LevelGenerationOptions::setWorldName(const wstring &x) { info()->setWorldName(x); } +void LevelGenerationOptions::setDisplayName(const wstring &x) { info()->setDisplayName(x); } +void LevelGenerationOptions::setGrfPath(const wstring &x) { info()->setGrfPath(x); } +void LevelGenerationOptions::setBaseSavePath(const wstring &x) { info()->setBaseSavePath(x); } + +bool LevelGenerationOptions::ready() { return info()->ready(); } + +void LevelGenerationOptions::setBaseSaveData(PBYTE pbData, DWORD dwSize) { m_pbBaseSaveData = pbData; m_dwBaseSaveSize = dwSize; } +PBYTE LevelGenerationOptions::getBaseSaveData(DWORD &size) { size = m_dwBaseSaveSize; return m_pbBaseSaveData; } +bool LevelGenerationOptions::hasBaseSaveData() { return m_dwBaseSaveSize > 0 && m_pbBaseSaveData != NULL; } +void LevelGenerationOptions::deleteBaseSaveData() { if(m_pbBaseSaveData) delete m_pbBaseSaveData; m_pbBaseSaveData = NULL; m_dwBaseSaveSize = 0; } + +bool LevelGenerationOptions::hasLoadedData() { return m_hasLoadedData; } +void LevelGenerationOptions::setLoadedData() { m_hasLoadedData = true; } + +__int64 LevelGenerationOptions::getLevelSeed() { return m_seed; } +Pos *LevelGenerationOptions::getSpawnPos() { return m_spawnPos; } +bool LevelGenerationOptions::getuseFlatWorld() { return m_useFlatWorld; } + +bool LevelGenerationOptions::requiresGameRules() { return m_bRequiresGameRules; } +void LevelGenerationOptions::setRequiredGameRules(LevelRuleset *rules) { m_requiredGameRules = rules; m_bRequiresGameRules = true; } +LevelRuleset *LevelGenerationOptions::getRequiredGameRules() { return m_requiredGameRules; } \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelGenerationOptions.h b/Minecraft.Client/Common/GameRules/LevelGenerationOptions.h new file mode 100644 index 0000000..0cc9da7 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelGenerationOptions.h @@ -0,0 +1,216 @@ +#pragma once +using namespace std; + +#pragma message("LevelGenerationOptions.h ") + +#include "GameRuleDefinition.h" +#include "..\..\..\Minecraft.World\StructureFeature.h" + +class ApplySchematicRuleDefinition; +class LevelChunk; +class ConsoleGenerateStructure; +class ConsoleSchematicFile; +class LevelRuleset; +class BiomeOverride; +class StartFeature; + +class GrSource +{ +public: + // 4J-JEV: + // Moved all this here; I didn't like that all this header information + // was being mixed in with all the game information as they have + // completely different lifespans. + + virtual bool requiresTexturePack()=0; + virtual UINT getRequiredTexturePackId()=0; + virtual wstring getDefaultSaveName()=0; + virtual LPCWSTR getWorldName()=0; + virtual LPCWSTR getDisplayName()=0; + virtual wstring getGrfPath()=0; + virtual bool requiresBaseSave() = 0; + virtual wstring getBaseSavePath() = 0; + + virtual void setRequiresTexturePack(bool)=0; + virtual void setRequiredTexturePackId(UINT)=0; + virtual void setDefaultSaveName(const wstring &)=0; + virtual void setWorldName(const wstring &)=0; + virtual void setDisplayName(const wstring &)=0; + virtual void setGrfPath(const wstring &)=0; + virtual void setBaseSavePath(const wstring &)=0; + + virtual bool ready()=0; + + //virtual void getGrfData(PBYTE &pData, DWORD &pSize)=0; +}; + +class JustGrSource : public GrSource +{ +protected: + wstring m_worldName; + wstring m_displayName; + wstring m_defaultSaveName; + bool m_bRequiresTexturePack; + int m_requiredTexturePackId; + wstring m_grfPath; + wstring m_baseSavePath; + bool m_bRequiresBaseSave; + +public: + virtual bool requiresTexturePack(); + virtual UINT getRequiredTexturePackId(); + virtual wstring getDefaultSaveName(); + virtual LPCWSTR getWorldName(); + virtual LPCWSTR getDisplayName(); + virtual wstring getGrfPath(); + virtual bool requiresBaseSave(); + virtual wstring getBaseSavePath(); + + virtual void setRequiresTexturePack(bool x); + virtual void setRequiredTexturePackId(UINT x); + virtual void setDefaultSaveName(const wstring &x); + virtual void setWorldName(const wstring &x); + virtual void setDisplayName(const wstring &x); + virtual void setGrfPath(const wstring &x); + virtual void setBaseSavePath(const wstring &x); + + virtual bool ready(); + + JustGrSource(); +}; + +class LevelGenerationOptions : public GameRuleDefinition +{ +public: + enum eSrc + { + eSrc_none, + + eSrc_fromSave, // Neither content or header is persistent. + + eSrc_fromDLC, // Header is persistent, content should be deleted to conserve space. + + eSrc_tutorial, // Both header and content is persistent, content cannot be reloaded. + + eSrc_MAX + }; + +private: + eSrc m_src; + + GrSource *m_pSrc; + GrSource *info(); + + bool m_hasLoadedData; + + PBYTE m_pbBaseSaveData; + DWORD m_dwBaseSaveSize; + +public: + + void setSrc(eSrc src); + eSrc getSrc(); + + bool isTutorial(); + bool isFromSave(); + bool isFromDLC(); + + bool requiresTexturePack(); + UINT getRequiredTexturePackId(); + wstring getDefaultSaveName(); + LPCWSTR getWorldName(); + LPCWSTR getDisplayName(); + wstring getGrfPath(); + bool requiresBaseSave(); + wstring getBaseSavePath(); + + void setGrSource(GrSource *grs); + + void setRequiresTexturePack(bool x); + void setRequiredTexturePackId(UINT x); + void setDefaultSaveName(const wstring &x); + void setWorldName(const wstring &x); + void setDisplayName(const wstring &x); + void setGrfPath(const wstring &x); + void setBaseSavePath(const wstring &x); + + bool ready(); + + void setBaseSaveData(PBYTE pbData, DWORD dwSize); + PBYTE getBaseSaveData(DWORD &size); + bool hasBaseSaveData(); + void deleteBaseSaveData(); + + bool hasLoadedData(); + void setLoadedData(); + +private: + // This should match the "MapOptionsRule" definition in the XML schema + __int64 m_seed; + bool m_useFlatWorld; + Pos *m_spawnPos; + vector m_schematicRules; + vector m_structureRules; + bool m_bHaveMinY; + int m_minY; + unordered_map m_schematics; + vector m_biomeOverrides; + vector m_features; + + bool m_bRequiresGameRules; + LevelRuleset *m_requiredGameRules; + + StringTable *m_stringTable; + +public: + LevelGenerationOptions(); + ~LevelGenerationOptions(); + + virtual ConsoleGameRules::EGameRuleType getActionType(); + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + __int64 getLevelSeed(); + Pos *getSpawnPos(); + bool getuseFlatWorld(); + + void processSchematics(LevelChunk *chunk); + void processSchematicsLighting(LevelChunk *chunk); + + bool checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1); + +private: + void clearSchematics(); + +public: + ConsoleSchematicFile *loadSchematicFile(const wstring &filename, PBYTE pbData, DWORD dwLen); + +public: + ConsoleSchematicFile *getSchematicFile(const wstring &filename); + void releaseSchematicFile(const wstring &filename); + + bool requiresGameRules(); + void setRequiredGameRules(LevelRuleset *rules); + LevelRuleset *getRequiredGameRules(); + + void getBiomeOverride(int biomeId, BYTE &tile, BYTE &topTile); + bool isFeatureChunk(int chunkX, int chunkZ, StructureFeature::EFeatureTypes feature); + + void loadStringTable(StringTable *table); + LPCWSTR getString(const wstring &key); + + unordered_map *getUnfinishedSchematicFiles(); + + // 4J-JEV: + // ApplySchematicRules contain limited state + // which needs to be reset BEFORE a new game starts. + void reset_start(); + + // 4J-JEV: + // This file contains state that needs to be deleted + // or reset once a game has finished. + void reset_finish(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelGenerators.cpp b/Minecraft.Client/Common/GameRules/LevelGenerators.cpp new file mode 100644 index 0000000..653a26d --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelGenerators.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "LevelGenerationOptions.h" +#include "LevelGenerators.h" + + +LevelGenerators::LevelGenerators() +{ +} + +void LevelGenerators::addLevelGenerator(const wstring &displayName, LevelGenerationOptions *generator) +{ + if(!displayName.empty()) generator->setDisplayName(displayName); + m_levelGenerators.push_back(generator); +} + +void LevelGenerators::removeLevelGenerator(LevelGenerationOptions *generator) +{ + vector::iterator it; + while ( (it = find( m_levelGenerators.begin(), + m_levelGenerators.end(), + generator ) ) + != m_levelGenerators.end() ) + { + m_levelGenerators.erase(it); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelGenerators.h b/Minecraft.Client/Common/GameRules/LevelGenerators.h new file mode 100644 index 0000000..824b838 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelGenerators.h @@ -0,0 +1,19 @@ +#pragma once + +using namespace std; + +class LevelGenerationOptions; + +class LevelGenerators +{ +private: + vector m_levelGenerators; + +public: + LevelGenerators(); + + void addLevelGenerator(const wstring &displayName, LevelGenerationOptions *generator); + void removeLevelGenerator(LevelGenerationOptions *generator); + + vector *getLevelGenerators() { return &m_levelGenerators; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelRules.cpp b/Minecraft.Client/Common/GameRules/LevelRules.cpp new file mode 100644 index 0000000..b7c8a8a --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelRules.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "LevelRules.h" + + +LevelRules::LevelRules() +{ +} + +void LevelRules::addLevelRule(const wstring &displayName, PBYTE pbData, DWORD dwLen) +{ +} + +void LevelRules::addLevelRule(const wstring &displayName, LevelRuleset *rootRule) +{ +} + +void LevelRules::removeLevelRule(LevelRuleset *removing) +{ + // TODO ? +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelRules.h b/Minecraft.Client/Common/GameRules/LevelRules.h new file mode 100644 index 0000000..a94a212 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelRules.h @@ -0,0 +1,14 @@ +#pragma once + +class LevelRuleset; + +class LevelRules +{ +public: + LevelRules(); + + void addLevelRule(const wstring &displayName, PBYTE pbData, DWORD dwLen); + void addLevelRule(const wstring &displayName, LevelRuleset *rootRule); + + void removeLevelRule(LevelRuleset *removing); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/LevelRuleset.cpp b/Minecraft.Client/Common/GameRules/LevelRuleset.cpp new file mode 100644 index 0000000..1c7ecd4 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelRuleset.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\StringTable.h" +#include "ConsoleGameRules.h" +#include "LevelRuleset.h" + +LevelRuleset::LevelRuleset() +{ + m_stringTable = NULL; +} + +LevelRuleset::~LevelRuleset() +{ + for(AUTO_VAR(it, m_areas.begin()); it != m_areas.end(); ++it) + { + delete *it; + } +} + +void LevelRuleset::getChildren(vector *children) +{ + CompoundGameRuleDefinition::getChildren(children); + for (AUTO_VAR(it, m_areas.begin()); it != m_areas.end(); it++) + children->push_back(*it); +} + +GameRuleDefinition *LevelRuleset::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_NamedArea) + { + rule = new NamedAreaRuleDefinition(); + m_areas.push_back((NamedAreaRuleDefinition *)rule); + } + else + { + rule = CompoundGameRuleDefinition::addChild(ruleType); + } + return rule; +} + +void LevelRuleset::loadStringTable(StringTable *table) +{ + m_stringTable = table; +} + +LPCWSTR LevelRuleset::getString(const wstring &key) +{ + if(m_stringTable == NULL) + { + return L""; + } + else + { + return m_stringTable->getString(key); + } +} + +AABB *LevelRuleset::getNamedArea(const wstring &areaName) +{ + AABB *area = NULL; + for(AUTO_VAR(it, m_areas.begin()); it != m_areas.end(); ++it) + { + if( (*it)->getName().compare(areaName) == 0 ) + { + area = (*it)->getArea(); + break; + } + } + return area; +} diff --git a/Minecraft.Client/Common/GameRules/LevelRuleset.h b/Minecraft.Client/Common/GameRules/LevelRuleset.h new file mode 100644 index 0000000..bbb17c4 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/LevelRuleset.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CompoundGameRuleDefinition.h" + +class NamedAreaRuleDefinition; + +class LevelRuleset : public CompoundGameRuleDefinition +{ +private: + vector m_areas; + StringTable *m_stringTable; +public: + LevelRuleset(); + ~LevelRuleset(); + + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_LevelRules; } + + void loadStringTable(StringTable *table); + LPCWSTR getString(const wstring &key); + + AABB *getNamedArea(const wstring &areaName); + + StringTable *getStringTable() { return m_stringTable; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.cpp new file mode 100644 index 0000000..41ff15e --- /dev/null +++ b/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "NamedAreaRuleDefinition.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" + +NamedAreaRuleDefinition::NamedAreaRuleDefinition() +{ + m_name = L""; + m_area = AABB::newPermanent(0,0,0,0,0,0); +} + +NamedAreaRuleDefinition::~NamedAreaRuleDefinition() +{ + delete m_area; +} + +void NamedAreaRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + GameRuleDefinition::writeAttributes(dos, numAttributes + 7); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_name); + dos->writeUTF(m_name); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x0); + dos->writeUTF(_toString(m_area->x0)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y0); + dos->writeUTF(_toString(m_area->y0)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z0); + dos->writeUTF(_toString(m_area->z0)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x1); + dos->writeUTF(_toString(m_area->x1)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y1); + dos->writeUTF(_toString(m_area->y1)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z1); + dos->writeUTF(_toString(m_area->z1)); +} + +void NamedAreaRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"name") == 0) + { + m_name = attributeValue; +#ifndef _CONTENT_PACKAGE + wprintf(L"NamedAreaRuleDefinition: Adding parameter name=%ls\n",m_name.c_str()); +#endif + } + else if(attributeName.compare(L"x0") == 0) + { + m_area->x0 = _fromString(attributeValue); + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter x0=%f\n",m_area->x0); + } + else if(attributeName.compare(L"y0") == 0) + { + m_area->y0 = _fromString(attributeValue); + if(m_area->y0 < 0) m_area->y0 = 0; + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter y0=%f\n",m_area->y0); + } + else if(attributeName.compare(L"z0") == 0) + { + m_area->z0 = _fromString(attributeValue); + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter z0=%f\n",m_area->z0); + } + else if(attributeName.compare(L"x1") == 0) + { + m_area->x1 = _fromString(attributeValue); + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter x1=%f\n",m_area->x1); + } + else if(attributeName.compare(L"y1") == 0) + { + m_area->y1 = _fromString(attributeValue); + if(m_area->y1 < 0) m_area->y1 = 0; + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter y1=%f\n",m_area->y1); + } + else if(attributeName.compare(L"z1") == 0) + { + m_area->z1 = _fromString(attributeValue); + app.DebugPrintf("NamedAreaRuleDefinition: Adding parameter z1=%f\n",m_area->z1); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} diff --git a/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.h b/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.h new file mode 100644 index 0000000..7cf7db1 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/NamedAreaRuleDefinition.h @@ -0,0 +1,23 @@ +#pragma once + +#include "GameRuleDefinition.h" + +class NamedAreaRuleDefinition : public GameRuleDefinition +{ +private: + wstring m_name; + AABB *m_area; + +public: + NamedAreaRuleDefinition(); + ~NamedAreaRuleDefinition(); + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_NamedArea; } + + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + AABB *getArea() { return m_area; } + wstring getName() { return m_name; } +}; diff --git a/Minecraft.Client/Common/GameRules/StartFeature.cpp b/Minecraft.Client/Common/GameRules/StartFeature.cpp new file mode 100644 index 0000000..9d5f15c --- /dev/null +++ b/Minecraft.Client/Common/GameRules/StartFeature.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "StartFeature.h" + +StartFeature::StartFeature() +{ + m_chunkX = 0; + m_chunkZ = 0; + m_feature = StructureFeature::eFeature_Temples; +} + +void StartFeature::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + GameRuleDefinition::writeAttributes(dos, numAttrs + 3); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_chunkX); + dos->writeUTF(_toString(m_chunkX)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_chunkZ); + dos->writeUTF(_toString(m_chunkZ)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_feature); + dos->writeUTF(_toString((int)m_feature)); +} + +void StartFeature::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"chunkX") == 0) + { + int value = _fromString(attributeValue); + m_chunkX = value; + app.DebugPrintf("StartFeature: Adding parameter chunkX=%d\n",m_chunkX); + } + else if(attributeName.compare(L"chunkZ") == 0) + { + int value = _fromString(attributeValue); + m_chunkZ = value; + app.DebugPrintf("StartFeature: Adding parameter chunkZ=%d\n",m_chunkZ); + } + else if(attributeName.compare(L"feature") == 0) + { + int value = _fromString(attributeValue); + m_feature = (StructureFeature::EFeatureTypes)value; + app.DebugPrintf("StartFeature: Adding parameter feature=%d\n",m_feature); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool StartFeature::isFeatureChunk(int chunkX, int chunkZ, StructureFeature::EFeatureTypes feature) +{ + return chunkX == m_chunkX && chunkZ == m_chunkZ && feature == m_feature; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/StartFeature.h b/Minecraft.Client/Common/GameRules/StartFeature.h new file mode 100644 index 0000000..d3f1280 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/StartFeature.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "GameRuleDefinition.h" +#include "..\..\..\Minecraft.World\StructureFeature.h" + +class StartFeature : public GameRuleDefinition +{ +private: + int m_chunkX, m_chunkZ; + StructureFeature::EFeatureTypes m_feature; + +public: + StartFeature(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_StartFeature; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool isFeatureChunk(int chunkX, int chunkZ, StructureFeature::EFeatureTypes feature); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.cpp new file mode 100644 index 0000000..6e55cd4 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.cpp @@ -0,0 +1,171 @@ +#include "stdafx.h" +#include "UpdatePlayerRuleDefinition.h" +#include "ConsoleGameRules.h" +#include "..\..\..\Minecraft.World\Pos.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.food.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +UpdatePlayerRuleDefinition::UpdatePlayerRuleDefinition() +{ + m_bUpdateHealth = m_bUpdateFood = m_bUpdateYRot = false;; + m_health = 0; + m_food = 0; + m_spawnPos = NULL; + m_yRot = 0.0f; +} + +UpdatePlayerRuleDefinition::~UpdatePlayerRuleDefinition() +{ + for(AUTO_VAR(it, m_items.begin()); it != m_items.end(); ++it) + { + delete *it; + } +} + +void UpdatePlayerRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + int attrCount = 3; + if(m_bUpdateHealth) ++attrCount; + if(m_bUpdateFood) ++attrCount; + if(m_bUpdateYRot) ++attrCount; + GameRuleDefinition::writeAttributes(dos, numAttributes + attrCount ); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnX); + dos->writeUTF(_toString(m_spawnPos->x)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnY); + dos->writeUTF(_toString(m_spawnPos->y)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_spawnZ); + dos->writeUTF(_toString(m_spawnPos->z)); + + if(m_bUpdateYRot) + { + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_yRot); + dos->writeUTF(_toString(m_yRot)); + } + if(m_bUpdateHealth) + { + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_food); + dos->writeUTF(_toString(m_health)); + } + if(m_bUpdateFood) + { + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_health); + dos->writeUTF(_toString(m_food)); + } +} + +void UpdatePlayerRuleDefinition::getChildren(vector *children) +{ + GameRuleDefinition::getChildren(children); + for(AUTO_VAR(it, m_items.begin()); it!=m_items.end(); it++) + children->push_back(*it); +} + +GameRuleDefinition *UpdatePlayerRuleDefinition::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_AddItem) + { + rule = new AddItemRuleDefinition(); + m_items.push_back((AddItemRuleDefinition *)rule); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"UpdatePlayerRuleDefinition: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + return rule; +} + +void UpdatePlayerRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"spawnX") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->x = value; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter spawnX=%d\n",value); + } + else if(attributeName.compare(L"spawnY") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->y = value; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter spawnY=%d\n",value); + } + else if(attributeName.compare(L"spawnZ") == 0) + { + if(m_spawnPos == NULL) m_spawnPos = new Pos(); + int value = _fromString(attributeValue); + m_spawnPos->z = value; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter spawnZ=%d\n",value); + } + else if(attributeName.compare(L"health") == 0) + { + int value = _fromString(attributeValue); + m_health = value; + m_bUpdateHealth = true; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter health=%d\n",value); + } + else if(attributeName.compare(L"food") == 0) + { + int value = _fromString(attributeValue); + m_food = value; + m_bUpdateFood = true; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter health=%d\n",value); + } + else if(attributeName.compare(L"yRot") == 0) + { + float value = _fromString(attributeValue); + m_yRot = value; + m_bUpdateYRot = true; + app.DebugPrintf("UpdatePlayerRuleDefinition: Adding parameter yRot=%f\n",value); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +void UpdatePlayerRuleDefinition::postProcessPlayer(shared_ptr player) +{ + if(m_bUpdateHealth) + { + player->lastHealth = m_health; + player->setHealth(m_health); + } + + if(m_bUpdateFood) + { + player->getFoodData()->setFoodLevel(m_food); + } + + double x = player->x; + double y = player->y; + double z = player->z; + float yRot = player->yRot; + float xRot = player->xRot; + if(m_spawnPos != NULL) + { + x = m_spawnPos->x; + y = m_spawnPos->y; + z = m_spawnPos->z; + } + + if(m_bUpdateYRot) + { + yRot = m_yRot; + } + + if(m_spawnPos != NULL || m_bUpdateYRot) player->absMoveTo(x,y,z,yRot,xRot); + + for(AUTO_VAR(it, m_items.begin()); it != m_items.end(); ++it) + { + AddItemRuleDefinition *addItem = *it; + + addItem->addItemToContainer(player->inventory, -1); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.h b/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.h new file mode 100644 index 0000000..538aefa --- /dev/null +++ b/Minecraft.Client/Common/GameRules/UpdatePlayerRuleDefinition.h @@ -0,0 +1,33 @@ +#pragma once +using namespace std; + +#include "GameRuleDefinition.h" + +class AddItemRuleDefinition; +class Pos; + +class UpdatePlayerRuleDefinition : public GameRuleDefinition +{ +private: + vector m_items; + + bool m_bUpdateHealth, m_bUpdateFood, m_bUpdateYRot, m_bUpdateInventory; + int m_health; + int m_food; + Pos *m_spawnPos; + float m_yRot; + +public: + UpdatePlayerRuleDefinition(); + ~UpdatePlayerRuleDefinition(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_UpdatePlayerRule; } + + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + virtual void postProcessPlayer(shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.cpp b/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.cpp new file mode 100644 index 0000000..965405a --- /dev/null +++ b/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "UseTileRuleDefinition.h" + +UseTileRuleDefinition::UseTileRuleDefinition() +{ + m_tileId = -1; + m_useCoords = false; +} + +void UseTileRuleDefinition::writeAttributes(DataOutputStream *dos, UINT numAttributes) +{ + GameRuleDefinition::writeAttributes(dos, numAttributes + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_tileId); + dos->writeUTF(_toString(m_tileId)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_useCoords); + dos->writeUTF(_toString(m_useCoords)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x); + dos->writeUTF(_toString(m_coordinates.x)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y); + dos->writeUTF(_toString(m_coordinates.y)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z); + dos->writeUTF(_toString(m_coordinates.z)); +} + +void UseTileRuleDefinition::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"tileId") == 0) + { + m_tileId = _fromString(attributeValue); + app.DebugPrintf("UseTileRule: Adding parameter tileId=%d\n",m_tileId); + } + else if(attributeName.compare(L"useCoords") == 0) + { + m_useCoords = _fromString(attributeValue); + app.DebugPrintf("UseTileRule: Adding parameter useCoords=%s\n",m_useCoords?"TRUE":"FALSE"); + } + else if(attributeName.compare(L"x") == 0) + { + m_coordinates.x = _fromString(attributeValue); + app.DebugPrintf("UseTileRule: Adding parameter x=%d\n",m_coordinates.x); + } + else if(attributeName.compare(L"y") == 0) + { + m_coordinates.y = _fromString(attributeValue); + app.DebugPrintf("UseTileRule: Adding parameter y=%d\n",m_coordinates.y); + } + else if(attributeName.compare(L"z") == 0) + { + m_coordinates.z = _fromString(attributeValue); + app.DebugPrintf("UseTileRule: Adding parameter z=%d\n",m_coordinates.z); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool UseTileRuleDefinition::onUseTile(GameRule *rule, int tileId, int x, int y, int z) +{ + bool statusChanged = false; + if( m_tileId == tileId ) + { + if( !m_useCoords || (m_coordinates.x == x && m_coordinates.y == y && m_coordinates.z == z) ) + { + if(!getComplete(rule)) + { + statusChanged = true; + setComplete(rule,true); + app.DebugPrintf("Completed UseTileRule with info - t:%d, coords:%s, x:%d, y:%d, z:%d\n", m_tileId,m_useCoords?"TRUE":"FALSE",m_coordinates.x,m_coordinates.y,m_coordinates.z); + + // Send a packet or some other announcement here + } + } + } + return statusChanged; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.h b/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.h new file mode 100644 index 0000000..ad64bfe --- /dev/null +++ b/Minecraft.Client/Common/GameRules/UseTileRuleDefinition.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "GameRuleDefinition.h" +#include "..\..\..\Minecraft.World\Pos.h" + +class UseTileRuleDefinition : public GameRuleDefinition +{ +private: + // These values should map directly to the xsd definition for this Rule + int m_tileId; + bool m_useCoords; + Pos m_coordinates; + +public: + UseTileRuleDefinition(); + + ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_UseTileRule; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + virtual bool onUseTile(GameRule *rule, int tileId, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.cpp b/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.cpp new file mode 100644 index 0000000..6d687b3 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "XboxStructureActionGenerateBox.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.levelgen.structure.h" + +XboxStructureActionGenerateBox::XboxStructureActionGenerateBox() +{ + m_x0 = m_y0 = m_z0 = m_x1 = m_y1 = m_z1 = m_edgeTile = m_fillTile = 0; + m_skipAir = false; +} + +void XboxStructureActionGenerateBox::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + ConsoleGenerateStructureAction::writeAttributes(dos, numAttrs + 9); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x0); + dos->writeUTF(_toString(m_x0)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y0); + dos->writeUTF(_toString(m_y0)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z0); + dos->writeUTF(_toString(m_z0)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x1); + dos->writeUTF(_toString(m_x1)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y1); + dos->writeUTF(_toString(m_y1)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z1); + dos->writeUTF(_toString(m_z1)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_edgeTile); + dos->writeUTF(_toString(m_edgeTile)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_fillTile); + dos->writeUTF(_toString(m_fillTile)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_skipAir); + dos->writeUTF(_toString(m_skipAir)); +} + +void XboxStructureActionGenerateBox::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"x0") == 0) + { + int value = _fromString(attributeValue); + m_x0 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter x0=%d\n",m_x0); + } + else if(attributeName.compare(L"y0") == 0) + { + int value = _fromString(attributeValue); + m_y0 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter y0=%d\n",m_y0); + } + else if(attributeName.compare(L"z0") == 0) + { + int value = _fromString(attributeValue); + m_z0 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter z0=%d\n",m_z0); + } + else if(attributeName.compare(L"x1") == 0) + { + int value = _fromString(attributeValue); + m_x1 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter x1=%d\n",m_x1); + } + else if(attributeName.compare(L"y1") == 0) + { + int value = _fromString(attributeValue); + m_y1 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter y1=%d\n",m_y1); + } + else if(attributeName.compare(L"z1") == 0) + { + int value = _fromString(attributeValue); + m_z1 = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter z1=%d\n",m_z1); + } + else if(attributeName.compare(L"edgeTile") == 0) + { + int value = _fromString(attributeValue); + m_edgeTile = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter edgeTile=%d\n",m_edgeTile); + } + else if(attributeName.compare(L"fillTile") == 0) + { + int value = _fromString(attributeValue); + m_fillTile = value; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter fillTile=%d\n",m_fillTile); + } + else if(attributeName.compare(L"skipAir") == 0) + { + if(attributeValue.compare(L"true") == 0) m_skipAir = true; + app.DebugPrintf("XboxStructureActionGenerateBox: Adding parameter skipAir=%s\n",m_skipAir?"TRUE":"FALSE"); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool XboxStructureActionGenerateBox::generateBoxInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB) +{ + app.DebugPrintf("XboxStructureActionGenerateBox - generating a box\n"); + structure->generateBox(level,chunkBB,m_x0,m_y0,m_z0,m_x1,m_y1,m_z1,m_edgeTile,m_fillTile,m_skipAir); + return true; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.h b/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.h new file mode 100644 index 0000000..78664d4 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionGenerateBox.h @@ -0,0 +1,26 @@ +#pragma once +#include "ConsoleGenerateStructureAction.h" + +class StructurePiece; +class Level; +class BoundingBox; + +class XboxStructureActionGenerateBox : public ConsoleGenerateStructureAction +{ +private: + int m_x0, m_y0, m_z0, m_x1, m_y1, m_z1, m_edgeTile, m_fillTile; + bool m_skipAir; +public: + XboxStructureActionGenerateBox(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_GenerateBox; } + + virtual int getEndX() { return m_x1; } + virtual int getEndY() { return m_y1; } + virtual int getEndZ() { return m_z1; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool generateBoxInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.cpp b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.cpp new file mode 100644 index 0000000..b816d5b --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "XboxStructureActionPlaceBlock.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.levelgen.structure.h" + +XboxStructureActionPlaceBlock::XboxStructureActionPlaceBlock() +{ + m_x = m_y = m_z = m_tile = m_data = 0; +} + +void XboxStructureActionPlaceBlock::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + ConsoleGenerateStructureAction::writeAttributes(dos, numAttrs + 5); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_x); + dos->writeUTF(_toString(m_x)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_y); + dos->writeUTF(_toString(m_y)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_z); + dos->writeUTF(_toString(m_z)); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_data); + dos->writeUTF(_toString(m_data)); + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_block); + dos->writeUTF(_toString(m_tile)); +} + + +void XboxStructureActionPlaceBlock::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"x") == 0) + { + int value = _fromString(attributeValue); + m_x = value; + app.DebugPrintf("XboxStructureActionPlaceBlock: Adding parameter x=%d\n",m_x); + } + else if(attributeName.compare(L"y") == 0) + { + int value = _fromString(attributeValue); + m_y = value; + app.DebugPrintf("XboxStructureActionPlaceBlock: Adding parameter y=%d\n",m_y); + } + else if(attributeName.compare(L"z") == 0) + { + int value = _fromString(attributeValue); + m_z = value; + app.DebugPrintf("XboxStructureActionPlaceBlock: Adding parameter z=%d\n",m_z); + } + else if(attributeName.compare(L"block") == 0) + { + int value = _fromString(attributeValue); + m_tile = value; + app.DebugPrintf("XboxStructureActionPlaceBlock: Adding parameter block=%d\n",m_tile); + } + else if(attributeName.compare(L"data") == 0) + { + int value = _fromString(attributeValue); + m_data = value; + app.DebugPrintf("XboxStructureActionPlaceBlock: Adding parameter data=%d\n",m_data); + } + else + { + GameRuleDefinition::addAttribute(attributeName, attributeValue); + } +} + +bool XboxStructureActionPlaceBlock::placeBlockInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB) +{ + app.DebugPrintf("XboxStructureActionPlaceBlock - placing a block\n"); + structure->placeBlock(level,m_tile,m_data,m_x,m_y,m_z,chunkBB); + return true; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.h b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.h new file mode 100644 index 0000000..3ee377b --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceBlock.h @@ -0,0 +1,25 @@ +#pragma once +#include "ConsoleGenerateStructureAction.h" + +class StructurePiece; +class Level; +class BoundingBox; + +class XboxStructureActionPlaceBlock : public ConsoleGenerateStructureAction +{ +protected: + int m_x, m_y, m_z, m_tile, m_data; +public: + XboxStructureActionPlaceBlock(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_PlaceBlock; } + + virtual int getEndX() { return m_x; } + virtual int getEndY() { return m_y; } + virtual int getEndZ() { return m_z; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool placeBlockInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.cpp b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.cpp new file mode 100644 index 0000000..8184f45 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XboxStructureActionPlaceContainer.h" +#include "AddItemRuleDefinition.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.levelgen.structure.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +XboxStructureActionPlaceContainer::XboxStructureActionPlaceContainer() +{ + m_tile = Tile::chest_Id; +} + +XboxStructureActionPlaceContainer::~XboxStructureActionPlaceContainer() +{ + for(AUTO_VAR(it, m_items.begin()); it != m_items.end(); ++it) + { + delete *it; + } +} + +// 4J-JEV: Super class handles attr-facing fine. +//void XboxStructureActionPlaceContainer::writeAttributes(DataOutputStream *dos, UINT numAttrs) + + +void XboxStructureActionPlaceContainer::getChildren(vector *children) +{ + XboxStructureActionPlaceBlock::getChildren(children); + for(AUTO_VAR(it, m_items.begin()); it!=m_items.end(); it++) + children->push_back( *it ); +} + +GameRuleDefinition *XboxStructureActionPlaceContainer::addChild(ConsoleGameRules::EGameRuleType ruleType) +{ + GameRuleDefinition *rule = NULL; + if(ruleType == ConsoleGameRules::eGameRuleType_AddItem) + { + rule = new AddItemRuleDefinition(); + m_items.push_back((AddItemRuleDefinition *)rule); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"XboxStructureActionPlaceContainer: Attempted to add invalid child rule - %d\n", ruleType ); +#endif + } + return rule; +} + +void XboxStructureActionPlaceContainer::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"facing") == 0) + { + int value = _fromString(attributeValue); + m_data = value; + app.DebugPrintf("XboxStructureActionPlaceContainer: Adding parameter facing=%d\n",m_data); + } + else + { + XboxStructureActionPlaceBlock::addAttribute(attributeName, attributeValue); + } +} + +bool XboxStructureActionPlaceContainer::placeContainerInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB) +{ + int worldX = structure->getWorldX( m_x, m_z ); + int worldY = structure->getWorldY( m_y ); + int worldZ = structure->getWorldZ( m_x, m_z ); + + if ( chunkBB->isInside( worldX, worldY, worldZ ) ) + { + if ( level->getTileEntity( worldX, worldY, worldZ ) != NULL ) + { + // Remove the current tile entity + level->removeTileEntity( worldX, worldY, worldZ ); + level->setTile( worldX, worldY, worldZ, 0 ); + } + + level->setTile( worldX, worldY, worldZ, m_tile ); + shared_ptr container = dynamic_pointer_cast(level->getTileEntity( worldX, worldY, worldZ )); + + app.DebugPrintf("XboxStructureActionPlaceContainer - placing a container at (%d,%d,%d)\n", worldX, worldY, worldZ); + if ( container != NULL ) + { + level->setData( worldX, worldY, worldZ, m_data); + // Add items + int slotId = 0; + for(AUTO_VAR(it, m_items.begin()); it != m_items.end() && (slotId < container->getContainerSize()); ++it, ++slotId ) + { + AddItemRuleDefinition *addItem = *it; + + addItem->addItemToContainer(container,slotId); + } + } + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.h b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.h new file mode 100644 index 0000000..6355ca1 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceContainer.h @@ -0,0 +1,29 @@ +#pragma once + +#include "XboxStructureActionPlaceBlock.h" + +class AddItemRuleDefinition; +class StructurePiece; +class Level; +class BoundingBox; + +class XboxStructureActionPlaceContainer : public XboxStructureActionPlaceBlock +{ +private: + vector m_items; +public: + XboxStructureActionPlaceContainer(); + ~XboxStructureActionPlaceContainer(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_PlaceContainer; } + + virtual void getChildren(vector *children); + virtual GameRuleDefinition *addChild(ConsoleGameRules::EGameRuleType ruleType); + + // 4J-JEV: Super class handles attr-facing fine. + //virtual void writeAttributes(DataOutputStream *dos, UINT numAttributes); + + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool placeContainerInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.cpp b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.cpp new file mode 100644 index 0000000..1eca334 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XboxStructureActionPlaceSpawner.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.levelgen.structure.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" + +XboxStructureActionPlaceSpawner::XboxStructureActionPlaceSpawner() +{ + m_tile = Tile::mobSpawner_Id; + m_entityId = L"Pig"; +} + +XboxStructureActionPlaceSpawner::~XboxStructureActionPlaceSpawner() +{ +} + +void XboxStructureActionPlaceSpawner::writeAttributes(DataOutputStream *dos, UINT numAttrs) +{ + XboxStructureActionPlaceBlock::writeAttributes(dos, numAttrs + 1); + + ConsoleGameRules::write(dos, ConsoleGameRules::eGameRuleAttr_entity); + dos->writeUTF(m_entityId); +} + +void XboxStructureActionPlaceSpawner::addAttribute(const wstring &attributeName, const wstring &attributeValue) +{ + if(attributeName.compare(L"entity") == 0) + { + m_entityId = attributeValue; +#ifndef _CONTENT_PACKAGE + wprintf(L"XboxStructureActionPlaceSpawner: Adding parameter entity=%ls\n",m_entityId.c_str()); +#endif + } + else + { + XboxStructureActionPlaceBlock::addAttribute(attributeName, attributeValue); + } +} + +bool XboxStructureActionPlaceSpawner::placeSpawnerInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB) +{ + int worldX = structure->getWorldX( m_x, m_z ); + int worldY = structure->getWorldY( m_y ); + int worldZ = structure->getWorldZ( m_x, m_z ); + + if ( chunkBB->isInside( worldX, worldY, worldZ ) ) + { + if ( level->getTileEntity( worldX, worldY, worldZ ) != NULL ) + { + // Remove the current tile entity + level->removeTileEntity( worldX, worldY, worldZ ); + level->setTile( worldX, worldY, worldZ, 0 ); + } + + level->setTile( worldX, worldY, worldZ, m_tile ); + shared_ptr entity = dynamic_pointer_cast(level->getTileEntity( worldX, worldY, worldZ )); + +#ifndef _CONTENT_PACKAGE + wprintf(L"XboxStructureActionPlaceSpawner - placing a %ls spawner at (%d,%d,%d)\n", m_entityId.c_str(), worldX, worldY, worldZ); +#endif + if( entity != NULL ) + { + entity->setEntityId(m_entityId); + } + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.h b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.h new file mode 100644 index 0000000..1600098 --- /dev/null +++ b/Minecraft.Client/Common/GameRules/XboxStructureActionPlaceSpawner.h @@ -0,0 +1,24 @@ +#pragma once + +#include "XboxStructureActionPlaceBlock.h" + +class StructurePiece; +class Level; +class BoundingBox; +class GRFObject; + +class XboxStructureActionPlaceSpawner : public XboxStructureActionPlaceBlock +{ +private: + wstring m_entityId; +public: + XboxStructureActionPlaceSpawner(); + ~XboxStructureActionPlaceSpawner(); + + virtual ConsoleGameRules::EGameRuleType getActionType() { return ConsoleGameRules::eGameRuleType_PlaceSpawner; } + + virtual void writeAttributes(DataOutputStream *dos, UINT numAttrs); + virtual void addAttribute(const wstring &attributeName, const wstring &attributeValue); + + bool placeSpawnerInLevel(StructurePiece *structure, Level *level, BoundingBox *chunkBB); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Leaderboards/LeaderboardManager.cpp b/Minecraft.Client/Common/Leaderboards/LeaderboardManager.cpp new file mode 100644 index 0000000..33707b1 --- /dev/null +++ b/Minecraft.Client/Common/Leaderboards/LeaderboardManager.cpp @@ -0,0 +1,106 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\StringHelpers.h" + +#include "LeaderboardManager.h" + +const wstring LeaderboardManager::filterNames[eNumFilterModes] = + { + L"Friends", L"MyScore", L"TopRank" + }; + +void LeaderboardManager::DeleteInstance() +{ + delete m_instance; + m_instance = NULL; +} + +LeaderboardManager::LeaderboardManager() +{ + zeroReadParameters(); + + m_myXUID = INVALID_XUID; +} + +void LeaderboardManager::zeroReadParameters() +{ + m_difficulty = -1; + m_statsType = eStatsType_UNDEFINED; + m_readListener = NULL; + m_startIndex = 0; + m_readCount = 0; + m_eFilterMode = eFM_UNDEFINED; +} + +bool LeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount) +{ + zeroReadParameters(); + + m_readListener = listener; + m_difficulty = difficulty; + m_statsType = type; + + m_eFilterMode = eFM_Friends; + return true; +} + +bool LeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount) +{ + zeroReadParameters(); + + m_readListener = listener; + m_difficulty = difficulty; + m_statsType = type; + + m_readCount = readCount; + + m_eFilterMode = eFM_MyScore; + return true; +} + +bool LeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount) +{ + zeroReadParameters(); + + m_readListener = listener; + m_difficulty = difficulty; + m_statsType = type; + + m_startIndex = startIndex; + m_readCount = readCount; + + m_eFilterMode = eFM_TopRank; + return true; +} + +#ifndef _XBOX +void LeaderboardManager::printStats(ReadView &view) +{ + app.DebugPrintf("[LeaderboardManager] Printing stats:\n" + "\tnumQueries=%i\n", view.m_numQueries); + + for (int i=0; i>29)&0x7) +#define GET_SLOTDISPLAY_ALPHA_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>24)&0x1F) +#define GET_SLOTDISPLAY_DECORATIONS_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x800000)?true:false) +//#define GET_SLOTDISPLAY_AUXVAL_FROM_DATA_BITMASK(uiBitmask) ((((unsigned long)uiBitmask)>>12)&0x7FF) +#define GET_SLOTDISPLAY_COUNT_FROM_DATA_BITMASK(uiBitmask) (((((unsigned int)uiBitmask)>>6)&0x3F)+1) +#define GET_SLOTDISPLAY_SCALE_FROM_DATA_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0x3F) +#define GET_SLOTDISPLAY_POPTIME_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>20)&0x7) + +// 16 bits for id (either item id or xzp icon id) +// 15 bits for aux value +// 1 bit for foil +#define MAKE_SLOTDISPLAY_ITEM_BITMASK(uiId,uiAuxValue,bFoil) ( (uiId & 0xFFFF) | ((uiAuxValue & 0x7FFF) << 16) | (bFoil?0x80000000:0) ) + +#define GET_SLOTDISPLAY_ID_FROM_ITEM_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0xFFFF) +#define GET_SLOTDISPLAY_AUXVAL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>16) & 0x7FFF) +#define GET_SLOTDISPLAY_FOIL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x80000000)?true:false) + + +// For encoding the players skin selection in their profile +// bDlcSkin = false is a players skin, bDlcSkin = true is a DLC skin +#define MAKE_SKIN_BITMASK(bDlcSkin, dwSkinId) ( (bDlcSkin?0x80000000:0) | (dwSkinId & 0x7FFFFFFF) ) +#define IS_SKIN_ID_IN_RANGE(dwSkinId) (dwSkinId <= 0x7FFFFFFF) + +#define GET_DLC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFFF) +#define GET_UGC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFE0) +#define GET_DEFAULT_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x0000001F) +#define GET_IS_DLC_SKIN_FROM_BITMASK(uiBitmask) ((((DWORD)uiBitmask)&0x80000000)?true:false) + diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp new file mode 100644 index 0000000..5110ae9 --- /dev/null +++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp @@ -0,0 +1,2006 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\Socket.h" +#include "..\..\..\Minecraft.World\ThreadName.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\ClientConnection.h" +#include "..\..\Minecraft.h" +#include "..\..\User.h" +#include "..\..\MinecraftServer.h" +#include "..\..\PlayerList.h" +#include "..\..\ServerPlayer.h" +#include "..\..\PlayerConnection.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" +#include "..\..\..\Minecraft.World\compression.h" +#include "..\..\..\Minecraft.World\OldChunkStorage.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" + +#include "..\..\Gui.h" +#include "..\..\LevelRenderer.h" +#include "..\..\..\Minecraft.World\IntCache.h" +#include "..\GameRules\ConsoleGameRules.h" +#include "GameNetworkManager.h" + +#ifdef _XBOX +#include "Common\XUI\XUI_PauseMenu.h" +#elif !(defined __PSVITA__) +#include "Common\UI\UI.h" +#include "Common\UI\UIScene_PauseMenu.h" +#include "..\..\Xbox\Network\NetworkPlayerXbox.h" +#endif + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +// Global instance +CGameNetworkManager g_NetworkManager; +CPlatformNetworkManager *CGameNetworkManager::s_pPlatformNetworkManager; + +__int64 CGameNetworkManager::messageQueue[512]; +__int64 CGameNetworkManager::byteQueue[512]; +int CGameNetworkManager::messageQueuePos = 0; + +CGameNetworkManager::CGameNetworkManager() +{ + m_bInitialised = false; + m_bLastDisconnectWasLostRoomOnly = false; + m_bFullSessionMessageOnNextSessionChange = false; + +#ifdef __ORBIS__ + m_pUpsell = NULL; + m_pInviteInfo = NULL; +#endif +} + +void CGameNetworkManager::Initialise() +{ + ServerStoppedCreate( false ); + ServerReadyCreate( false ); + int flagIndexSize = LevelRenderer::getGlobalChunkCount() / (Level::maxBuildHeight / 16); // dividing here by number of renderer chunks in one column +#ifdef _XBOX + s_pPlatformNetworkManager = new CPlatformNetworkManagerXbox(); +#elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + s_pPlatformNetworkManager = new CPlatformNetworkManagerSony(); +#elif defined _DURANGO + s_pPlatformNetworkManager = new CPlatformNetworkManagerDurango(); +#else + s_pPlatformNetworkManager = new CPlatformNetworkManagerStub(); +#endif + s_pPlatformNetworkManager->Initialise( this, flagIndexSize ); + m_bNetworkThreadRunning = false; + m_bInitialised = true; +} + +void CGameNetworkManager::Terminate() +{ + if( m_bInitialised ) + { + s_pPlatformNetworkManager->Terminate(); + } +} + +void CGameNetworkManager::DoWork() +{ +#ifdef _XBOX + // did we get any notifications from the game listener? + if(app.GetNotifications()->size()!=0) + { + PNOTIFICATION pNotification=app.GetNotifications()->back(); + + switch(pNotification->dwNotification) + { + case XN_LIVE_LINK_STATE_CHANGED: + { + int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad(); + bool bConnected = (pNotification->uiParam!=0)?true:false; + if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && bConnected == false && g_NetworkManager.IsInSession() ) + { + app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected); + } + } + break; + case XN_LIVE_INVITE_ACCEPTED: + s_pPlatformNetworkManager->Notify(pNotification->dwNotification,pNotification->uiParam); + break; + } + + app.GetNotifications()->pop_back(); + delete pNotification; + } +#endif + s_pPlatformNetworkManager->DoWork(); + +#ifdef __ORBIS__ + if (m_pUpsell != NULL && m_pUpsell->hasResponse()) + { + int iPad_invited = m_iPlayerInvited, iPad_checking = m_pUpsell->m_userIndex; + + m_iPlayerInvited = -1; + + delete m_pUpsell; + m_pUpsell = NULL; + + if (ProfileManager.HasPlayStationPlus(iPad_checking)) + { + this->GameInviteReceived(iPad_invited, m_pInviteInfo); + + // m_pInviteInfo deleted by GameInviteReceived. + m_pInviteInfo = NULL; + } + else + { + delete m_pInviteInfo; + m_pInviteInfo = NULL; + } + } +#endif +} + +bool CGameNetworkManager::_RunNetworkGame(LPVOID lpParameter) +{ + bool success = true; + + bool isHost = g_NetworkManager.IsHost(); + // Start the network game + Minecraft *pMinecraft=Minecraft::GetInstance(); + success = StartNetworkGame(pMinecraft,lpParameter); + + if(!success) return false; + + if( isHost ) + { + // We do not have a lobby, so the only players in the game at this point are local ones. + + success = s_pPlatformNetworkManager->_RunNetworkGame(); + if(!success) + { + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); + return true; + } + } + else + { + // Client needs QNET_STATE_GAME_PLAY so that IsInGameplay() returns true + s_pPlatformNetworkManager->SetGamePlayState(); + } + + if( g_NetworkManager.IsLeavingGame() ) return false; + + app.SetGameStarted(true); + + // 4J-PB - if this is the trial game, start the trial timer + if(!ProfileManager.IsFullVersion()) + { + ui.SetTrialTimerLimitSecs(MinecraftDynamicConfigurations::GetTrialTime()); + app.SetTrialTimerStart(); + } + //app.CloseXuiScenes(ProfileManager.GetPrimaryPad()); + + return success; +} + +bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParameter) +{ +#ifdef _DURANGO + ProfileManager.SetDeferredSignoutEnabled(true); +#endif + + __int64 seed = 0; + if(lpParameter != NULL) + { + NetworkGameInitData *param = (NetworkGameInitData *)lpParameter; + seed = param->seed; + + app.setLevelGenerationOptions(param->levelGen); + if(param->levelGen != NULL) + { + if(app.getLevelGenerationOptions() == NULL) + { + app.DebugPrintf("Game rule was not loaded, and seed is required. Exiting.\n"); + return false; + } + else + { + param->seed = seed = app.getLevelGenerationOptions()->getLevelSeed(); + } + } + } + + static __int64 sseed = seed; // Create static version so this will be valid until next call to this function & whilst thread is running + ServerStoppedCreate(false); + if( g_NetworkManager.IsHost() ) + { + ServerStoppedCreate(true); + ServerReadyCreate(true); + // Ready to go - create actual networking thread & start hosting + C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, lpParameter, "Server", 256 * 1024); +#if defined __PS3__ || defined __PSVITA__ + thread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); +#endif //__PS3__ + + thread->SetProcessor(CPU_CORE_SERVER); + thread->Run(); + + ServerReadyWait(); + ServerReadyDestroy(); + + if( MinecraftServer::serverHalted() ) + return false; + +// printf("Server ready to go!\n"); + } + else + { + Socket::Initialise(NULL); + } + +#ifndef _XBOX + Minecraft *pMinecraft = Minecraft::GetInstance(); + // Make sure that we have transitioned through any joining/creating stages and are actually playing the game, so that we know the players should be valid + bool changedMessage = false; + while(!IsReadyToPlayOrIdle()) + { + changedMessage = true; + pMinecraft->progressRenderer->progressStage( g_NetworkManager.CorrectErrorIDS(IDS_PROGRESS_SAVING_TO_DISC) ); // "Finalizing..." vaguest message I could find + pMinecraft->progressRenderer->progressStagePercentage( g_NetworkManager.GetJoiningReadyPercentage() ); + Sleep(10); + } + if( changedMessage ) + { + pMinecraft->progressRenderer->progressStagePercentage( 100 ); + } +#endif + + // If we aren't in session, then something bad must have happened - we aren't joining, creating or ready play + if(!IsInSession() ) + { + MinecraftServer::HaltServer(); + return false; + } + + // 4J Stu - Wait a while to make sure that DLC is loaded. This is the last point before the network communication starts + // so the latest we can check this + while( !app.DLCInstallProcessCompleted() && app.DLCInstallPending() && !g_NetworkManager.IsLeavingGame() ) + { + Sleep( 10 ); + } + if( g_NetworkManager.IsLeavingGame() ) + { + MinecraftServer::HaltServer(); + return false; + } + + // PRIMARY PLAYER + + vector createdConnections; + ClientConnection *connection; + + if( g_NetworkManager.IsHost() ) + { + connection = new ClientConnection(minecraft, NULL); + } + else + { + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(ProfileManager.GetLockedProfile()); + if(pNetworkPlayer == NULL) + { + MinecraftServer::HaltServer(); + app.DebugPrintf("%d\n",ProfileManager.GetLockedProfile()); + // If the player is NULL here then something went wrong in the session setup, and continuing will end up in a crash + return false; + } + + Socket *socket = pNetworkPlayer->GetSocket(); + + // Fix for #13259 - CRASH: Gameplay: loading process is halted when player loads saved data + if(socket == NULL) + { + assert(false); + MinecraftServer::HaltServer(); + // If the socket is NULL here then something went wrong in the session setup, and continuing will end up in a crash + return false; + } + + connection = new ClientConnection(minecraft, socket); + } + + if( !connection->createdOk ) + { + assert(false); + delete connection; + connection = NULL; + MinecraftServer::HaltServer(); + return false; + } + + connection->send( shared_ptr( new PreLoginPacket(minecraft->user->name) ) ); + + // Tick connection until we're ready to go. The stages involved in this are: + // (1) Creating the ClientConnection sends a prelogin packet to the server + // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet + // (3) the server sends a login back, which is handled by the client connection to start the game + if( !g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING); + } + else + { + // 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting + INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId(); + TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId); + } + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + do + { + app.DebugPrintf("ticking connection A\n"); + connection->tick(); + + // 4J Stu - We were ticking this way too fast which could cause the connection to time out + // The connections should tick at 20 per second + Sleep(50); + } while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ); + ui.CleanUpSkinReload(); + + // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash + // We need to break out of the above loop if m_bLeavingGame is set, and close the connection + if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) + { + connection->close(); + } + + if( connection->isStarted() && !connection->isClosed() ) + { + createdConnections.push_back( connection ); + + int primaryPad = ProfileManager.GetPrimaryPad(); + app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK); + if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there + { + if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + else + { + if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + } + + + // ALL OTHER LOCAL PLAYERS + for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + // Already have setup the primary pad + if(idx == ProfileManager.GetPrimaryPad() ) continue; + + if( GetLocalPlayerByUserIndex(idx) != NULL && !ProfileManager.IsSignedIn(idx) ) + { + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); + Socket *socket = pNetworkPlayer->GetSocket(); + app.DebugPrintf("Closing socket due to player %d not being signed in any more\n"); + if( !socket->close(false) ) socket->close(true); + + continue; + } + + // By default when we host we only have the local player, but currently allow multiple local players to join + // when joining any other way, so just because they are signed in doesn't mean they are in the session + // 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); + if( pNetworkPlayer == NULL ) + continue; + + ClientConnection *connection; + + Socket *socket = pNetworkPlayer->GetSocket(); + connection = new ClientConnection(minecraft, socket, idx); + + minecraft->addPendingLocalConnection(idx, connection); + //minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection); + + // Open the socket on the server end to accept incoming data + Socket::addIncomingSocket(socket); + + connection->send( shared_ptr( new PreLoginPacket(convStringToWstring( ProfileManager.GetGamertag(idx) )) ) ); + + createdConnections.push_back( connection ); + + // Tick connection until we're ready to go. The stages involved in this are: + // (1) Creating the ClientConnection sends a prelogin packet to the server + // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet + // (3) the server sends a login back, which is handled by the client connection to start the game + do + { + // We need to keep ticking the connections for players that already logged in + for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it) + { + (*it)->tick(); + } + + // 4J Stu - We were ticking this way too fast which could cause the connection to time out + // The connections should tick at 20 per second + Sleep(50); + app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame()); +#if defined _XBOX || __PS3__ + } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() ); +#else + // TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour + } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() ); +#endif + + // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash + // We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections + // The connections in the createdConnections vector get closed at the end of the thread + if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break; + + if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() ) + { + app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK); + if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + else + { + connection->close(); + AUTO_VAR(it, find( createdConnections.begin(), createdConnections.end(), connection )); + if(it != createdConnections.end() ) createdConnections.erase( it ); + } + } + + app.SetGameMode( eMode_Multiplayer ); + } + else if ( connection->isClosed() || !IsInSession()) + { +// assert(false); + MinecraftServer::HaltServer(); + return false; + } + + + if(g_NetworkManager.IsLeavingGame() || !IsInSession() ) + { + for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it) + { + (*it)->close(); + } +// assert(false); + MinecraftServer::HaltServer(); + return false; + } + + // Catch in-case server has been halted (by a player signout). + if ( MinecraftServer::serverHalted() ) + return false; + + return true; +} + +int CGameNetworkManager::CorrectErrorIDS(int IDS) +{ + return s_pPlatformNetworkManager->CorrectErrorIDS(IDS); +} + +int CGameNetworkManager::GetLocalPlayerMask(int playerIndex) +{ + return s_pPlatformNetworkManager->GetLocalPlayerMask( playerIndex ); +} + +int CGameNetworkManager::GetPlayerCount() +{ + return s_pPlatformNetworkManager->GetPlayerCount(); +} + +int CGameNetworkManager::GetOnlinePlayerCount() +{ + return s_pPlatformNetworkManager->GetOnlinePlayerCount(); +} + +bool CGameNetworkManager::AddLocalPlayerByUserIndex( int userIndex ) +{ + return s_pPlatformNetworkManager->AddLocalPlayerByUserIndex( userIndex ); +} + +bool CGameNetworkManager::RemoveLocalPlayerByUserIndex( int userIndex ) +{ + return s_pPlatformNetworkManager->RemoveLocalPlayerByUserIndex( userIndex ); +} + +INetworkPlayer *CGameNetworkManager::GetLocalPlayerByUserIndex(int userIndex ) +{ + return s_pPlatformNetworkManager->GetLocalPlayerByUserIndex( userIndex ); +} + +INetworkPlayer *CGameNetworkManager::GetPlayerByIndex(int playerIndex) +{ + return s_pPlatformNetworkManager->GetPlayerByIndex( playerIndex ); +} + +INetworkPlayer *CGameNetworkManager::GetPlayerByXuid(PlayerUID xuid) +{ + return s_pPlatformNetworkManager->GetPlayerByXuid( xuid ); +} + +INetworkPlayer *CGameNetworkManager::GetPlayerBySmallId(unsigned char smallId) +{ + return s_pPlatformNetworkManager->GetPlayerBySmallId( smallId ); +} + +#ifdef _DURANGO +wstring CGameNetworkManager::GetDisplayNameByGamertag(wstring gamertag) +{ + return s_pPlatformNetworkManager->GetDisplayNameByGamertag(gamertag); +} +#endif + +INetworkPlayer *CGameNetworkManager::GetHostPlayer() +{ + return s_pPlatformNetworkManager->GetHostPlayer(); +} + +void CGameNetworkManager::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + s_pPlatformNetworkManager->RegisterPlayerChangedCallback( iPad, callback, callbackParam ); +} + +void CGameNetworkManager::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + s_pPlatformNetworkManager->UnRegisterPlayerChangedCallback( iPad, callback, callbackParam ); +} + +void CGameNetworkManager::HandleSignInChange() +{ + s_pPlatformNetworkManager->HandleSignInChange(); +} + +bool CGameNetworkManager::ShouldMessageForFullSession() +{ + return s_pPlatformNetworkManager->ShouldMessageForFullSession(); +} + +bool CGameNetworkManager::IsInSession() +{ + return s_pPlatformNetworkManager->IsInSession(); +} + +bool CGameNetworkManager::IsInGameplay() +{ + return s_pPlatformNetworkManager->IsInGameplay(); +} + +bool CGameNetworkManager::IsReadyToPlayOrIdle() +{ + return s_pPlatformNetworkManager->IsReadyToPlayOrIdle(); +} + +bool CGameNetworkManager::IsLeavingGame() +{ + return s_pPlatformNetworkManager->IsLeavingGame(); +} + +bool CGameNetworkManager::SetLocalGame(bool isLocal) +{ + return s_pPlatformNetworkManager->SetLocalGame( isLocal ); +} + +bool CGameNetworkManager::IsLocalGame() +{ + return s_pPlatformNetworkManager->IsLocalGame(); +} + +void CGameNetworkManager::SetPrivateGame(bool isPrivate) +{ + s_pPlatformNetworkManager->SetPrivateGame( isPrivate ); +} + +bool CGameNetworkManager::IsPrivateGame() +{ + return s_pPlatformNetworkManager->IsPrivateGame(); +} + +void CGameNetworkManager::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots, unsigned char privateSlots) +{ + // 4J Stu - clear any previous connection errors + Minecraft::GetInstance()->clearConnectionFailed(); + + s_pPlatformNetworkManager->HostGame( localUsersMask, bOnlineGame, bIsPrivate, publicSlots, privateSlots ); +} + +bool CGameNetworkManager::IsHost() +{ + return (s_pPlatformNetworkManager->IsHost() == TRUE); +} + +bool CGameNetworkManager::IsInStatsEnabledSession() +{ + return s_pPlatformNetworkManager->IsInStatsEnabledSession(); +} + +bool CGameNetworkManager::SessionHasSpace(unsigned int spaceRequired) +{ + return s_pPlatformNetworkManager->SessionHasSpace( spaceRequired ); +} + +vector *CGameNetworkManager::GetSessionList(int iPad, int localPlayers, bool partyOnly) +{ + return s_pPlatformNetworkManager->GetSessionList( iPad, localPlayers, partyOnly ); +} + +bool CGameNetworkManager::GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession) +{ + return s_pPlatformNetworkManager->GetGameSessionInfo( iPad, sessionId, foundSession ); +} + +void CGameNetworkManager::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) +{ + s_pPlatformNetworkManager->SetSessionsUpdatedCallback( SessionsUpdatedCallback, pSearchParam ); +} + +void CGameNetworkManager::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) +{ + s_pPlatformNetworkManager->GetFullFriendSessionInfo(foundSession, FriendSessionUpdatedFn, pParam); +} + +void CGameNetworkManager::ForceFriendsSessionRefresh() +{ + s_pPlatformNetworkManager->ForceFriendsSessionRefresh(); +} + +bool CGameNetworkManager::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo) +{ + return s_pPlatformNetworkManager->JoinGameFromInviteInfo( userIndex, userMask, pInviteInfo ); +} + +CGameNetworkManager::eJoinGameResult CGameNetworkManager::JoinGame(FriendSessionInfo *searchResult, int localUsersMask) +{ + app.SetTutorialMode( false ); + g_NetworkManager.SetLocalGame(false); + + int primaryUserIndex = ProfileManager.GetLockedProfile(); + + // 4J-PB - clear any previous connection errors + Minecraft::GetInstance()->clearConnectionFailed(); + + // Make sure that the Primary Pad is in by default + localUsersMask |= GetLocalPlayerMask( ProfileManager.GetPrimaryPad() ); + + return (eJoinGameResult)(s_pPlatformNetworkManager->JoinGame( searchResult, localUsersMask, primaryUserIndex )); +} + +void CGameNetworkManager::CancelJoinGame(LPVOID lpParam) +{ +#ifdef _XBOX_ONE + s_pPlatformNetworkManager->CancelJoinGame(); +#endif +} + +bool CGameNetworkManager::LeaveGame(bool bMigrateHost) +{ + Minecraft::GetInstance()->gui->clearMessages(); + return s_pPlatformNetworkManager->LeaveGame( bMigrateHost ); +} + +int CGameNetworkManager::JoinFromInvite_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + INVITE_INFO * pInviteInfo = (INVITE_INFO *)pParam; + + if(bContinue==true) + { +#ifdef __ORBIS__ + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + + return 0; + } +#endif + + app.DebugPrintf("JoinFromInvite_SignInReturned, iPad %d\n",iPad); + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad) && ProfileManager.IsSignedInLive(iPad) ) + { + app.DebugPrintf("JoinFromInvite_SignInReturned, passed sign-in tests\n"); + int localUsersMask = 0; + int joiningUsers = 0; + + bool noPrivileges = false; + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + ++joiningUsers; + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + localUsersMask |= GetLocalPlayerMask( index ); + } + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(iPad,false,&noUGC,NULL,NULL); +#elif defined(__ORBIS__) + ProfileManager.GetChatAndContentRestrictions(iPad,false,NULL,&noUGC,NULL); +#endif + + if(noUGC) + { + int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + if(joiningUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + + ui.RequestUGCMessageBox(IDS_CONNECTION_FAILED, messageText); + } + else if(noPrivileges) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { +#if defined(__ORBIS__) || defined(__PSVITA__) + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(iPad,false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( 0, ProfileManager.GetPrimaryPad() ); + } +#endif + ProfileManager.SetLockedProfile(iPad); + ProfileManager.SetPrimaryPad(iPad); + + g_NetworkManager.SetLocalGame(false); + + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + + // 4J-PB - clear any previous connection errors + Minecraft::GetInstance()->clearConnectionFailed(); + + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + bool success = g_NetworkManager.JoinGameFromInviteInfo( + iPad, // dwUserIndex + localUsersMask, // dwUserMask + pInviteInfo ); // pInviteInfo + if( !success ) + { + app.DebugPrintf( "Failed joining game from invite\n" ); + } + } + } + else + { + app.DebugPrintf("JoinFromInvite_SignInReturned, failed sign-in tests :%d %d\n",ProfileManager.IsSignedIn(iPad),ProfileManager.IsSignedInLive(iPad)); + } + } + return 0; + +} + +void CGameNetworkManager::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + TexturePack *tPack = pMinecraft->skins->getSelected(); + s_pPlatformNetworkManager->SetSessionTexturePackParentId( tPack->getDLCParentPackId() ); + s_pPlatformNetworkManager->SetSessionSubTexturePackId( tPack->getDLCSubPackId() ); + + s_pPlatformNetworkManager->UpdateAndSetGameSessionData( pNetworkPlayerLeaving ); +} + +void CGameNetworkManager::SendInviteGUI(int quadrant) +{ + s_pPlatformNetworkManager->SendInviteGUI(quadrant); +} + +void CGameNetworkManager::ResetLeavingGame() +{ + s_pPlatformNetworkManager->ResetLeavingGame(); +} + +bool CGameNetworkManager::IsNetworkThreadRunning() +{ + return m_bNetworkThreadRunning;; +} + +int CGameNetworkManager::RunNetworkGameThreadProc( void* lpParameter ) +{ + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + Tile::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + + g_NetworkManager.m_bNetworkThreadRunning = true; + bool success = g_NetworkManager._RunNetworkGame(lpParameter); + g_NetworkManager.m_bNetworkThreadRunning = false; + if( !success) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + while ( tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ) + { + Sleep(1); + } + ui.CleanUpSkinReload(); + if(app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None) + { + app.SetDisconnectReason( DisconnectPacket::eDisconnect_ConnectionCreationFailed ); + } + // If we failed before the server started, clear the game rules. Otherwise the server will clear it up. + if(MinecraftServer::getInstance() == NULL) app.m_gameRules.unloadCurrentGameRules(); + Tile::ReleaseThreadStorage(); + return -1; + } + +#ifdef __PSVITA__ + // 4J-JEV: Wait for the loading/saving to finish. + while (StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle) Sleep(10); +#endif + + Tile::ReleaseThreadStorage(); + IntCache::ReleaseThreadStorage(); + return 0; +} + +int CGameNetworkManager::ServerThreadProc( void* lpParameter ) +{ + __int64 seed = 0; + if(lpParameter != NULL) + { + NetworkGameInitData *param = (NetworkGameInitData *)lpParameter; + seed = param->seed; + app.SetGameHostOption(eGameHostOption_All,param->settings); + } + + SetThreadName(-1, "Minecraft Server thread"); + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::UseDefaultThreadStorage(); + OldChunkStorage::UseDefaultThreadStorage(); + Entity::useSmallIds(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + MinecraftServer::main(seed, lpParameter); //saveData, app.GetGameHostOption(eGameHostOption_All)); + + Tile::ReleaseThreadStorage(); + AABB::ReleaseThreadStorage(); + Vec3::ReleaseThreadStorage(); + IntCache::ReleaseThreadStorage(); + Level::destroyLightingCache(); + + if(lpParameter != NULL) delete lpParameter; + + return S_OK; +} + +int CGameNetworkManager::ExitAndJoinFromInviteThreadProc( void* lpParam ) +{ + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + //app.SetGameStarted(false); + +#ifndef __PSVITA__ + UIScene_PauseMenu::_ExitWorld(NULL); +#endif + + while( g_NetworkManager.IsInSession() ) + { + Sleep(1); + } + + // Xbox should always be online when receiving invites - on PS3 we need to check & ask the user to sign in +#ifndef __PS3__ + JoinFromInviteData *inviteData = (JoinFromInviteData *)lpParam; + app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, lpParam); +#else + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + JoinFromInviteData *inviteData = (JoinFromInviteData *)lpParam; + app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, lpParam); + } + else + { + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CGameNetworkManager::MustSignInReturned_0,lpParam, app.GetStringTable()); + } +#endif + + return S_OK; +} + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +// This case happens when we have been returned from the game to the main menu after receiving an invite and are now trying to go back in to join the new game +// The pair of methods MustSignInReturned_0 & PSNSignInReturned_0 handle this +int CGameNetworkManager::MustSignInReturned_0(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + SQRNetworkManager_PS3::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true); +#elif defined __ORBIS__ + SQRNetworkManager_Orbis::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_0, pParam,true); +#endif + } + else + { + app.SetAction(0,eAppAction_Idle); + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + } + + return 0; +} + +int CGameNetworkManager::PSNSignInReturned_0(void* pParam, bool bContinue, int iPad) +{ + JoinFromInviteData *inviteData = (JoinFromInviteData *)pParam; + + // If the invite data isn't set up yet (indicated by it being all zeroes, easiest detected via the net version), then try and get it again... this can happen if we got + // the invite whilst signed out + + if( bContinue ) + { + if(inviteData->pInviteInfo->netVersion == 0) + { +#if defined __PS3__ || defined __VITA__ + if(!SQRNetworkManager_PS3::UpdateInviteData((SQRNetworkManager::PresenceSyncInfo *)inviteData->pInviteInfo)) + { + bContinue = false; + } +#elif defined __ORBIS__ + // TODO: No Orbis equivalent (should there be?) +#endif + } + } + + if( bContinue ) + { + app.SetAction(inviteData->dwUserIndex, eAppAction_JoinFromInvite, pParam); + } + else + { + app.SetAction(inviteData->dwUserIndex,eAppAction_Idle); + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + } + + return 0; +} + +// This case happens when we were in the main menus when we got an invite, and weren't signed in... now can proceed with the normal flow of code for this situation +// The pair of methods MustSignInReturned_1 & PSNSignInReturned_1 handle this +int CGameNetworkManager::MustSignInReturned_1(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + SQRNetworkManager_PS3::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true); +#elif defined __ORBIS__ + SQRNetworkManager_Orbis::AttemptPSNSignIn(&CGameNetworkManager::PSNSignInReturned_1, pParam,true); +#endif + } + return 0; +} + +int CGameNetworkManager::PSNSignInReturned_1(void* pParam, bool bContinue, int iPad) +{ + INVITE_INFO *inviteInfo = (INVITE_INFO *)pParam; + + // If the invite data isn't set up yet (indicated by it being all zeroes, easiest detected via the net version), then try and get it again... this can happen if we got + // the invite whilst signed out + + if( bContinue ) + { + if(inviteInfo->netVersion == 0) + { +#if defined __PS3__ || defined __VITA__ + if(!SQRNetworkManager_PS3::UpdateInviteData((SQRNetworkManager::PresenceSyncInfo *)inviteInfo)) + { + bContinue = false; + } +#elif defined __ORBIS__ + // TODO: No Orbis equivalent (should there be?) +#endif + + } + } + + if( bContinue ) + { + g_NetworkManager.HandleInviteWhenInMenus(0, inviteInfo); + } + + return 0; +} +#endif + +void CGameNetworkManager::_LeaveGame() +{ + s_pPlatformNetworkManager->_LeaveGame(false, true); +} + +int CGameNetworkManager::ChangeSessionTypeThreadProc( void* lpParam ) +{ + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + MinecraftServer *pServer = MinecraftServer::getInstance(); + +#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + if( g_NetworkManager.m_bLastDisconnectWasLostRoomOnly ) + { + if(g_NetworkManager.m_bSignedOutofPSN) + { + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, IDS_ERROR_PSN_SIGN_OUT, uiIDA,1,ProfileManager.GetPrimaryPad()); + } + else + { + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, uiIDA,1,ProfileManager.GetPrimaryPad()); + } + } + else + { + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CONNECTION_LOST, g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT), uiIDA,1,ProfileManager.GetPrimaryPad()); + } + + // Swap these two messages around as one is too long to display at 480 + pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + pMinecraft->progressRenderer->progressStage( -1 ); //g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); +#elif defined(_XBOX_ONE) + if( g_NetworkManager.m_bFullSessionMessageOnNextSessionChange ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME, IDS_IN_PARTY_SESSION_FULL, uiIDA,1,ProfileManager.GetPrimaryPad()); + pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + pMinecraft->progressRenderer->progressStage( -1 ); + } + else + { + pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); + pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + } + +#else + pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); + pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); +#endif + + while( app.GetXuiServerAction(ProfileManager.GetPrimaryPad() ) != eXuiServerAction_Idle && !MinecraftServer::serverHalted() ) + { + Sleep(10); + } + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); + + // wait for the server to be in a non-ticking state + pServer->m_serverPausedEvent->WaitForSignal(INFINITE); + +#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ + // Swap these two messages around as one is too long to display at 480 + pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + pMinecraft->progressRenderer->progressStage( -1 ); //g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); +#elif defined(_XBOX_ONE) + if( g_NetworkManager.m_bFullSessionMessageOnNextSessionChange ) + { + pMinecraft->progressRenderer->progressStartNoAbort( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + pMinecraft->progressRenderer->progressStage( -1 ); + } + else + { + pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); + pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); + } +#else + pMinecraft->progressRenderer->progressStartNoAbort( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT) ); + pMinecraft->progressRenderer->progressStage( IDS_PROGRESS_CONVERTING_TO_OFFLINE_GAME ); +#endif + + pMinecraft->progressRenderer->progressStagePercentage(25); + +#ifdef _XBOX_ONE + // wait for any players that were being added, to finish doing this. On XB1, if we don't do this then there's an async thread running doing this, + // which could then finish at any inappropriate time later + while( s_pPlatformNetworkManager->IsAddingPlayer() ) + { + Sleep(1); + } +#endif + + // Null the network player of all the server players that are local, to stop them being removed from the server when removed from the session + if( pServer != NULL ) + { + PlayerList *players = pServer->getPlayers(); + for(AUTO_VAR(it, players->players.begin()); it < players->players.end(); ++it) + { + shared_ptr servPlayer = *it; + if( servPlayer->connection->isLocal() && !servPlayer->connection->isGuest() ) + { + servPlayer->connection->connection->getSocket()->setPlayer(NULL); + } + } + } + + // delete the current session - if we weren't actually disconnected fully from the network but have just lost our room, then pass a bLeaveRoom flag of false + // here as by definition we don't need to leave the room (again). This is currently only an issue for sony platforms. + if( g_NetworkManager.m_bLastDisconnectWasLostRoomOnly ) + { + s_pPlatformNetworkManager->_LeaveGame(false, false); + } + else + { + s_pPlatformNetworkManager->_LeaveGame(false, true); + } + + // wait for the current session to end + while( g_NetworkManager.IsInSession() ) + { + Sleep(1); + } + + // Reset this flag as the we don't need to know that we only lost the room only from this point onwards, the behaviour is exactly the same + g_NetworkManager.m_bLastDisconnectWasLostRoomOnly = false; + g_NetworkManager.m_bFullSessionMessageOnNextSessionChange = false; + + pMinecraft->progressRenderer->progressStagePercentage(50); + + // Defaulting to making this a local game + g_NetworkManager.SetLocalGame(true); + + // Create a new session with all the players that were in the old one + int localUsersMask = 0; + char numLocalPlayers = 0; + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) && pMinecraft->localplayers[index] != NULL ) + { + numLocalPlayers++; + localUsersMask |= GetLocalPlayerMask(index); + } + } + + s_pPlatformNetworkManager->_HostGame( localUsersMask ); + + pMinecraft->progressRenderer->progressStagePercentage(75); + + // Wait for all the local players to rejoin the session + while( g_NetworkManager.GetPlayerCount() < numLocalPlayers ) + { + Sleep(1); + } + + // Restore the network player of all the server players that are local + if( pServer != NULL ) + { + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) && pMinecraft->localplayers[index] != NULL ) + { + PlayerUID localPlayerXuid = pMinecraft->localplayers[index]->getXuid(); + + PlayerList *players = pServer->getPlayers(); + for(AUTO_VAR(it, players->players.begin()); it < players->players.end(); ++it) + { + shared_ptr servPlayer = *it; + if( servPlayer->getXuid() == localPlayerXuid ) + { + servPlayer->connection->connection->getSocket()->setPlayer( g_NetworkManager.GetLocalPlayerByUserIndex(index) ); + } + } + + // Player might have a pending connection + if (pMinecraft->m_pendingLocalConnections[index] != NULL) + { + // Update the network player + pMinecraft->m_pendingLocalConnections[index]->getConnection()->getSocket()->setPlayer(g_NetworkManager.GetLocalPlayerByUserIndex(index)); + } + } + } + } + + pMinecraft->progressRenderer->progressStagePercentage(100); + +#ifndef _XBOX + // Make sure that we have transitioned through any joining/creating stages so we're actually ready to set to play + while(!s_pPlatformNetworkManager->IsReadyToPlayOrIdle()) + { + Sleep(10); + } +#endif + + s_pPlatformNetworkManager->_StartGame(); + +#ifndef _XBOX + // Wait until the message box has been closed + while(ui.IsSceneInStack(XUSER_INDEX_ANY, eUIScene_MessageBox)) + { + Sleep(10); + } +#endif + + // Start the game again + app.SetGameStarted(true); + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + app.SetChangingSessionType(false); + app.SetReallyChangingSessionType(false); + + return S_OK; + +} + +void CGameNetworkManager::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index) +{ + s_pPlatformNetworkManager->SystemFlagSet( pNetworkPlayer, index ); +} + +bool CGameNetworkManager::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index) +{ + return s_pPlatformNetworkManager->SystemFlagGet( pNetworkPlayer, index ); +} + +wstring CGameNetworkManager::GatherStats() +{ + return s_pPlatformNetworkManager->GatherStats(); +} + +void CGameNetworkManager::renderQueueMeter() +{ +#ifdef _XBOX + int height = 720; + + CGameNetworkManager::byteQueue[(CGameNetworkManager::messageQueuePos) & (CGameNetworkManager::messageQueue_length - 1)] = GetHostPlayer()->GetSendQueueSizeBytes(NULL, false); + CGameNetworkManager::messageQueue[(CGameNetworkManager::messageQueuePos++) & (CGameNetworkManager::messageQueue_length - 1)] = GetHostPlayer()->GetSendQueueSizeMessages(NULL, false); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->gui->renderGraph(CGameNetworkManager::messageQueue_length, CGameNetworkManager::messageQueuePos, CGameNetworkManager::messageQueue, 10, 1000, CGameNetworkManager::byteQueue, 100, 25000); +#endif +} + +wstring CGameNetworkManager::GatherRTTStats() +{ + return s_pPlatformNetworkManager->GatherRTTStats(); +} + +void CGameNetworkManager::StateChange_AnyToHosting() +{ + app.DebugPrintf("Disabling Guest Signin\n"); + XEnableGuestSignin(FALSE); + Minecraft::GetInstance()->clearPendingClientTextureRequests(); +} + +void CGameNetworkManager::StateChange_AnyToJoining() +{ + app.DebugPrintf("Disabling Guest Signin\n"); + XEnableGuestSignin(FALSE); + Minecraft::GetInstance()->clearPendingClientTextureRequests(); + + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = -1; + param->showTooltips = false; + param->setFailTimer = true; + param->timerTime = CONNECTING_PROGRESS_CHECK_TIME; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); +} + +void CGameNetworkManager::StateChange_JoiningToIdle(CPlatformNetworkManager::eJoinFailedReason reason) +{ + DisconnectPacket::eDisconnectReason disconnectReason; + switch(reason) + { + case CPlatformNetworkManager::JOIN_FAILED_SERVER_FULL: + disconnectReason = DisconnectPacket::eDisconnect_ServerFull; + break; + case CPlatformNetworkManager::JOIN_FAILED_INSUFFICIENT_PRIVILEGES: + disconnectReason = DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin; + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_FailedToJoinNoPrivileges); + break; + default: + disconnectReason = DisconnectPacket::eDisconnect_ConnectionCreationFailed; + break; + }; + Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), disconnectReason); +} + +void CGameNetworkManager::StateChange_AnyToStarting() +{ +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + app.getRemoteStorage()->shutdown(); // shut the remote storage lib down and hopefully get our 7mb back +#endif + + if(!g_NetworkManager.IsHost()) + { + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = NULL; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = ProfileManager.GetPrimaryPad(); + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } +} + +void CGameNetworkManager::StateChange_AnyToEnding(bool bStateWasPlaying) +{ + // Kick off a stats write for players that are signed into LIVE, if this is a local game + if( bStateWasPlaying && g_NetworkManager.IsLocalGame() ) + { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(i); + if(pNetworkPlayer != NULL && ProfileManager.IsSignedIn( i ) ) + { + app.DebugPrintf("Stats save for an offline game for the player at index %d\n", i ); + Minecraft::GetInstance()->forceStatsSave(pNetworkPlayer->GetUserIndex()); + } + } + } + + Minecraft::GetInstance()->gui->clearMessages(); + + if(!g_NetworkManager.IsHost() && !g_NetworkManager.IsLeavingGame() ) + { + // 4J Stu - If the host is saving then it might take a while to quite the session, so do it ourself + //m_bLeavingGame = true; + + // The host has notified that the game is about to end + if(app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None) app.SetDisconnectReason( DisconnectPacket::eDisconnect_Quitting ); + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); + } +} + +void CGameNetworkManager::StateChange_AnyToIdle() +{ + app.DebugPrintf("Enabling Guest Signin\n"); + XEnableGuestSignin(TRUE); + // Reset this here so that we can search for games again + // 4J Stu - If we are changing session type there is a race between that thread setting the game to local, and this setting it to not local + if(!app.GetChangingSessionType()) g_NetworkManager.SetLocalGame( false ); + +} + +void CGameNetworkManager::CreateSocket( INetworkPlayer *pNetworkPlayer, bool localPlayer ) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + Socket *socket = NULL; + int userIdx = pNetworkPlayer->GetUserIndex(); + shared_ptr mpPlayer = (userIdx >= 0 && userIdx < XUSER_MAX_COUNT) ? pMinecraft->localplayers[userIdx] : nullptr; + if( localPlayer && mpPlayer != NULL && mpPlayer->connection != NULL) + { + // If we already have a MultiplayerLocalPlayer here then we are doing a session type change + socket = mpPlayer->connection->getSocket(); + + // Pair this socket and network player + pNetworkPlayer->SetSocket( socket); + if( socket ) + { + socket->setPlayer( pNetworkPlayer ); + } + } + else + { + socket = new Socket( pNetworkPlayer, g_NetworkManager.IsHost(), g_NetworkManager.IsHost() && localPlayer ); + pNetworkPlayer->SetSocket( socket ); + + // 4J Stu - May be other states we want to accept aswell + // Add this user to the game server if the game is started already + if( g_NetworkManager.IsHost() && g_NetworkManager.IsInGameplay() ) + { + Socket::addIncomingSocket(socket); + } + + // If this is a local player and we are already in the game, we need to setup a local connection and log + // the player in to the game server + if( localPlayer && g_NetworkManager.IsInGameplay() ) + { + int idx = pNetworkPlayer->GetUserIndex(); + app.DebugPrintf("Creating new client connection for idx: %d\n", idx); + + ClientConnection *connection; + connection = new ClientConnection(pMinecraft, socket, idx); + + if( connection->createdOk ) + { + connection->send( shared_ptr( new PreLoginPacket( pNetworkPlayer->GetOnlineName() ) ) ); + pMinecraft->addPendingLocalConnection(idx, connection); + } + else + { + pMinecraft->connectionDisconnected( idx , DisconnectPacket::eDisconnect_ConnectionCreationFailed ); + delete connection; + connection = NULL; + } + } + } + +} + +void CGameNetworkManager::CloseConnection( INetworkPlayer *pNetworkPlayer ) +{ + MinecraftServer *server = MinecraftServer::getInstance(); + if( server != NULL ) + { + PlayerList *players = server->getPlayers(); + if( players != NULL ) + { + players->closePlayerConnectionBySmallId(pNetworkPlayer->GetSmallId()); + } + } +} + +void CGameNetworkManager::PlayerJoining( INetworkPlayer *pNetworkPlayer ) +{ + if (g_NetworkManager.IsInGameplay()) // 4J-JEV: Wait to do this at StartNetworkGame if not in-game yet. + { + // 4J-JEV: Update RichPresence when a player joins the game. + bool multiplayer = g_NetworkManager.GetPlayerCount() > 1, localgame = g_NetworkManager.IsLocalGame(); + for (int iPad=0; iPadIsLocal() ) + { + TelemetryManager->RecordPlayerSessionStart(pNetworkPlayer->GetUserIndex()); + } +#ifdef _XBOX + else + { + if( !pNetworkPlayer->IsHost() ) + { + for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(Minecraft::GetInstance()->localplayers[idx] != NULL) + { + TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount()); + } + } + } + } +#endif +} + +void CGameNetworkManager::PlayerLeaving( INetworkPlayer *pNetworkPlayer ) +{ + if( pNetworkPlayer->IsLocal() ) + { + ProfileManager.SetCurrentGameActivity(pNetworkPlayer->GetUserIndex(),CONTEXT_PRESENCE_IDLE,false); + + TelemetryManager->RecordPlayerSessionExit(pNetworkPlayer->GetUserIndex(), app.GetDisconnectReason()); + } +#ifdef _XBOX + else + { + for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(Minecraft::GetInstance()->localplayers[idx] != NULL) + { + TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, Minecraft::GetInstance()->level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount()); + } + } + } +#endif +} + +void CGameNetworkManager::HostChanged() +{ + // Disable host migration + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); +} + +void CGameNetworkManager::WriteStats( INetworkPlayer *pNetworkPlayer ) +{ + Minecraft::GetInstance()->forceStatsSave( pNetworkPlayer->GetUserIndex() ); +} + +void CGameNetworkManager::GameInviteReceived( int userIndex, const INVITE_INFO *pInviteInfo) +{ +#ifdef __ORBIS__ + if (m_pUpsell != NULL) + { + delete pInviteInfo; + return; + } + + // Need to check we're signed in to PSN + bool isSignedInLive = true; + bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable(); + int iPadNotSignedInLive = -1; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (ProfileManager.IsSignedIn(i) && (i == ProfileManager.GetPrimaryPad() || isLocalMultiplayerAvailable)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + } + } + + if (!isSignedInLive) + { + // Determine why they're not "signed in live" + + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else if (ProfileManager.isSignedInPSN(iPadNotSignedInLive)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPadNotSignedInLive)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPadNotSignedInLive, &CGameNetworkManager::MustSignInReturned_1, (void *)pInviteInfo, app.GetStringTable(), NULL, 0, false); + } + return; + } + + // 4J-JEV: Check that all players are authorised for PsPlus, present upsell to players that aren't and try again. + for (unsigned int index = 0; index < XUSER_MAX_COUNT; index++) + { + if ( ProfileManager.IsSignedIn(index) + && !ProfileManager.HasPlayStationPlus(userIndex) ) + { + m_pInviteInfo = (INVITE_INFO *) pInviteInfo; + m_iPlayerInvited = userIndex; + + m_pUpsell = new PsPlusUpsellWrapper(index); + m_pUpsell->displayUpsell(); + + return; + } + } +#endif + +#ifdef __PSVITA__ + // Need to check we're signed in to PSN + bool isSignedInLive = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()); + if (!isSignedInLive) + { + // Determine why they're not "signed in live" + // MGH - we need to add a new message at some point for connecting when already signed in +// if (ProfileManager.IsSignedInPSN(ProfileManager.GetPrimaryPad())) +// { +// // Signed in to PSN but not connected (no internet access) +// UINT uiIDA[1]; +// uiIDA[0] = IDS_OK; +// ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, ProfileManager.GetPrimaryPad(), NULL, NULL, app.GetStringTable()); +// } +// else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), &CGameNetworkManager::MustSignInReturned_1, (void *)pInviteInfo, app.GetStringTable(), NULL, 0, false); + } + return; + } +#endif + + + int localUsersMask = 0; + Minecraft *pMinecraft = Minecraft::GetInstance(); + int joiningUsers = 0; + + bool noPrivileges = false; + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + // 4J-PB we shouldn't bring any inactive players into the game, except for the invited player (who may be an inactive player) + // 4J Stu - If we are not in a game, then bring in all players signed in + if(index==userIndex || pMinecraft->localplayers[index]!=NULL ) + { + ++joiningUsers; + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + localUsersMask |= GetLocalPlayerMask( index ); + } + } + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + bool bContentRestricted=false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(userIndex,false,&noUGC,&bContentRestricted,NULL); +#else + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; +#endif + +#if defined(_XBOX) || defined(__PS3__) + if(joiningUsers > 1 && !RenderManager.IsHiDef() && userIndex != ProfileManager.GetPrimaryPad()) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + // 4J-PB - it's possible there is no primary pad here, when accepting an invite from the dashboard + ui.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_CONNECTION_FAILED_NO_SD_SPLITSCREEN, uiIDA,1,XUSER_INDEX_ANY,NULL,NULL, app.GetStringTable()); + } + else +#endif + + if( noUGC ) + { + int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + if(joiningUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + + ui.RequestUGCMessageBox(IDS_CONNECTION_FAILED, messageText, XUSER_INDEX_ANY); + } +#if defined(__PS3__) || defined __PSVITA__ + else if(bContentRestricted) + { + int messageText = IDS_CONTENT_RESTRICTION; + if(joiningUsers > 1) messageText = IDS_CONTENT_RESTRICTION_MULTIPLAYER; + + ui.RequestContentRestrictedMessageBox(IDS_CONNECTION_FAILED, messageText, XUSER_INDEX_ANY); + } +#endif + else if(noPrivileges) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + // 4J-PB - it's possible there is no primary pad here, when accepting an invite from the dashboard + //StorageManager.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,XUSER_INDEX_ANY,NULL,NULL, app.GetStringTable()); + } + else + { +#if defined(__ORBIS__) || defined(__PSVITA__) + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() ); + } +#endif + if( !g_NetworkManager.IsInSession() ) + { +#ifndef __PS3__ + HandleInviteWhenInMenus(userIndex, pInviteInfo); +#else + // PS3 is more complicated here - we need to make sure that the player is online. If they are then we can do the same as the xbox, if not we need to try and get them online and then, if they do sign in, go down the same path + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + HandleInviteWhenInMenus(userIndex, pInviteInfo); + } + else + { + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CGameNetworkManager::MustSignInReturned_1,(void *)pInviteInfo, app.GetStringTable()); + } +#endif + } + else + { + app.DebugPrintf("We are already in a multiplayer game...need to leave it\n"); + +// JoinFromInviteData *joinData = new JoinFromInviteData(); +// joinData->dwUserIndex = dwUserIndex; +// joinData->dwLocalUsersMask = dwLocalUsersMask; +// joinData->pInviteInfo = pInviteInfo; + + // tell the app to process this +#ifdef __PSVITA__ + if(((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->checkValidInviteData(pInviteInfo)) +#endif + { + app.ProcessInvite(userIndex,localUsersMask,pInviteInfo); + } + } + } +} + +volatile bool waitHere = true; + +void CGameNetworkManager::HandleInviteWhenInMenus( int userIndex, const INVITE_INFO *pInviteInfo) +{ + // We are in the root menus somewhere + +#if 0 + while( waitHere ) + { + Sleep(1); + } +#endif + + // if this is the trial game, then we need the user to unlock the full game + if(!ProfileManager.IsFullVersion()) + { + // The marketplace will fail with the primary player set to -1 + ProfileManager.SetPrimaryPad(userIndex); + + app.SetAction(userIndex,eAppAction_DashboardTrialJoinFromInvite); + } + else + { + ProfileManager.SetPrimaryPad(userIndex); + + // 4J Stu - If we accept an invite from the main menu before going to play game we need to load the DLC + // These checks are done within the StartInstallDLCProcess - (!app.DLCInstallProcessCompleted() && !app.DLCInstallPending()) app.StartInstallDLCProcess(dwUserIndex); + app.StartInstallDLCProcess(userIndex); + + // 4J Stu - Fix for #10936 - MP Lab: TCR 001: Matchmaking: Player is stuck in a soft-locked state after selecting the guest account when prompted + // The locked profile should not be changed if we are in menus as the main player might sign out in the sign-in ui + //ProfileManager.SetLockedProfile(-1); + + if(!app.IsLocalMultiplayerAvailable()) + { + bool noPrivileges=!ProfileManager.AllowedToPlayMultiplayer(userIndex); + + if(noPrivileges) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + ProfileManager.SetLockedProfile(userIndex); + ProfileManager.SetPrimaryPad(userIndex); + + int localUsersMask=0; + localUsersMask |= GetLocalPlayerMask( userIndex ); + + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + + // 4J-PB - clear any previous connection errors + Minecraft::GetInstance()->clearConnectionFailed(); + + g_NetworkManager.SetLocalGame(false); + + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + bool success = g_NetworkManager.JoinGameFromInviteInfo( userIndex, localUsersMask, pInviteInfo ); + if( !success ) + { + app.DebugPrintf( "Failed joining game from invite\n" ); + } + } + } + else + { + // the FromInvite will make the lib decide how many panes to display based on connected pads/signed in players +#ifdef _XBOX + ProfileManager.RequestSignInUI(true, false, false, false, false,&CGameNetworkManager::JoinFromInvite_SignInReturned, (LPVOID)pInviteInfo,userIndex); +#else + SignInInfo info; + info.Func = &CGameNetworkManager::JoinFromInvite_SignInReturned; + info.lpParam = (LPVOID)pInviteInfo; + info.requireOnline = true; + app.DebugPrintf("Using fullscreen layer\n"); + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info,eUILayer_Alert,eUIGroup_Fullscreen); +#endif + } + } +} + +void CGameNetworkManager::AddLocalPlayerFailed(int idx, bool serverFull/* = false*/) +{ + Minecraft::GetInstance()->connectionDisconnected(idx, serverFull ? DisconnectPacket::eDisconnect_ServerFull : DisconnectPacket::eDisconnect_ConnectionCreationFailed); +} + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +void CGameNetworkManager::HandleDisconnect(bool bLostRoomOnly,bool bPSNSignout) +#else +void CGameNetworkManager::HandleDisconnect(bool bLostRoomOnly) +#endif +{ + int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad(); + + if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && g_NetworkManager.IsInSession() ) + { + m_bLastDisconnectWasLostRoomOnly = bLostRoomOnly; +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + m_bSignedOutofPSN=bPSNSignout; +#endif + app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected); + } + else + { + m_bLastDisconnectWasLostRoomOnly = false; + } +} + +int CGameNetworkManager::GetPrimaryPad() +{ + return ProfileManager.GetPrimaryPad(); +} + +int CGameNetworkManager::GetLockedProfile() +{ + return ProfileManager.GetLockedProfile(); +} + +bool CGameNetworkManager::IsSignedInLive(int playerIdx) +{ + return ProfileManager.IsSignedInLive(playerIdx); +} + +bool CGameNetworkManager::AllowedToPlayMultiplayer(int playerIdx) +{ + return ProfileManager.AllowedToPlayMultiplayer(playerIdx); +} + +char *CGameNetworkManager::GetOnlineName(int playerIdx) +{ + return ProfileManager.GetGamertag(playerIdx); +} + +void CGameNetworkManager::ServerReadyCreate(bool create) +{ + m_hServerReadyEvent = ( create ? ( new C4JThread::Event ) : NULL ); +} + +void CGameNetworkManager::ServerReady() +{ + m_hServerReadyEvent->Set(); +} + +void CGameNetworkManager::ServerReadyWait() +{ + m_hServerReadyEvent->WaitForSignal(INFINITE); +} + +void CGameNetworkManager::ServerReadyDestroy() +{ + delete m_hServerReadyEvent; + m_hServerReadyEvent = NULL; +} + +bool CGameNetworkManager::ServerReadyValid() +{ + return ( m_hServerReadyEvent != NULL ); +} + +void CGameNetworkManager::ServerStoppedCreate(bool create) +{ + m_hServerStoppedEvent = ( create ? ( new C4JThread::Event ) : NULL ); +} + +void CGameNetworkManager::ServerStopped() +{ + m_hServerStoppedEvent->Set(); +} + +void CGameNetworkManager::ServerStoppedWait() +{ + // If this is called from the main thread, then this won't be ticking anything which can mean that the storage manager state can't progress. + // This means that the server thread we are waiting on won't ever finish, as it might be locked waiting for this to complete itself. + // Do some ticking here then if this is the case. + if( C4JThread::isMainThread() ) + { + int result = WAIT_TIMEOUT; + do + { +#ifndef _XBOX + RenderManager.StartFrame(); +#endif + result = m_hServerStoppedEvent->WaitForSignal(20); + // Tick some simple things + ProfileManager.Tick(); + StorageManager.Tick(); + InputManager.Tick(); + RenderManager.Tick(); + ui.tick(); + ui.render(); + RenderManager.Present(); + } while( result == WAIT_TIMEOUT ); + } + else + { + m_hServerStoppedEvent->WaitForSignal(INFINITE); + } +} + +void CGameNetworkManager::ServerStoppedDestroy() +{ + delete m_hServerStoppedEvent; + m_hServerStoppedEvent = NULL; +} + +bool CGameNetworkManager::ServerStoppedValid() +{ + return ( m_hServerStoppedEvent != NULL ); +} + +int CGameNetworkManager::GetJoiningReadyPercentage() +{ + return s_pPlatformNetworkManager->GetJoiningReadyPercentage(); +} + +#ifndef _XBOX +void CGameNetworkManager::FakeLocalPlayerJoined() +{ + s_pPlatformNetworkManager->FakeLocalPlayerJoined(); +} +#endif + +#ifdef __PSVITA__ +bool CGameNetworkManager::usingAdhocMode() +{ + return ((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->usingAdhocMode(); +} + +void CGameNetworkManager::setAdhocMode(bool bAdhoc) +{ + ((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->setAdhocMode(bAdhoc); +} + +void CGameNetworkManager::startAdhocMatching() +{ + ((CPlatformNetworkManagerSony*)s_pPlatformNetworkManager)->startAdhocMatching(); +} + +#endif diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.h b/Minecraft.Client/Common/Network/GameNetworkManager.h new file mode 100644 index 0000000..1bb532d --- /dev/null +++ b/Minecraft.Client/Common/Network/GameNetworkManager.h @@ -0,0 +1,236 @@ +#pragma once +using namespace std; +#include +#include +#include "..\..\..\Minecraft.World\C4JThread.h" +#include "NetworkPlayerInterface.h" +#ifdef _XBOX +#include "..\..\Xbox\Network\PlatformNetworkManagerXbox.h" +#elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ +#include "..\..\Common\Network\Sony\PlatformNetworkManagerSony.h" +#elif defined _DURANGO +#include "..\..\Durango\Network\PlatformNetworkManagerDurango.h" +#else +#include "PlatformNetworkManagerStub.h" +#endif +#include "SessionInfo.h" + +#ifdef __ORBIS__ +#include "..\..\Orbis\Network\PsPlusUpsellWrapper_Orbis.h" +#endif + +class ClientConnection; +class Minecraft; + + +// This class implements the game-side interface to the networking system. As such, it is platform independent and may contain bits of game-side code where appropriate. +// It shouldn't ever reference any platform specifics of the network implementation (eg QNET), rather it should interface with an implementation of PlatformNetworkManager to +// provide this functionality. + +class CGameNetworkManager +{ +#ifdef _XBOX + friend class CPlatformNetworkManagerXbox; +#elif defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + friend class CPlatformNetworkManagerSony; +#elif defined _DURANGO + friend class CPlatformNetworkManagerDurango; +#else + friend class CPlatformNetworkManagerStub; +#endif +public: + CGameNetworkManager(); + // Misc high level flow + + typedef enum + { + JOINGAME_SUCCESS, + JOINGAME_FAIL_GENERAL, + JOINGAME_FAIL_SERVER_FULL + } eJoinGameResult; + + void Initialise(); + void Terminate(); + void DoWork(); + bool _RunNetworkGame(LPVOID lpParameter); + bool StartNetworkGame(Minecraft *minecraft, LPVOID lpParameter); + int CorrectErrorIDS(int IDS); + + // Player management + + static int GetLocalPlayerMask(int playerIndex); + int GetPlayerCount(); + int GetOnlinePlayerCount(); + bool AddLocalPlayerByUserIndex( int userIndex ); + bool RemoveLocalPlayerByUserIndex( int userIndex ); + INetworkPlayer *GetLocalPlayerByUserIndex(int userIndex ); + INetworkPlayer *GetPlayerByIndex(int playerIndex); + INetworkPlayer *GetPlayerByXuid(PlayerUID xuid); + INetworkPlayer *GetPlayerBySmallId(unsigned char smallId); + wstring GetDisplayNameByGamertag(wstring gamertag); + INetworkPlayer *GetHostPlayer(); + void RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + void UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + void HandleSignInChange(); + bool ShouldMessageForFullSession(); + + // State management + + bool IsInSession(); + bool IsInGameplay(); + bool IsLeavingGame(); + bool IsReadyToPlayOrIdle(); + + // Hosting and game type + + bool SetLocalGame(bool isLocal); + bool IsLocalGame(); + void SetPrivateGame(bool isPrivate); + bool IsPrivateGame(); + void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0); + bool IsHost(); + bool IsInStatsEnabledSession(); + + // Client session discovery + + bool SessionHasSpace(unsigned int spaceRequired = 1); + vector *GetSessionList(int iPad, int localPlayers, bool partyOnly); + bool GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession); + void SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ); + void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ); + void ForceFriendsSessionRefresh(); + + // Session joining and leaving + + bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo); + eJoinGameResult JoinGame(FriendSessionInfo *searchResult, int localUsersMask); + static void CancelJoinGame(LPVOID lpParam); // Not part of the shared interface + bool LeaveGame(bool bMigrateHost); + static int JoinFromInvite_SignInReturned(void *pParam,bool bContinue, int iPad); + void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = NULL); + void SendInviteGUI(int iPad); + void ResetLeavingGame(); + + // Threads + + bool IsNetworkThreadRunning(); + static int RunNetworkGameThreadProc( void* lpParameter ); + static int ServerThreadProc( void* lpParameter ); + static int ExitAndJoinFromInviteThreadProc( void* lpParam ); + +#if (defined __PS3__) || (defined __ORBIS__) || (defined __PSVITA__) + static int MustSignInReturned_0(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int PSNSignInReturned_0(void* pParam, bool bContinue, int iPad); + + static int MustSignInReturned_1(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int PSNSignInReturned_1(void* pParam, bool bContinue, int iPad); +#endif + + static void _LeaveGame(); + static int ChangeSessionTypeThreadProc( void* lpParam ); + + // System flags + + void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index); + bool SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index); + + // Events + + void ServerReadyCreate(bool create); // Create the signal (or set to NULL) + void ServerReady(); // Signal that we are ready + void ServerReadyWait(); // Wait for the signal + void ServerReadyDestroy(); // Destroy signal + bool ServerReadyValid(); // Is non-NULL + + void ServerStoppedCreate(bool create); // Create the signal + void ServerStopped(); // Signal that we are ready + void ServerStoppedWait(); // Wait for the signal + void ServerStoppedDestroy(); // Destroy signal + bool ServerStoppedValid(); // Is non-NULL + +#ifdef __PSVITA__ + static bool usingAdhocMode(); + static void setAdhocMode(bool bAdhoc); + static void startAdhocMatching(); +#endif + // Debug output + + wstring GatherStats(); + void renderQueueMeter(); + wstring GatherRTTStats(); + + // GUI debug output + + // Used for debugging output + static const int messageQueue_length = 512; + static __int64 messageQueue[messageQueue_length]; + static const int byteQueue_length = 512; + static __int64 byteQueue[byteQueue_length]; + static int messageQueuePos; + + // Methods called from PlatformNetworkManager +private: + void StateChange_AnyToHosting(); + void StateChange_AnyToJoining(); + void StateChange_JoiningToIdle(CPlatformNetworkManager::eJoinFailedReason reason); + void StateChange_AnyToStarting(); + void StateChange_AnyToEnding(bool bStateWasPlaying); + void StateChange_AnyToIdle(); + void CreateSocket( INetworkPlayer *pNetworkPlayer, bool localPlayer ); + void CloseConnection( INetworkPlayer *pNetworkPlayer ); + void PlayerJoining( INetworkPlayer *pNetworkPlayer ); + void PlayerLeaving( INetworkPlayer *pNetworkPlayer ); + void HostChanged(); + void WriteStats( INetworkPlayer *pNetworkPlayer ); + void GameInviteReceived( int userIndex, const INVITE_INFO *pInviteInfo); + void HandleInviteWhenInMenus( int userIndex, const INVITE_INFO *pInviteInfo); + void AddLocalPlayerFailed(int idx, bool serverFull = false); +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + void HandleDisconnect(bool bLostRoomOnly,bool bPSNSignOut); +#else + void HandleDisconnect(bool bLostRoomOnly); +#endif + + int GetPrimaryPad(); + int GetLockedProfile(); + bool IsSignedInLive(int playerIdx); + bool AllowedToPlayMultiplayer(int playerIdx); + char *GetOnlineName(int playerIdx); + + C4JThread::Event* m_hServerStoppedEvent; + C4JThread::Event* m_hServerReadyEvent; + bool m_bInitialised; + +#ifdef _XBOX_ONE +public: + void SetFullSessionMessageOnNextSessionChange() { m_bFullSessionMessageOnNextSessionChange = true; } +#endif +private: + float m_lastPlayerEventTimeStart; // For telemetry + static CPlatformNetworkManager *s_pPlatformNetworkManager; + bool m_bNetworkThreadRunning; + int GetJoiningReadyPercentage(); + bool m_bLastDisconnectWasLostRoomOnly; + bool m_bFullSessionMessageOnNextSessionChange; +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + bool m_bSignedOutofPSN; +#endif + +#ifdef __ORBIS__ + PsPlusUpsellWrapper *m_pUpsell; + INVITE_INFO *m_pInviteInfo; + int m_iPlayerInvited; +#endif + +public: +#ifndef _XBOX + void FakeLocalPlayerJoined(); // Temporary method whilst we don't have real networking to make this happen +#endif +}; + +extern CGameNetworkManager g_NetworkManager; + +#ifdef __PS3__ +#undef __in +#define __out +#endif diff --git a/Minecraft.Client/Common/Network/NetworkPlayerInterface.h b/Minecraft.Client/Common/Network/NetworkPlayerInterface.h new file mode 100644 index 0000000..501b08f --- /dev/null +++ b/Minecraft.Client/Common/Network/NetworkPlayerInterface.h @@ -0,0 +1,31 @@ +#pragma once + +class Socket; + +// This is the platform independent interface for dealing with players within a network game. This should be used directly by game code (and GameNetworkManager) rather than the platform-specific implementations. + +class INetworkPlayer +{ +public: + virtual ~INetworkPlayer() {} + virtual unsigned char GetSmallId() = 0; + virtual void SendData(INetworkPlayer *player, const void *pvData, int dataSize, bool lowPriority) = 0; + virtual bool IsSameSystem(INetworkPlayer *player) = 0; + virtual int GetSendQueueSizeBytes( INetworkPlayer *player, bool lowPriority ) = 0; + virtual int GetSendQueueSizeMessages( INetworkPlayer *player, bool lowPriority ) = 0; + virtual int GetCurrentRtt() = 0; + virtual bool IsHost() = 0; + virtual bool IsGuest() = 0; + virtual bool IsLocal() = 0; + virtual int GetSessionIndex() = 0; + virtual bool IsTalking() = 0; + virtual bool IsMutedByLocalUser(int userIndex) = 0; + virtual bool HasVoice() = 0; + virtual bool HasCamera() = 0; + virtual int GetUserIndex() = 0; + virtual void SetSocket(Socket *pSocket) = 0; + virtual Socket *GetSocket() = 0; + virtual const wchar_t *GetOnlineName() = 0; + virtual wstring GetDisplayName() = 0; + virtual PlayerUID GetUID() = 0; +}; diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerInterface.h b/Minecraft.Client/Common/Network/PlatformNetworkManagerInterface.h new file mode 100644 index 0000000..31c415a --- /dev/null +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerInterface.h @@ -0,0 +1,127 @@ +#pragma once +using namespace std; +#include +#include +#include "..\..\..\Minecraft.World\C4JThread.h" +#include "NetworkPlayerInterface.h" +#include "SessionInfo.h" + +class ClientConnection; +class Minecraft; +class CGameNetworkManager; + +// This is the interface to be implemented by the platform-specific versions of the PlatformNetworkManagers. This API is used directly by GameNetworkManager so that +// it can remain as platform independent as possible. + +// This value should be incremented if the server version changes, or the game session data changes +#define MINECRAFT_NET_VERSION VER_NETWORK + + +typedef struct _SearchForGamesData +{ + DWORD sessionIDCount; + XSESSION_SEARCHRESULT_HEADER *searchBuffer; + XNQOS **ppQos; + SessionID *sessionIDList; + XOVERLAPPED *pOverlapped; +} SearchForGamesData; + +class CPlatformNetworkManager +{ + friend class CGameNetworkManager; +public: + + typedef enum + { + JOIN_FAILED_SERVER_FULL, + JOIN_FAILED_INSUFFICIENT_PRIVILEGES, + JOIN_FAILED_NONSPECIFIC, + } eJoinFailedReason; + + virtual bool Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize) = 0; + virtual void Terminate() = 0; + virtual int GetJoiningReadyPercentage() = 0; + virtual int CorrectErrorIDS(int IDS) = 0; + + virtual void DoWork() = 0; + virtual int GetPlayerCount() = 0; + virtual int GetOnlinePlayerCount() = 0; + virtual int GetLocalPlayerMask(int playerIndex) = 0; + virtual bool AddLocalPlayerByUserIndex( int userIndex ) = 0; + virtual bool RemoveLocalPlayerByUserIndex( int userIndex ) = 0; + virtual INetworkPlayer *GetLocalPlayerByUserIndex( int userIndex ) = 0; + virtual INetworkPlayer *GetPlayerByIndex(int playerIndex) = 0; + virtual INetworkPlayer * GetPlayerByXuid(PlayerUID xuid) = 0; + virtual INetworkPlayer * GetPlayerBySmallId(unsigned char smallId) = 0; + virtual bool ShouldMessageForFullSession() = 0; + + virtual INetworkPlayer *GetHostPlayer() = 0; + virtual bool IsHost() = 0; + virtual bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo) = 0; + virtual bool LeaveGame(bool bMigrateHost) = 0; + + virtual bool IsInSession() = 0; + virtual bool IsInGameplay() = 0; + virtual bool IsReadyToPlayOrIdle() = 0; + virtual bool IsInStatsEnabledSession() = 0; + virtual bool SessionHasSpace(unsigned int spaceRequired = 1) = 0; + virtual void SendInviteGUI(int quadrant) = 0; + virtual bool IsAddingPlayer() = 0; + + virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0; + virtual int JoinGame(FriendSessionInfo *searchResult, int dwLocalUsersMask, int dwPrimaryUserIndex ) = 0; + virtual void CancelJoinGame() {}; + virtual bool SetLocalGame(bool isLocal) = 0; + virtual bool IsLocalGame() = 0; + virtual void SetPrivateGame(bool isPrivate) = 0; + virtual bool IsPrivateGame() = 0; + virtual bool IsLeavingGame() = 0; + virtual void ResetLeavingGame() = 0; + + virtual void RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) = 0; + virtual void UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) = 0; + + virtual void HandleSignInChange() = 0; + + virtual bool _RunNetworkGame() = 0; + virtual void SetGamePlayState() {} + +private: + virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom) = 0; + virtual void _HostGame(int usersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0) = 0; + virtual bool _StartGame() = 0; + + +public: + virtual void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = NULL) = 0; + +private: + virtual bool RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ) = 0; + +public: + virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index) = 0; + virtual bool SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index) = 0; + + virtual wstring GatherStats() = 0; + virtual wstring GatherRTTStats() = 0; + +private: + virtual void SetSessionTexturePackParentId( int id ) = 0; + virtual void SetSessionSubTexturePackId( int id ) = 0; + virtual void Notify(int ID, ULONG_PTR Param) = 0; + +public: + virtual vector *GetSessionList(int iPad, int localPlayers, bool partyOnly) = 0; + virtual bool GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession) = 0; + virtual void SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) = 0; + virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) = 0; + virtual void ForceFriendsSessionRefresh() = 0; + +#ifndef _XBOX + virtual void FakeLocalPlayerJoined() {}; // Temporary method whilst we don't have real networking to make this happen +#endif + +#ifdef _DURANGO + virtual wstring GetDisplayNameByGamertag(wstring gamertag) = 0; +#endif +}; diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp new file mode 100644 index 0000000..24d5243 --- /dev/null +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp @@ -0,0 +1,859 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Socket.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "PlatformNetworkManagerStub.h" +#include "..\..\Xbox\Network\NetworkPlayerXbox.h" +#ifdef _WINDOWS64 +#include "..\..\Windows64\Network\WinsockNetLayer.h" +#include "..\..\Minecraft.h" +#include "..\..\User.h" +#endif + +CPlatformNetworkManagerStub *g_pPlatformNetworkManager; + + +void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer ) +{ + const char * pszDescription; + + // 4J Stu - We create a fake socket for every where that we need an INBOUND queue of game data. Outbound + // is all handled by QNet so we don't need that. Therefore each client player has one, and the host has one + // for each client player. + bool createFakeSocket = false; + bool localPlayer = false; + + NetworkPlayerXbox *networkPlayer = (NetworkPlayerXbox *)addNetworkPlayer(pQNetPlayer); + + if( pQNetPlayer->IsLocal() ) + { + localPlayer = true; + if( pQNetPlayer->IsHost() ) + { + pszDescription = "local host"; + // 4J Stu - No socket for the localhost as it uses a special loopback queue + + m_machineQNetPrimaryPlayers.push_back( pQNetPlayer ); + } + else + { + pszDescription = "local"; + + // We need an inbound queue on all local players to receive data from the host + createFakeSocket = true; + } + } + else + { + if( pQNetPlayer->IsHost() ) + { + pszDescription = "remote host"; + } + else + { + pszDescription = "remote"; + + // If we are the host, then create a fake socket for every remote player + if( m_pIQNet->IsHost() ) + { + createFakeSocket = true; + } + } + + if( m_pIQNet->IsHost() && !m_bHostChanged ) + { + // Do we already have a primary player for this system? + bool systemHasPrimaryPlayer = false; + for(AUTO_VAR(it, m_machineQNetPrimaryPlayers.begin()); it < m_machineQNetPrimaryPlayers.end(); ++it) + { + IQNetPlayer *pQNetPrimaryPlayer = *it; + if( pQNetPlayer->IsSameSystem(pQNetPrimaryPlayer) ) + { + systemHasPrimaryPlayer = true; + break; + } + } + if( !systemHasPrimaryPlayer ) + m_machineQNetPrimaryPlayers.push_back( pQNetPlayer ); + } + } + g_NetworkManager.PlayerJoining( networkPlayer ); + + if( createFakeSocket == true && !m_bHostChanged ) + { + g_NetworkManager.CreateSocket( networkPlayer, localPlayer ); + } + + app.DebugPrintf( "Player 0x%p \"%ls\" joined; %s; voice %i; camera %i.\n", + pQNetPlayer, + pQNetPlayer->GetGamertag(), + pszDescription, + (int) pQNetPlayer->HasVoice(), + (int) pQNetPlayer->HasCamera() ); + + + if( m_pIQNet->IsHost() ) + { + // 4J-PB - only the host should do this +// g_NetworkManager.UpdateAndSetGameSessionData(); + SystemFlagAddPlayer( networkPlayer ); + } + + for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(playerChangedCallback[idx] != NULL) + playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, false ); + } + + if(m_pIQNet->GetState() == QNET_STATE_GAME_PLAY) + { + int localPlayerCount = 0; + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if( m_pIQNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount; + } + + float appTime = app.getAppTime(); + + // Only record stats for the primary player here + m_lastPlayerEventTimeStart = appTime; + } +} + +void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer *pQNetPlayer) +{ + app.DebugPrintf("Player 0x%p \"%ls\" leaving.\n", pQNetPlayer, pQNetPlayer->GetGamertag()); + + INetworkPlayer *networkPlayer = getNetworkPlayer(pQNetPlayer); + if (networkPlayer == NULL) + return; + + Socket *socket = networkPlayer->GetSocket(); + if (socket != NULL) + { + if (m_pIQNet->IsHost()) + g_NetworkManager.CloseConnection(networkPlayer); + } + + if (m_pIQNet->IsHost()) + { + SystemFlagRemovePlayer(networkPlayer); + } + + g_NetworkManager.PlayerLeaving(networkPlayer); + + for (int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if (playerChangedCallback[idx] != NULL) + playerChangedCallback[idx](playerChangedCallbackParam[idx], networkPlayer, true); + } + + removeNetworkPlayer(pQNetPlayer); +} + +bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize) +{ + m_pGameNetworkManager = pGameNetworkManager; + m_flagIndexSize = flagIndexSize; + m_pIQNet = new IQNet(); + g_pPlatformNetworkManager = this; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + playerChangedCallback[ i ] = NULL; + } + + m_bLeavingGame = false; + m_bLeaveGameOnTick = false; + m_bHostChanged = false; + + m_bSearchResultsReady = false; + m_bSearchPending = false; + + m_bIsOfflineGame = false; + m_pSearchParam = NULL; + m_SessionsUpdatedCallback = NULL; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_searchResultsCount[i] = 0; + m_lastSearchStartTime[i] = 0; + + // The results that will be filled in with the current search + m_pSearchResults[i] = NULL; + m_pQoSResult[i] = NULL; + m_pCurrentSearchResults[i] = NULL; + m_pCurrentQoSResult[i] = NULL; + m_currentSearchResultsCount[i] = 0; + } + + // Success! + return true; +} + +void CPlatformNetworkManagerStub::Terminate() +{ +} + +int CPlatformNetworkManagerStub::GetJoiningReadyPercentage() +{ + return 100; +} + +int CPlatformNetworkManagerStub::CorrectErrorIDS(int IDS) +{ + return IDS; +} + +bool CPlatformNetworkManagerStub::isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer) +{ + return true; +} + +// We call this twice a frame, either side of the render call so is a good place to "tick" things +void CPlatformNetworkManagerStub::DoWork() +{ +#ifdef _WINDOWS64 + extern QNET_STATE _iQNetStubState; + if (_iQNetStubState == QNET_STATE_SESSION_STARTING && app.GetGameStarted()) + { + _iQNetStubState = QNET_STATE_GAME_PLAY; + if (m_pIQNet->IsHost()) + WinsockNetLayer::UpdateAdvertiseJoinable(true); + } + if (_iQNetStubState == QNET_STATE_IDLE) + TickSearch(); + if (_iQNetStubState == QNET_STATE_GAME_PLAY && m_pIQNet->IsHost()) + { + BYTE disconnectedSmallId; + while (WinsockNetLayer::PopDisconnectedSmallId(&disconnectedSmallId)) + { + IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId); + if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId) + { + NotifyPlayerLeaving(qnetPlayer); + qnetPlayer->m_smallId = 0; + qnetPlayer->m_isRemote = false; + qnetPlayer->m_isHostPlayer = false; + qnetPlayer->m_gamertag[0] = 0; + qnetPlayer->SetCustomDataValue(0); + WinsockNetLayer::PushFreeSmallId(disconnectedSmallId); + if (IQNet::s_playerCount > 1) + IQNet::s_playerCount--; + } + } + } +#endif +} + +int CPlatformNetworkManagerStub::GetPlayerCount() +{ + return m_pIQNet->GetPlayerCount(); +} + +bool CPlatformNetworkManagerStub::ShouldMessageForFullSession() +{ + return false; +} + +int CPlatformNetworkManagerStub::GetOnlinePlayerCount() +{ + return 1; +} + +int CPlatformNetworkManagerStub::GetLocalPlayerMask(int playerIndex) +{ + return 1 << playerIndex; +} + +bool CPlatformNetworkManagerStub::AddLocalPlayerByUserIndex( int userIndex ) +{ + NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(userIndex)); + return ( m_pIQNet->AddLocalPlayerByUserIndex(userIndex) == S_OK ); +} + +bool CPlatformNetworkManagerStub::RemoveLocalPlayerByUserIndex( int userIndex ) +{ + return true; +} + +bool CPlatformNetworkManagerStub::IsInStatsEnabledSession() +{ + return true; +} + +bool CPlatformNetworkManagerStub::SessionHasSpace(unsigned int spaceRequired /*= 1*/) +{ + return true; +} + +void CPlatformNetworkManagerStub::SendInviteGUI(int quadrant) +{ +} + +bool CPlatformNetworkManagerStub::IsAddingPlayer() +{ + return false; +} + +bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost) +{ + if( m_bLeavingGame ) return true; + + m_bLeavingGame = true; + +#ifdef _WINDOWS64 + WinsockNetLayer::StopAdvertising(); +#endif + + if(m_pIQNet->IsHost() && g_NetworkManager.ServerStoppedValid()) + { + m_pIQNet->EndGame(); + g_NetworkManager.ServerStoppedWait(); + g_NetworkManager.ServerStoppedDestroy(); + } + else + { + m_pIQNet->EndGame(); + } + + for (AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++) + delete *it; + currentNetworkPlayers.clear(); + m_machineQNetPrimaryPlayers.clear(); + SystemFlagReset(); + +#ifdef _WINDOWS64 + WinsockNetLayer::Shutdown(); + WinsockNetLayer::Initialize(); +#endif + + return true; +} + +bool CPlatformNetworkManagerStub::_LeaveGame(bool bMigrateHost, bool bLeaveRoom) +{ + return true; +} + +void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) +{ + SetLocalGame( !bOnlineGame ); + SetPrivateGame( bIsPrivate ); + SystemFlagReset(); + + localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() ); + + m_bLeavingGame = false; + + m_pIQNet->HostGame(); + +#ifdef _WINDOWS64 + IQNet::m_player[0].m_smallId = 0; + IQNet::m_player[0].m_isRemote = false; + IQNet::m_player[0].m_isHostPlayer = true; + IQNet::s_playerCount = 1; +#endif + + _HostGame( localUsersMask, publicSlots, privateSlots ); + +#ifdef _WINDOWS64 + int port = WIN64_NET_DEFAULT_PORT; + if (!WinsockNetLayer::IsActive()) + WinsockNetLayer::HostGame(port); + + const wchar_t *hostName = IQNet::m_player[0].m_gamertag; + unsigned int settings = app.GetGameHostOption(eGameHostOption_All); + WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); +#endif +} + +void CPlatformNetworkManagerStub::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) +{ +} + +bool CPlatformNetworkManagerStub::_StartGame() +{ + return true; +} + +int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex) +{ +#ifdef _WINDOWS64 + if (searchResult == NULL) + return CGameNetworkManager::JOINGAME_FAIL_GENERAL; + + const char *hostIP = searchResult->data.hostIP; + int hostPort = searchResult->data.hostPort; + + if (hostPort <= 0 || hostIP[0] == 0) + return CGameNetworkManager::JOINGAME_FAIL_GENERAL; + + m_bLeavingGame = false; + IQNet::s_isHosting = false; + m_pIQNet->ClientJoinGame(); + + IQNet::m_player[0].m_smallId = 0; + IQNet::m_player[0].m_isRemote = true; + IQNet::m_player[0].m_isHostPlayer = true; + wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE); + + WinsockNetLayer::StopDiscovery(); + + if (!WinsockNetLayer::JoinGame(hostIP, hostPort)) + { + app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort); + return CGameNetworkManager::JOINGAME_FAIL_GENERAL; + } + + BYTE localSmallId = WinsockNetLayer::GetLocalSmallId(); + + IQNet::m_player[localSmallId].m_smallId = localSmallId; + IQNet::m_player[localSmallId].m_isRemote = false; + IQNet::m_player[localSmallId].m_isHostPlayer = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str()); + IQNet::s_playerCount = localSmallId + 1; + + NotifyPlayerJoined(&IQNet::m_player[0]); + NotifyPlayerJoined(&IQNet::m_player[localSmallId]); + + m_pGameNetworkManager->StateChange_AnyToStarting(); + + return CGameNetworkManager::JOINGAME_SUCCESS; +#else + return CGameNetworkManager::JOINGAME_SUCCESS; +#endif +} + +bool CPlatformNetworkManagerStub::SetLocalGame(bool isLocal) +{ + m_bIsOfflineGame = isLocal; + + return true; +} + +void CPlatformNetworkManagerStub::SetPrivateGame(bool isPrivate) +{ + app.DebugPrintf("Setting as private game: %s\n", isPrivate ? "yes" : "no" ); + m_bIsPrivateGame = isPrivate; +} + +void CPlatformNetworkManagerStub::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + playerChangedCallback[iPad] = callback; + playerChangedCallbackParam[iPad] = callbackParam; +} + +void CPlatformNetworkManagerStub::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + if(playerChangedCallbackParam[iPad] == callbackParam) + { + playerChangedCallback[iPad] = NULL; + playerChangedCallbackParam[iPad] = NULL; + } +} + +void CPlatformNetworkManagerStub::HandleSignInChange() +{ + return; +} + +bool CPlatformNetworkManagerStub::_RunNetworkGame() +{ +#ifdef _WINDOWS64 + extern QNET_STATE _iQNetStubState; + _iQNetStubState = QNET_STATE_GAME_PLAY; + + for (DWORD i = 0; i < IQNet::s_playerCount; i++) + { + if (IQNet::m_player[i].m_isRemote) + { + INetworkPlayer *pNetworkPlayer = getNetworkPlayer(&IQNet::m_player[i]); + if (pNetworkPlayer != NULL && pNetworkPlayer->GetSocket() != NULL) + { + Socket::addIncomingSocket(pNetworkPlayer->GetSocket()); + } + } + } +#endif + return true; +} + +void CPlatformNetworkManagerStub::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/) +{ +// DWORD playerCount = m_pIQNet->GetPlayerCount(); +// +// if( this->m_bLeavingGame ) +// return; +// +// if( GetHostPlayer() == NULL ) +// return; +// +// for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) +// { +// if( i < playerCount ) +// { +// INetworkPlayer *pNetworkPlayer = GetPlayerByIndex(i); +// +// // We can call this from NotifyPlayerLeaving but at that point the player is still considered in the session +// if( pNetworkPlayer != pNetworkPlayerLeaving ) +// { +// m_hostGameSessionData.players[i] = ((NetworkPlayerXbox *)pNetworkPlayer)->GetUID(); +// +// char *temp; +// temp = (char *)wstringtofilename( pNetworkPlayer->GetOnlineName() ); +// memcpy(m_hostGameSessionData.szPlayers[i],temp,XUSER_NAME_SIZE); +// } +// else +// { +// m_hostGameSessionData.players[i] = NULL; +// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE); +// } +// } +// else +// { +// m_hostGameSessionData.players[i] = NULL; +// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE); +// } +// } +// +// m_hostGameSessionData.hostPlayerUID = ((NetworkPlayerXbox *)GetHostPlayer())->GetQNetPlayer()->GetXuid(); +// m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All); +} + +int CPlatformNetworkManagerStub::RemovePlayerOnSocketClosedThreadProc( void* lpParam ) +{ + INetworkPlayer *pNetworkPlayer = (INetworkPlayer *)lpParam; + + Socket *socket = pNetworkPlayer->GetSocket(); + + if( socket != NULL ) + { + //printf("Waiting for socket closed event\n"); + socket->m_socketClosedEvent->WaitForSignal(INFINITE); + + //printf("Socket closed event has fired\n"); + // 4J Stu - Clear our reference to this socket + pNetworkPlayer->SetSocket( NULL ); + delete socket; + } + + return g_pPlatformNetworkManager->RemoveLocalPlayer( pNetworkPlayer ); +} + +bool CPlatformNetworkManagerStub::RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ) +{ + return true; +} + +CPlatformNetworkManagerStub::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count) +{ + // 4J Stu - Don't assert, just make it a multiple of 8! This count is calculated from a load of separate values, + // and makes tweaking world/render sizes a pain if we hit an assert here + count = (count + 8 - 1) & ~(8 - 1); + //assert( ( count % 8 ) == 0 ); + this->m_pNetworkPlayer = pNetworkPlayer; + this->flags = new unsigned char [ count / 8 ]; + memset( this->flags, 0, count / 8 ); + this->count = count; +} +CPlatformNetworkManagerStub::PlayerFlags::~PlayerFlags() +{ + delete [] flags; +} + +// Add a player to the per system flag storage - if we've already got a player from that system, copy its flags over +void CPlatformNetworkManagerStub::SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer) +{ + PlayerFlags *newPlayerFlags = new PlayerFlags( pNetworkPlayer, m_flagIndexSize); + // If any of our existing players are on the same system, then copy over flags from that one + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) + { + memcpy( newPlayerFlags->flags, m_playerFlags[i]->flags, m_playerFlags[i]->count / 8 ); + break; + } + } + m_playerFlags.push_back(newPlayerFlags); +} + +// Remove a player from the per system flag storage - just maintains the m_playerFlags vector without any gaps in it +void CPlatformNetworkManagerStub::SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer) +{ + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) + { + delete m_playerFlags[i]; + m_playerFlags[i] = m_playerFlags.back(); + m_playerFlags.pop_back(); + return; + } + } +} + +void CPlatformNetworkManagerStub::SystemFlagReset() +{ + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + delete m_playerFlags[i]; + } + m_playerFlags.clear(); +} + +// Set a per system flag - this is done by setting the flag on every player that shares that system +void CPlatformNetworkManagerStub::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index) +{ + if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return; + if( pNetworkPlayer == NULL ) return; + + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) + { + m_playerFlags[i]->flags[ index / 8 ] |= ( 128 >> ( index % 8 ) ); + } + } +} + +// Get value of a per system flag - can be read from the flags of the passed in player as anything else sent to that +// system should also have been duplicated here +bool CPlatformNetworkManagerStub::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index) +{ + if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return false; + if( pNetworkPlayer == NULL ) + { + return false; + } + + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) + { + return ( ( m_playerFlags[i]->flags[ index / 8 ] & ( 128 >> ( index % 8 ) ) ) != 0 ); + } + } + return false; +} + +wstring CPlatformNetworkManagerStub::GatherStats() +{ + return L""; +} + +wstring CPlatformNetworkManagerStub::GatherRTTStats() +{ + wstring stats(L"Rtt: "); + + wchar_t stat[32]; + + for(unsigned int i = 0; i < GetPlayerCount(); ++i) + { + IQNetPlayer *pQNetPlayer = ((NetworkPlayerXbox *)GetPlayerByIndex( i ))->GetQNetPlayer(); + + if(!pQNetPlayer->IsLocal()) + { + ZeroMemory(stat,32*sizeof(WCHAR)); + swprintf(stat, 32, L"%d: %d/", i, pQNetPlayer->GetCurrentRtt() ); + stats.append(stat); + } + } + return stats; +} + +void CPlatformNetworkManagerStub::TickSearch() +{ +#ifdef _WINDOWS64 + if (m_SessionsUpdatedCallback == NULL) + return; + + static DWORD lastSearchTime = 0; + DWORD now = GetTickCount(); + if (now - lastSearchTime < 2000) + return; + lastSearchTime = now; + + SearchForGames(); +#endif +} + +void CPlatformNetworkManagerStub::SearchForGames() +{ +#ifdef _WINDOWS64 + std::vector lanSessions = WinsockNetLayer::GetDiscoveredSessions(); + + for (size_t i = 0; i < friendsSessions[0].size(); i++) + delete friendsSessions[0][i]; + friendsSessions[0].clear(); + + for (size_t i = 0; i < lanSessions.size(); i++) + { + FriendSessionInfo *info = new FriendSessionInfo(); + size_t nameLen = wcslen(lanSessions[i].hostName); + info->displayLabel = new wchar_t[nameLen + 1]; + wcscpy_s(info->displayLabel, nameLen + 1, lanSessions[i].hostName); + info->displayLabelLength = (unsigned char)nameLen; + info->displayLabelViewableStartIndex = 0; + + info->data.netVersion = lanSessions[i].netVersion; + info->data.m_uiGameHostSettings = lanSessions[i].gameHostSettings; + info->data.texturePackParentId = lanSessions[i].texturePackParentId; + info->data.subTexturePackId = lanSessions[i].subTexturePackId; + info->data.isReadyToJoin = lanSessions[i].isJoinable; + info->data.isJoinable = lanSessions[i].isJoinable; + strncpy_s(info->data.hostIP, sizeof(info->data.hostIP), lanSessions[i].hostIP, _TRUNCATE); + info->data.hostPort = lanSessions[i].hostPort; + wcsncpy_s(info->data.hostName, XUSER_NAME_SIZE, lanSessions[i].hostName, _TRUNCATE); + info->data.playerCount = lanSessions[i].playerCount; + info->data.maxPlayers = lanSessions[i].maxPlayers; + + info->sessionId = (SessionID)((unsigned __int64)inet_addr(lanSessions[i].hostIP) | ((unsigned __int64)lanSessions[i].hostPort << 32)); + + friendsSessions[0].push_back(info); + } + + m_searchResultsCount[0] = (int)friendsSessions[0].size(); + + if (m_SessionsUpdatedCallback != NULL) + m_SessionsUpdatedCallback(m_pSearchParam); +#endif +} + +int CPlatformNetworkManagerStub::SearchForGamesThreadProc( void* lpParameter ) +{ + return 0; +} + +void CPlatformNetworkManagerStub::SetSearchResultsReady(int resultCount) +{ + m_bSearchResultsReady = true; + m_searchResultsCount[m_lastSearchPad] = resultCount; +} + +vector *CPlatformNetworkManagerStub::GetSessionList(int iPad, int localPlayers, bool partyOnly) +{ + vector *filteredList = new vector(); + for (size_t i = 0; i < friendsSessions[0].size(); i++) + filteredList->push_back(friendsSessions[0][i]); + return filteredList; +} + +bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo) +{ + return false; +} + +void CPlatformNetworkManagerStub::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) +{ + m_SessionsUpdatedCallback = SessionsUpdatedCallback; m_pSearchParam = pSearchParam; +} + +void CPlatformNetworkManagerStub::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) +{ + FriendSessionUpdatedFn(true, pParam); +} + +void CPlatformNetworkManagerStub::ForceFriendsSessionRefresh() +{ + app.DebugPrintf("Resetting friends session search data\n"); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_searchResultsCount[i] = 0; + m_lastSearchStartTime[i] = 0; + delete m_pSearchResults[i]; + m_pSearchResults[i] = NULL; + } +} + +INetworkPlayer *CPlatformNetworkManagerStub::addNetworkPlayer(IQNetPlayer *pQNetPlayer) +{ + NetworkPlayerXbox *pNetworkPlayer = new NetworkPlayerXbox(pQNetPlayer); + pQNetPlayer->SetCustomDataValue((ULONG_PTR)pNetworkPlayer); + currentNetworkPlayers.push_back( pNetworkPlayer ); + return pNetworkPlayer; +} + +void CPlatformNetworkManagerStub::removeNetworkPlayer(IQNetPlayer *pQNetPlayer) +{ + INetworkPlayer *pNetworkPlayer = getNetworkPlayer(pQNetPlayer); + for( AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++ ) + { + if( *it == pNetworkPlayer ) + { + currentNetworkPlayers.erase(it); + return; + } + } +} + +INetworkPlayer *CPlatformNetworkManagerStub::getNetworkPlayer(IQNetPlayer *pQNetPlayer) +{ + return pQNetPlayer ? (INetworkPlayer *)(pQNetPlayer->GetCustomDataValue()) : NULL; +} + + +INetworkPlayer *CPlatformNetworkManagerStub::GetLocalPlayerByUserIndex(int userIndex ) +{ + return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex)); +} + +INetworkPlayer *CPlatformNetworkManagerStub::GetPlayerByIndex(int playerIndex) +{ + return getNetworkPlayer(m_pIQNet->GetPlayerByIndex(playerIndex)); +} + +INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerByXuid(PlayerUID xuid) +{ + return getNetworkPlayer( m_pIQNet->GetPlayerByXuid(xuid)) ; +} + +INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerBySmallId(unsigned char smallId) +{ + return getNetworkPlayer(m_pIQNet->GetPlayerBySmallId(smallId)); +} + +INetworkPlayer *CPlatformNetworkManagerStub::GetHostPlayer() +{ + return getNetworkPlayer(m_pIQNet->GetHostPlayer()); +} + +bool CPlatformNetworkManagerStub::IsHost() +{ + return m_pIQNet->IsHost() && !m_bHostChanged; +} + +bool CPlatformNetworkManagerStub::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo) +{ + return ( m_pIQNet->JoinGameFromInviteInfo( userIndex, userMask, pInviteInfo ) == S_OK); +} + +void CPlatformNetworkManagerStub::SetSessionTexturePackParentId( int id ) +{ + m_hostGameSessionData.texturePackParentId = id; +} + +void CPlatformNetworkManagerStub::SetSessionSubTexturePackId( int id ) +{ + m_hostGameSessionData.subTexturePackId = id; +} + +void CPlatformNetworkManagerStub::Notify(int ID, ULONG_PTR Param) +{ +} + +bool CPlatformNetworkManagerStub::IsInSession() +{ + return m_pIQNet->GetState() != QNET_STATE_IDLE; +} + +bool CPlatformNetworkManagerStub::IsInGameplay() +{ + return m_pIQNet->GetState() == QNET_STATE_GAME_PLAY; +} + +bool CPlatformNetworkManagerStub::IsReadyToPlayOrIdle() +{ + return true; +} diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h new file mode 100644 index 0000000..919efd7 --- /dev/null +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h @@ -0,0 +1,171 @@ +#pragma once +using namespace std; +#include +#include "..\..\..\Minecraft.World\C4JThread.h" +#include "NetworkPlayerInterface.h" +#include "PlatformNetworkManagerInterface.h" +#include "SessionInfo.h" + +class CPlatformNetworkManagerStub : public CPlatformNetworkManager +{ + friend class CGameNetworkManager; +public: + virtual bool Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize); + virtual void Terminate(); + virtual int GetJoiningReadyPercentage(); + virtual int CorrectErrorIDS(int IDS); + + virtual void DoWork(); + virtual int GetPlayerCount(); + virtual int GetOnlinePlayerCount(); + virtual int GetLocalPlayerMask(int playerIndex); + virtual bool AddLocalPlayerByUserIndex( int userIndex ); + virtual bool RemoveLocalPlayerByUserIndex( int userIndex ); + virtual INetworkPlayer *GetLocalPlayerByUserIndex( int userIndex ); + virtual INetworkPlayer *GetPlayerByIndex(int playerIndex); + virtual INetworkPlayer * GetPlayerByXuid(PlayerUID xuid); + virtual INetworkPlayer * GetPlayerBySmallId(unsigned char smallId); + virtual bool ShouldMessageForFullSession(); + + virtual INetworkPlayer *GetHostPlayer(); + virtual bool IsHost(); + virtual bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo); + virtual bool LeaveGame(bool bMigrateHost); + + virtual bool IsInSession(); + virtual bool IsInGameplay(); + virtual bool IsReadyToPlayOrIdle(); + virtual bool IsInStatsEnabledSession(); + virtual bool SessionHasSpace(unsigned int spaceRequired = 1); + virtual void SendInviteGUI(int quadrant); + virtual bool IsAddingPlayer(); + + virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0); + virtual int JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex ); + virtual bool SetLocalGame(bool isLocal); + virtual bool IsLocalGame() { return m_bIsOfflineGame; } + virtual void SetPrivateGame(bool isPrivate); + virtual bool IsPrivateGame() { return m_bIsPrivateGame; } + virtual bool IsLeavingGame() { return m_bLeavingGame; } + virtual void ResetLeavingGame() { m_bLeavingGame = false; } + + virtual void RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + virtual void UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + + virtual void HandleSignInChange(); + + virtual bool _RunNetworkGame(); + +private: + bool isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer); + virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom); + virtual void _HostGame(int dwUsersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0); + virtual bool _StartGame(); + + IQNet * m_pIQNet; // pointer to QNet interface + + HANDLE m_notificationListener; + + vector m_machineQNetPrimaryPlayers; // collection of players that we deem to be the main one for that system + + bool m_bLeavingGame; + bool m_bLeaveGameOnTick; + bool m_migrateHostOnLeave; + bool m_bHostChanged; + + bool m_bIsOfflineGame; + bool m_bIsPrivateGame; + int m_flagIndexSize; + + // This is only maintained by the host, and is not valid on client machines + GameSessionData m_hostGameSessionData; + CGameNetworkManager *m_pGameNetworkManager; +public: + virtual void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = NULL); + +private: + // TODO 4J Stu - Do we need to be able to have more than one of these? + void (*playerChangedCallback[XUSER_MAX_COUNT])(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + void *playerChangedCallbackParam[XUSER_MAX_COUNT]; + + static int RemovePlayerOnSocketClosedThreadProc( void* lpParam ); + virtual bool RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ); + + // Things for handling per-system flags + class PlayerFlags + { + public: + INetworkPlayer *m_pNetworkPlayer; + unsigned char *flags; + unsigned int count; + PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count); + ~PlayerFlags(); + }; + vector m_playerFlags; + void SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer); + void SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer); + void SystemFlagReset(); +public: + virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index); + virtual bool SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index); + + // For telemetry +private: + float m_lastPlayerEventTimeStart; + +public: + wstring GatherStats(); + wstring GatherRTTStats(); + +private: + vector friendsSessions[XUSER_MAX_COUNT]; + int m_searchResultsCount[XUSER_MAX_COUNT]; + int m_lastSearchStartTime[XUSER_MAX_COUNT]; + + // The results that will be filled in with the current search + XSESSION_SEARCHRESULT_HEADER *m_pSearchResults[XUSER_MAX_COUNT]; + XNQOS *m_pQoSResult[XUSER_MAX_COUNT]; + + // The results from the previous search, which are currently displayed in the game + XSESSION_SEARCHRESULT_HEADER *m_pCurrentSearchResults[XUSER_MAX_COUNT]; + XNQOS *m_pCurrentQoSResult[XUSER_MAX_COUNT]; + int m_currentSearchResultsCount[XUSER_MAX_COUNT]; + + int m_lastSearchPad; + bool m_bSearchResultsReady; + bool m_bSearchPending; + LPVOID m_pSearchParam; + void (*m_SessionsUpdatedCallback)(LPVOID pParam); + + C4JThread* m_SearchingThread; + + void TickSearch(); + void SearchForGames(); + static int SearchForGamesThreadProc( void* lpParameter ); + + void SetSearchResultsReady(int resultCount = 0); + + vectorcurrentNetworkPlayers; + INetworkPlayer *addNetworkPlayer(IQNetPlayer *pQNetPlayer); + void removeNetworkPlayer(IQNetPlayer *pQNetPlayer); + static INetworkPlayer *getNetworkPlayer(IQNetPlayer *pQNetPlayer); + + virtual void SetSessionTexturePackParentId( int id ); + virtual void SetSessionSubTexturePackId( int id ); + virtual void Notify(int ID, ULONG_PTR Param); + +public: + virtual vector *GetSessionList(int iPad, int localPlayers, bool partyOnly); + virtual bool GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession); + virtual void SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ); + virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ); + virtual void ForceFriendsSessionRefresh(); + +public: + void NotifyPlayerJoined( IQNetPlayer *pQNetPlayer ); + void NotifyPlayerLeaving( IQNetPlayer *pQNetPlayer ); + +#ifndef _XBOX + void FakeLocalPlayerJoined() { NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(0)); } +#endif +}; diff --git a/Minecraft.Client/Common/Network/SessionInfo.h b/Minecraft.Client/Common/Network/SessionInfo.h new file mode 100644 index 0000000..c4b6b9e --- /dev/null +++ b/Minecraft.Client/Common/Network/SessionInfo.h @@ -0,0 +1,127 @@ +#pragma once + +#if defined(__PS3__) || defined(__ORBIS__) +#include "..\..\Common\Network\Sony\SQRNetworkManager.h" +#endif + + +// A struct that we store in the QoS data when we are hosting the session. Max size 1020 bytes. +#ifdef _XBOX +typedef struct _GameSessionData +{ + unsigned short netVersion; // 2 bytes + char hostName[XUSER_NAME_SIZE]; // 16 bytes ( 16*1 ) + GameSessionUID hostPlayerUID; // 8 bytes ( 8*1 ) on xbox, 24 bytes on PS3 + GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS]; // 64 bytes ( 8*8 ) on xbox, 192 ( 24*8) on PS3 + char szPlayers[MINECRAFT_NET_MAX_PLAYERS][XUSER_NAME_SIZE]; // 128 bytes ( 8*16) + unsigned int m_uiGameHostSettings; // 4 bytes + unsigned int texturePackParentId; // 4 bytes + unsigned char subTexturePackId; // 1 byte + + bool isJoinable; // 1 byte + + _GameSessionData() + { + netVersion = 0; + memset(hostName,0,XUSER_NAME_SIZE); + memset(players,0,MINECRAFT_NET_MAX_PLAYERS*sizeof(players[0])); + memset(szPlayers,0,MINECRAFT_NET_MAX_PLAYERS*XUSER_NAME_SIZE); + isJoinable = true; + m_uiGameHostSettings = 0; + texturePackParentId = 0; + subTexturePackId = 0; + } +} GameSessionData; +#elif defined __PS3__ || defined __ORBIS__ || defined(__PSVITA__) +typedef struct _GameSessionData +{ + unsigned short netVersion; // 2 bytes + GameSessionUID hostPlayerUID; // 8 bytes ( 8*1 ) on xbox, 24 bytes on PS3 + GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS]; // 64 bytes ( 8*8 ) on xbox, 192 ( 24*8) on PS3 + unsigned int m_uiGameHostSettings; // 4 bytes + unsigned int texturePackParentId; // 4 bytes + unsigned char subTexturePackId; // 1 byte + + bool isJoinable; // 1 byte + + unsigned char playerCount; // 1 byte + bool isReadyToJoin; // 1 byte + + _GameSessionData() + { + netVersion = 0; + memset(players,0,MINECRAFT_NET_MAX_PLAYERS*sizeof(players[0])); + isJoinable = true; + m_uiGameHostSettings = 0; + texturePackParentId = 0; + subTexturePackId = 0; + playerCount = 0; + isReadyToJoin = false; + + } +} GameSessionData; +#else +typedef struct _GameSessionData +{ + unsigned short netVersion; + unsigned int m_uiGameHostSettings; + unsigned int texturePackParentId; + unsigned char subTexturePackId; + + bool isReadyToJoin; + bool isJoinable; + + char hostIP[64]; + int hostPort; + wchar_t hostName[XUSER_NAME_SIZE]; + unsigned char playerCount; + unsigned char maxPlayers; + + _GameSessionData() + { + netVersion = 0; + m_uiGameHostSettings = 0; + texturePackParentId = 0; + subTexturePackId = 0; + isReadyToJoin = false; + isJoinable = true; + memset(hostIP, 0, sizeof(hostIP)); + hostPort = 0; + memset(hostName, 0, sizeof(hostName)); + playerCount = 0; + maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + } +} GameSessionData; +#endif + +class FriendSessionInfo +{ +public: + SessionID sessionId; +#ifdef _XBOX + XSESSION_SEARCHRESULT searchResult; +#elif defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + SQRNetworkManager::SessionSearchResult searchResult; +#elif defined(_DURANGO) + DQRNetworkManager::SessionSearchResult searchResult; +#endif + wchar_t *displayLabel; + unsigned char displayLabelLength; + unsigned char displayLabelViewableStartIndex; + GameSessionData data; + bool hasPartyMember; + + FriendSessionInfo() + { + displayLabel = NULL; + displayLabelLength = 0; + displayLabelViewableStartIndex = 0; + hasPartyMember = false; + } + + ~FriendSessionInfo() + { + if(displayLabel!=NULL) + delete displayLabel; + } +}; diff --git a/Minecraft.Client/Common/Potion_Macros.h b/Minecraft.Client/Common/Potion_Macros.h new file mode 100644 index 0000000..29f3e03 --- /dev/null +++ b/Minecraft.Client/Common/Potion_Macros.h @@ -0,0 +1,54 @@ +#pragma once + +// 4J-JEV: +// All functional potions need bit-13 set. + +#define MASK_REGENERATION 0x2001 +#define MASK_SPEED 0x2002 +#define MASK_FIRE_RESISTANCE 0x2003 +#define MASK_POISON 0x2004 +#define MASK_INSTANTHEALTH 0x2005 +#define MASK_NIGHTVISION 0x2006 +#define MASK_INVISIBILITY 0x200E +#define MASK_WEAKNESS 0x2008 +#define MASK_STRENGTH 0x2009 +#define MASK_SLOWNESS 0x200A +#define MASK_INSTANTDAMAGE 0x200C + +#define MASK_TYPE_AWKWARD 0x0010 + +#define MASK_SPLASH 0x4000 +#define MASK_BIT13 0x2000 + +#define MASK_LEVEL2 0x0020 +#define MASK_EXTENDED 0x0040 +#define MASK_LEVEL2EXTENDED 0x0060 + +#define MACRO_POTION_IS_REGENERATION(aux) ((aux & 0x200F) == MASK_REGENERATION) +#define MACRO_POTION_IS_SPEED(aux) ((aux & 0x200F) == MASK_SPEED) +#define MACRO_POTION_IS_FIRE_RESISTANCE(aux) ((aux & 0x200F) == MASK_FIRE_RESISTANCE) +#define MACRO_POTION_IS_INSTANTHEALTH(aux) ((aux & 0x200F) == MASK_INSTANTHEALTH) +#define MACRO_POTION_IS_NIGHTVISION(aux) ((aux & 0x200F) == MASK_NIGHTVISION) +#define MACRO_POTION_IS_INVISIBILITY(aux) ((aux & 0x200F) == MASK_INVISIBILITY) +#define MACRO_POTION_IS_WEAKNESS(aux) ((aux & 0x200F) == MASK_WEAKNESS) +#define MACRO_POTION_IS_STRENGTH(aux) ((aux & 0x200F) == MASK_STRENGTH) +#define MACRO_POTION_IS_SLOWNESS(aux) ((aux & 0x200F) == MASK_SLOWNESS) +#define MACRO_POTION_IS_POISON(aux) ((aux & 0x200F) == MASK_POISON) +#define MACRO_POTION_IS_INSTANTDAMAGE(aux) ((aux & 0x200F) == MASK_INSTANTDAMAGE) + +#define MACRO_POTION_IS_SPLASH(aux) ((aux & MASK_SPLASH) == MASK_SPLASH) +#define MACRO_POTION_IS_BOTTLE(aux) ((aux & MASK_SPLASH) == 0) + +#define MACRO_POTION_IS_AKWARD(aux) ((aux & MASK_TYPE_AWKWARD) == MASK_TYPE_AWKWARD) + +#define MACRO_POTION_IS_REGULAR(aux) ((aux & (MASK_LEVEL2EXTENDED)) == 0) +#define MACRO_POTION_IS_LEVEL2(aux) ((aux & (MASK_LEVEL2 )) == MASK_LEVEL2) +#define MACRO_POTION_IS_EXTENDED(aux) ((aux & (MASK_EXTENDED)) == (MASK_EXTENDED)) +#define MACRO_POTION_IS_LEVEL2EXTENDED(aux) ((aux & (MASK_LEVEL2EXTENDED)) == (MASK_LEVEL2EXTENDED)) + + +#define MACRO_MAKEPOTION_AUXVAL(potion_type, potion_strength, potion_effect) (potion_type | potion_strength | potion_effect) + +// The potion brewing creates high aux values with redundant high bits, so use this to bring the aux val into ranges that match our macros +// 4J-JEV: 0x2000 == bit-13; Used to stop netherwart "resetting" functional potions. +#define NORMALISE_POTION_AUXVAL(aux) (aux & (MASK_BIT13 | MASK_SPLASH | 0xFF)) \ No newline at end of file diff --git a/Minecraft.Client/Common/Telemetry/TelemetryManager.cpp b/Minecraft.Client/Common/Telemetry/TelemetryManager.cpp new file mode 100644 index 0000000..4b04b19 --- /dev/null +++ b/Minecraft.Client/Common/Telemetry/TelemetryManager.cpp @@ -0,0 +1,450 @@ +#include "stdafx.h" + +#include "MultiPlayerLocalPlayer.h" + +#include "..\Minecraft.World\LevelSettings.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.World\Level.h" + +#include "TelemetryManager.h" + +#if !defined(_DURANGO) && !defined(_XBOX) + +CTelemetryManager *TelemetryManager = new CTelemetryManager(); + +#endif + +HRESULT CTelemetryManager::Init() +{ + return S_OK; +} + +HRESULT CTelemetryManager::Tick() +{ + return S_OK; +} + +HRESULT CTelemetryManager::Flush() +{ + return S_OK; +} + +bool CTelemetryManager::RecordPlayerSessionStart(int iPad) +{ + return true; +} + +bool CTelemetryManager::RecordPlayerSessionExit(int iPad, int exitStatus) +{ + return true; +} + +bool CTelemetryManager::RecordHeartBeat(int iPad) +{ + return true; +} + +bool CTelemetryManager::RecordLevelStart(int iPad, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, int numberOfLocalPlayers, int numberOfOnlinePlayers) +{ + if(iPad == ProfileManager.GetPrimaryPad() ) m_bFirstFlush = true; + + ++m_levelInstanceID; + m_fLevelStartTime[iPad] = app.getAppTime(); + + return true; +} + +bool CTelemetryManager::RecordLevelExit(int iPad, ESen_LevelExitStatus levelExitStatus) +{ + return true; +} + +bool CTelemetryManager::RecordLevelSaveOrCheckpoint(int iPad, int saveOrCheckPointID, int saveSizeInBytes) +{ + return true; +} + +bool CTelemetryManager::RecordLevelResume(int iPad, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, int numberOfLocalPlayers, int numberOfOnlinePlayers, int saveOrCheckPointID) +{ + return true; +} + +bool CTelemetryManager::RecordPauseOrInactive(int iPad) +{ + return true; +} + +bool CTelemetryManager::RecordUnpauseOrActive(int iPad) +{ + return true; +} + +bool CTelemetryManager::RecordMenuShown(int iPad, EUIScene menuID, int optionalMenuSubID) +{ + return true; +} + +bool CTelemetryManager::RecordAchievementUnlocked(int iPad, int achievementID, int achievementGamerscore) +{ + return true; +} + +bool CTelemetryManager::RecordMediaShareUpload(int iPad, ESen_MediaDestination mediaDestination, ESen_MediaType mediaType) +{ + return true; +} + +bool CTelemetryManager::RecordUpsellPresented(int iPad, ESen_UpsellID upsellId, int marketplaceOfferID) +{ + return true; +} + +bool CTelemetryManager::RecordUpsellResponded(int iPad, ESen_UpsellID upsellId, int marketplaceOfferID, ESen_UpsellOutcome upsellOutcome) +{ + return true; +} + +bool CTelemetryManager::RecordPlayerDiedOrFailed(int iPad, int lowResMapX, int lowResMapY, int lowResMapZ, int mapID, int playerWeaponID, int enemyWeaponID, ETelemetryChallenges enemyTypeID) +{ + return true; +} + +bool CTelemetryManager::RecordEnemyKilledOrOvercome(int iPad, int lowResMapX, int lowResMapY, int lowResMapZ, int mapID, int playerWeaponID, int enemyWeaponID, ETelemetryChallenges enemyTypeID) +{ + return true; +} + +bool CTelemetryManager::RecordTexturePackLoaded(int iPad, int texturePackId, bool purchased) +{ + return true; +} + +bool CTelemetryManager::RecordSkinChanged(int iPad, int dwSkinId) +{ + return true; +} + +bool CTelemetryManager::RecordBanLevel(int iPad) +{ + return true; +} + +bool CTelemetryManager::RecordUnBanLevel(int iPad) +{ + return true; +} + + + /////////////////////////////////////////////////////////////////// + // 4J-JEV: FOLLOWING LOGIC TAKEN FROM XBOX 'SentientManager.cpp' // + /////////////////////////////////////////////////////////////////// + + +/* +Number of seconds elapsed since Sentient initialize. +Title needs to track this and report it as a property. +These times will be used to create timelines and understand durations. +This should be tracked independently of saved games (restoring a save should not reset the seconds since initialize) +*/ +INT CTelemetryManager::GetSecondsSinceInitialize() +{ + return (INT)(app.getAppTime() - m_initialiseTime); +} + +/* +An in-game setting that significantly differentiates the play style of the game. +(This should be captured as an integer and correspond to mode specific to the game.) +Teams will have to provide the game mappings that correspond to the integers. +The intent is to allow teams to capture data on the highest level categories of gameplay in their game. +For example, a game mode could be the name of the specific mini game (eg: golf vs darts) or a specific multiplayer mode (eg: hoard vs beast.) ModeID = 0 means undefined or unknown. +The intent is to answer the question "How are players playing your game?" +*/ +INT CTelemetryManager::GetMode(DWORD dwUserId) +{ + INT mode = (INT)eTelem_ModeId_Undefined; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localplayers[dwUserId] != NULL && pMinecraft->localplayers[dwUserId]->level != NULL && pMinecraft->localplayers[dwUserId]->level->getLevelData() != NULL ) + { + GameType *gameType = pMinecraft->localplayers[dwUserId]->level->getLevelData()->getGameType(); + + if (gameType->isSurvival()) + { + mode = (INT)eTelem_ModeId_Survival; + } + else if (gameType->isCreative()) + { + mode = (INT)eTelem_ModeId_Creative; + } + else + { + mode = (INT)eTelem_ModeId_Undefined; + } + } + return mode; +} + +/* +Used when a title has more heirarchy required. +OptionalSubMode ID = 0 means undefined or unknown. +For titles that have sub-modes (Sports/Football). +Mode is always an indicator of "How is the player choosing to play my game?" so these do not have to be consecutive. +LevelIDs and SubLevelIDs can be reused as they will always be paired with a Mode/SubModeID, Mode should be unique - SubMode can be shared between modes. +*/ +INT CTelemetryManager::GetSubMode(DWORD dwUserId) +{ + INT subMode = (INT)eTelem_SubModeId_Undefined; + + if(Minecraft::GetInstance()->isTutorial()) + { + subMode = (INT)eTelem_SubModeId_Tutorial; + } + else + { + subMode = (INT)eTelem_SubModeId_Normal; + } + + return subMode; +} + +/* +This is a more granular view of mode, allowing teams to get a sense of the levels or maps players are playing and providing some insight into how players progress through a game. +Teams will have to provide the game mappings that correspond to the integers. +The intent is that a level is highest level at which modes can be dissected and provides an indication of player progression in a game. +The intent is that level start and ends do not occur more than every 2 minutes or so, otherwise the data reported will be difficult to understand. +Levels are unique only within a given modeID - so you can have a ModeID =1, LevelID =1 and a different ModeID=2, LevelID = 1 indicate two completely different levels. +LevelID = 0 means undefined or unknown. +*/ +INT CTelemetryManager::GetLevelId(DWORD dwUserId) +{ + INT levelId = (INT)eTelem_LevelId_Undefined; + + levelId = (INT)eTelem_LevelId_PlayerGeneratedLevel; + + return levelId; +} + +/* +Used when a title has more heirarchy required. OptionalSubLevel ID = 0 means undefined or unknown. +For titles that have sub-levels. +Level is always an indicator of "How far has the player progressed." so when possible these should be consecutive or at least monotonically increasing. +LevelIDs and SubLevelIDs can be reused as they will always be paired with a Mode/SubModeID +*/ +INT CTelemetryManager::GetSubLevelId(DWORD dwUserId) +{ + INT subLevelId = (INT)eTelem_SubLevelId_Undefined; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if(pMinecraft->localplayers[dwUserId] != NULL) + { + switch(pMinecraft->localplayers[dwUserId]->dimension) + { + case 0: + subLevelId = (INT)eTelem_SubLevelId_Overworld; + break; + case -1: + subLevelId = (INT)eTelem_SubLevelId_Nether; + break; + case 1: + subLevelId = (INT)eTelem_SubLevelId_End; + break; + }; + } + + return subLevelId; +} + +/* +Build version of the title, used to track changes in development as well as patches/title updates +Allows developer to separate out stats from different builds +*/ +INT CTelemetryManager::GetTitleBuildId() +{ + return (INT)VER_PRODUCTBUILD; +} + +/* +Generated by the game every time LevelStart or LevelResume is called. +This should be a unique ID (can be sequential) within a session. +Helps differentiate level attempts when a play plays the same mode/level - especially with aggregated stats +*/ +INT CTelemetryManager::GetLevelInstanceID() +{ + return (INT)m_levelInstanceID; +} + +/* +MultiplayerinstanceID is a title-generated value that is the same for all players in the same multiplayer session. +Link up players into a single multiplayer session ID. +*/ +INT CTelemetryManager::GetMultiplayerInstanceID() +{ + return m_multiplayerInstanceID; +} + +INT CTelemetryManager::GenerateMultiplayerInstanceId() +{ +#if defined(_DURANGO) || defined(_XBOX) + FILETIME SystemTimeAsFileTime; + GetSystemTimeAsFileTime( &SystemTimeAsFileTime ); + return *((INT *)&SystemTimeAsFileTime.dwLowDateTime); +#else + return 0; +#endif +} + +void CTelemetryManager::SetMultiplayerInstanceId(INT value) +{ + m_multiplayerInstanceID = value; +} + +/* +Indicates whether the game is being played in single or multiplayer mode and whether multiplayer is being played locally or over live. +How social is your game? How do people play it? +*/ +INT CTelemetryManager::GetSingleOrMultiplayer() +{ + INT singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Undefined; + + // Unused + //eSen_SingleOrMultiplayer_Single_Player + //eSen_SingleOrMultiplayer_Multiplayer_Live + + if(app.GetLocalPlayerCount() == 1 && g_NetworkManager.GetOnlinePlayerCount() == 0) + { + singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Single_Player; + } + else if(app.GetLocalPlayerCount() > 1 && g_NetworkManager.GetOnlinePlayerCount() == 0) + { + singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Local; + } + else if(app.GetLocalPlayerCount() == 1 && g_NetworkManager.GetOnlinePlayerCount() > 0) + { + singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Live; + } + else if(app.GetLocalPlayerCount() > 1 && g_NetworkManager.GetOnlinePlayerCount() > 0) + { + singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Both_Local_and_Live; + } + + return singleOrMultiplayer; +} + +/* +An in-game setting that differentiates the challenge imposed on the user. +Normalized to a standard 5-point scale. Are players changing the difficulty? +*/ +INT CTelemetryManager::GetDifficultyLevel(INT diff) +{ + INT difficultyLevel = (INT)eSen_DifficultyLevel_Undefined; + + switch(diff) + { + case 0: + difficultyLevel = (INT)eSen_DifficultyLevel_Easiest; + break; + case 1: + difficultyLevel = (INT)eSen_DifficultyLevel_Easier; + break; + case 2: + difficultyLevel = (INT)eSen_DifficultyLevel_Normal; + break; + case 3: + difficultyLevel = (INT)eSen_DifficultyLevel_Harder; + break; + } + + // Unused + //eSen_DifficultyLevel_Hardest = 5, + + return difficultyLevel; +} + +/* +Differentiates trial/demo from full purchased titles +Is this a full title or demo? +*/ +INT CTelemetryManager::GetLicense() +{ + INT license = eSen_License_Undefined; + + if(ProfileManager.IsFullVersion()) + { + license = (INT)eSen_License_Full_Purchased_Title; + } + else + { + license = (INT)eSen_License_Trial_or_Demo; + } + return license; +} + +/* +This is intended to capture whether players played using default control scheme or customized the control scheme. +Are players customizing your controls? +*/ +INT CTelemetryManager::GetDefaultGameControls() +{ + INT defaultGameControls = eSen_DefaultGameControls_Undefined; + + // Unused + //eSen_DefaultGameControls_Custom_controls + + defaultGameControls = eSen_DefaultGameControls_Default_controls; + + return defaultGameControls; +} + +/* +Are players changing default audio settings? +This is intended to capture whether players are playing with or without volume and whether they make changes from the default audio settings. +*/ +INT CTelemetryManager::GetAudioSettings(DWORD dwUserId) +{ + INT audioSettings = (INT)eSen_AudioSettings_Undefined; + + if(dwUserId == ProfileManager.GetPrimaryPad()) + { + BYTE volume = app.GetGameSettings(dwUserId,eGameSetting_SoundFXVolume); + + if(volume == 0) + { + audioSettings = (INT)eSen_AudioSettings_Off; + } + else if(volume == DEFAULT_VOLUME_LEVEL) + { + audioSettings = (INT)eSen_AudioSettings_On_Default; + } + else + { + audioSettings = (INT)eSen_AudioSettings_On_CustomSetting; + } + } + return audioSettings; +} + +/* +Refers to the highest level performance metric for your game. +For example, a performance metric could points earned, race time, total kills, etc. +This is entirely up to you and will help us understand how well the player performed, or how far the player progressed in the level before exiting. +How far did users progress before failing/exiting the level? +*/ +INT CTelemetryManager::GetLevelExitProgressStat1() +{ + // 4J Stu - Unused + return 0; +} + +/* +Refers to the highest level performance metric for your game. +For example, a performance metric could points earned, race time, total kills, etc. +This is entirely up to you and will help us understand how well the player performed, or how far the player progressed in the level before exiting. +How far did users progress before failing/exiting the level? +*/ +INT CTelemetryManager::GetLevelExitProgressStat2() +{ + // 4J Stu - Unused + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Telemetry/TelemetryManager.h b/Minecraft.Client/Common/Telemetry/TelemetryManager.h new file mode 100644 index 0000000..40b6c04 --- /dev/null +++ b/Minecraft.Client/Common/Telemetry/TelemetryManager.h @@ -0,0 +1,65 @@ +#pragma once + +#include "..\..\Common\UI\UIEnums.h" + +class CTelemetryManager +{ +public: + virtual HRESULT Init(); + virtual HRESULT Tick(); + virtual HRESULT Flush(); + + virtual bool RecordPlayerSessionStart(int iPad); + virtual bool RecordPlayerSessionExit(int iPad, int exitStatus); + virtual bool RecordHeartBeat(int iPad); + virtual bool RecordLevelStart(int iPad, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, int numberOfLocalPlayers, int numberOfOnlinePlayers); + virtual bool RecordLevelExit(int iPad, ESen_LevelExitStatus levelExitStatus); + virtual bool RecordLevelSaveOrCheckpoint(int iPad, int saveOrCheckPointID, int saveSizeInBytes); + virtual bool RecordLevelResume(int iPad, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, int numberOfLocalPlayers, int numberOfOnlinePlayers, int saveOrCheckPointID); + virtual bool RecordPauseOrInactive(int iPad); + virtual bool RecordUnpauseOrActive(int iPad); + virtual bool RecordMenuShown(int iPad, EUIScene menuID, int optionalMenuSubID); + virtual bool RecordAchievementUnlocked(int iPad, int achievementID, int achievementGamerscore); + virtual bool RecordMediaShareUpload(int iPad, ESen_MediaDestination mediaDestination, ESen_MediaType mediaType); + virtual bool RecordUpsellPresented(int iPad, ESen_UpsellID upsellId, int marketplaceOfferID); + virtual bool RecordUpsellResponded(int iPad, ESen_UpsellID upsellId, int marketplaceOfferID, ESen_UpsellOutcome upsellOutcome); + virtual bool RecordPlayerDiedOrFailed(int iPad, int lowResMapX, int lowResMapY, int lowResMapZ, int mapID, int playerWeaponID, int enemyWeaponID, ETelemetryChallenges enemyTypeID); + virtual bool RecordEnemyKilledOrOvercome(int iPad, int lowResMapX, int lowResMapY, int lowResMapZ, int mapID, int playerWeaponID, int enemyWeaponID, ETelemetryChallenges enemyTypeID); + virtual bool RecordTexturePackLoaded(int iPad, int texturePackId, bool purchased); + + virtual bool RecordSkinChanged(int iPad, int dwSkinId); + virtual bool RecordBanLevel(int iPad); + virtual bool RecordUnBanLevel(int iPad); + + virtual int GetMultiplayerInstanceID(); + virtual int GenerateMultiplayerInstanceId(); + virtual void SetMultiplayerInstanceId(int value); + +protected: + float m_initialiseTime; + float m_lastHeartbeat; + bool m_bFirstFlush; + + float m_fLevelStartTime[XUSER_MAX_COUNT]; + + INT m_multiplayerInstanceID; + DWORD m_levelInstanceID; + + // Helper functions to get the various common settings + INT GetSecondsSinceInitialize(); + INT GetMode(DWORD dwUserId); + INT GetSubMode(DWORD dwUserId); + INT GetLevelId(DWORD dwUserId); + INT GetSubLevelId(DWORD dwUserId); + INT GetTitleBuildId(); + INT GetLevelInstanceID(); + INT GetSingleOrMultiplayer(); + INT GetDifficultyLevel(INT diff); + INT GetLicense(); + INT GetDefaultGameControls(); + INT GetAudioSettings(DWORD dwUserId); + INT GetLevelExitProgressStat1(); + INT GetLevelExitProgressStat2(); +}; + +extern CTelemetryManager *TelemetryManager; \ No newline at end of file diff --git a/Minecraft.Client/Common/Trial/TrialLevel.mcs b/Minecraft.Client/Common/Trial/TrialLevel.mcs new file mode 100644 index 0000000..99b1738 Binary files /dev/null and b/Minecraft.Client/Common/Trial/TrialLevel.mcs differ diff --git a/Minecraft.Client/Common/Trial/TrialMode.cpp b/Minecraft.Client/Common/Trial/TrialMode.cpp new file mode 100644 index 0000000..e814913 --- /dev/null +++ b/Minecraft.Client/Common/Trial/TrialMode.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "TrialMode.h" +#include "..\Tutorial\FullTutorial.h" + +TrialMode::TrialMode(int iPad, Minecraft *minecraft, ClientConnection *connection) + : FullTutorialMode(iPad, minecraft, connection) +{ + tutorial = new FullTutorial(iPad, true); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Trial/TrialMode.h b/Minecraft.Client/Common/Trial/TrialMode.h new file mode 100644 index 0000000..a1034ac --- /dev/null +++ b/Minecraft.Client/Common/Trial/TrialMode.h @@ -0,0 +1,10 @@ +#pragma once +#include "..\Tutorial\FullTutorialMode.h" + +class TrialMode : public FullTutorialMode +{ +public: + TrialMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + + virtual bool isImplemented() { return true; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp b/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp new file mode 100644 index 0000000..f133d60 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaConstraint.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "AreaConstraint.h" +#include "..\..\..\Minecraft.World\AABB.h" + +AreaConstraint::AreaConstraint( int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool contains /*= true*/, bool restrictsMovement /*=true*/ ) + : TutorialConstraint( descriptionId ) +{ + messageArea = AABB::newPermanent(x0+2, y0+2, z0+2, x1-2, y1-2, z1-2); + movementArea = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + m_restrictsMovement = restrictsMovement; +} + +AreaConstraint::~AreaConstraint() +{ + delete messageArea; + delete movementArea; +} + +bool AreaConstraint::isConstraintSatisfied(int iPad) +{ + Minecraft *minecraft = Minecraft::GetInstance(); + return messageArea->contains( minecraft->localplayers[iPad]->getPos(1) ) == contains; +} + +bool AreaConstraint::isConstraintRestrictive(int iPad) +{ + return m_restrictsMovement; +} + + +bool AreaConstraint::canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) +{ + if(!m_restrictsMovement) return true; + + Vec3 *targetPos = Vec3::newTemp(xt, yt, zt); + Minecraft *minecraft = Minecraft::GetInstance(); + + if(movementArea->contains( targetPos ) == contains) + { + return true; + } + Vec3 *origPos = Vec3::newTemp(xo, yo, zo); + + double currDist = origPos->distanceTo(movementArea); + double targetDist = targetPos->distanceTo(movementArea); + return targetDist < currDist; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaConstraint.h b/Minecraft.Client/Common/Tutorial/AreaConstraint.h new file mode 100644 index 0000000..f98945e --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaConstraint.h @@ -0,0 +1,24 @@ +#pragma once + +#include "TutorialConstraint.h" + +class AABB; + +class AreaConstraint : public TutorialConstraint +{ +private: + AABB *movementArea; + AABB *messageArea; + bool contains; // If true we must stay in this area, if false must stay out of this area + bool m_restrictsMovement; + +public: + virtual ConstraintType getType() { return e_ConstraintArea; } + + AreaConstraint( int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool contains = true, bool restrictsMovement =true ); + ~AreaConstraint(); + + virtual bool isConstraintSatisfied(int iPad); + virtual bool isConstraintRestrictive(int iPad); + virtual bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaHint.cpp b/Minecraft.Client/Common/Tutorial/AreaHint.cpp new file mode 100644 index 0000000..8b711c8 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaHint.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "AreaHint.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "Tutorial.h" + +AreaHint::AreaHint(eTutorial_Hint id, Tutorial *tutorial, eTutorial_State displayState, eTutorial_State completeState, + int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool allowFade /*= false*/, bool contains /*= true*/ ) + : TutorialHint( id, tutorial, descriptionId, e_Hint_Area, allowFade ) +{ + area = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + + m_displayState = displayState; + m_completeState = completeState; +} + +AreaHint::~AreaHint() +{ + delete area; +} + +int AreaHint::tick() +{ + Minecraft *minecraft = Minecraft::GetInstance(); + if( (m_displayState == e_Tutorial_State_Any || m_tutorial->getCurrentState() == m_displayState) && + m_hintNeeded && + area->contains( minecraft->player->getPos(1) ) == contains ) + { + if( m_completeState == e_Tutorial_State_None ) + { + m_hintNeeded = false; + } + else if ( m_tutorial->isStateCompleted( m_completeState ) ) + { + m_hintNeeded = false; + return -1; + } + + return m_descriptionId; + } + else + { + return -1; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaHint.h b/Minecraft.Client/Common/Tutorial/AreaHint.h new file mode 100644 index 0000000..12ef897 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaHint.h @@ -0,0 +1,25 @@ +#pragma once + +#include "TutorialHint.h" + +class AABB; + +class AreaHint : public TutorialHint +{ +private: + AABB *area; + bool contains; // If true we must stay in this area, if false must stay out of this area + + // Only display the hint if the game is in this state + eTutorial_State m_displayState; + + // Only display the hint if this state is not completed + eTutorial_State m_completeState; + +public: + AreaHint(eTutorial_Hint id, Tutorial *tutorial, eTutorial_State displayState, eTutorial_State completeState, + int descriptionId, double x0, double y0, double z0, double x1, double y1, double z1, bool allowFade = true, bool contains = true ); + ~AreaHint(); + + virtual int tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaTask.cpp b/Minecraft.Client/Common/Tutorial/AreaTask.cpp new file mode 100644 index 0000000..de29ab1 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaTask.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "AreaTask.h" + +AreaTask::AreaTask(eTutorial_State state, Tutorial *tutorial, vector *inConstraints, int descriptionId, EAreaTaskCompletionStates completionState) + : TutorialTask( tutorial, descriptionId, false, inConstraints, false, false, false ) +{ + m_tutorialState = state; + if(m_tutorialState == e_Tutorial_State_Gameplay) + { + enableConstraints(true); + } + m_completionState = completionState; +} + +bool AreaTask::isCompleted() +{ + if(bIsCompleted) return true; + + bool complete = false; + switch(m_completionState) + { + case eAreaTaskCompletion_CompleteOnConstraintsSatisfied: + { + bool allSatisfied = true; + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + if(!constraint->isConstraintSatisfied(tutorial->getPad())) + { + allSatisfied = false; + break; + } + } + complete = allSatisfied; + } + break; + case eAreaTaskCompletion_CompleteOnActivation: + complete = bHasBeenActivated; + break; + }; + bIsCompleted = complete; + return complete; +} + +void AreaTask::setAsCurrentTask(bool active) +{ + TutorialTask::setAsCurrentTask(active); + + if(m_completionState == eAreaTaskCompletion_CompleteOnConstraintsSatisfied) + { + enableConstraints(active); + } +} + +void AreaTask::onStateChange(eTutorial_State newState) +{ + if(m_completionState == eAreaTaskCompletion_CompleteOnActivation) + { + if(m_tutorialState == newState) + { + enableConstraints(true); + } + else if(m_tutorialState != e_Tutorial_State_Gameplay) + { + //enableConstraints(false); + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/AreaTask.h b/Minecraft.Client/Common/Tutorial/AreaTask.h new file mode 100644 index 0000000..0d20bd7 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/AreaTask.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// A task that creates an maintains an area constraint until it is activated +class AreaTask : public TutorialTask +{ +public: + enum EAreaTaskCompletionStates + { + eAreaTaskCompletion_CompleteOnActivation, + eAreaTaskCompletion_CompleteOnConstraintsSatisfied, + }; +private: + EAreaTaskCompletionStates m_completionState; + eTutorial_State m_tutorialState; +public: + AreaTask(eTutorial_State state, Tutorial *tutorial, vector *inConstraints, int descriptionId = -1, EAreaTaskCompletionStates completionState = eAreaTaskCompletion_CompleteOnActivation); + virtual bool isCompleted(); + virtual void setAsCurrentTask(bool active = true); + virtual void onStateChange(eTutorial_State newState); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp new file mode 100644 index 0000000..f01db84 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" + +#include "Tutorial.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "ChangeStateConstraint.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +ChangeStateConstraint::ChangeStateConstraint( Tutorial *tutorial, eTutorial_State targetState, eTutorial_State sourceStates[], DWORD sourceStatesCount, + double x0, double y0, double z0, double x1, double y1, double z1, bool contains /*= true*/, bool changeGameMode /*= false*/, GameType *targetGameMode /*= 0*/ ) + : TutorialConstraint( -1 ) +{ + movementArea = AABB::newPermanent(x0, y0, z0, x1, y1, z1); + + this->contains = contains; + + m_changeGameMode = changeGameMode; + m_targetGameMode = targetGameMode; + m_changedFromGameMode = 0; + + m_tutorial = tutorial; + m_targetState = targetState; + m_sourceStatesCount = sourceStatesCount; + + m_bHasChanged = false; + m_changedFromState = e_Tutorial_State_None; + + m_bComplete = false; + + m_sourceStates = new eTutorial_State [m_sourceStatesCount]; + for(unsigned int i=0;i0) delete [] m_sourceStates; +} + +void ChangeStateConstraint::tick(int iPad) +{ + if(m_bComplete) return; + + if(m_tutorial->isStateCompleted(m_targetState)) + { + Minecraft *minecraft = Minecraft::GetInstance(); + if(m_changeGameMode) + { + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_changedFromGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + m_bComplete = true; + return; + } + + bool inASourceState = false; + Minecraft *minecraft = Minecraft::GetInstance(); + for(DWORD i = 0; i < m_sourceStatesCount; ++i) + { + if(m_sourceStates[i] == m_tutorial->getCurrentState()) + { + inASourceState = true; + break; + } + } + if( !m_bHasChanged && inASourceState && movementArea->contains( minecraft->localplayers[iPad]->getPos(1) ) == contains ) + { + m_bHasChanged = true; + m_changedFromState = m_tutorial->getCurrentState(); + m_tutorial->changeTutorialState(m_targetState); + + if(m_changeGameMode) + { + if(minecraft->localgameModes[iPad] != NULL) + { + m_changedFromGameMode = minecraft->localplayers[iPad]->abilities.instabuild ? GameType::CREATIVE : GameType::SURVIVAL; + + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_targetGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + } + } + else if( m_bHasChanged && movementArea->contains( minecraft->localplayers[iPad]->getPos(1) ) != contains ) + { + m_bHasChanged = false; + m_tutorial->changeTutorialState(m_changedFromState); + + if(m_changeGameMode) + { + unsigned int playerPrivs = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + Player::setPlayerGamePrivilege(playerPrivs,Player::ePlayerGamePrivilege_CreativeMode,m_changedFromGameMode == GameType::CREATIVE); + + unsigned int originalPrivileges = minecraft->localplayers[iPad]->getAllPlayerGamePrivileges(); + if(originalPrivileges != playerPrivs) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = minecraft->localplayers[iPad]; + if(player != NULL && player->connection && player->connection->getNetworkPlayer() != NULL) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( player->connection->getNetworkPlayer()->GetSmallId(), -1, playerPrivs) ) ); + } + } + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h new file mode 100644 index 0000000..2156870 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChangeStateConstraint.h @@ -0,0 +1,37 @@ +#pragma once + +#include "TutorialEnum.h" +#include "TutorialConstraint.h" + +class AABB; +class Tutorial; +class GameType; + +class ChangeStateConstraint : public TutorialConstraint +{ +private: + AABB *movementArea; + bool contains; // If true we must stay in this area, if false must stay out of this area + bool m_changeGameMode; + GameType *m_targetGameMode; + GameType *m_changedFromGameMode; + + eTutorial_State m_targetState; + eTutorial_State *m_sourceStates; + DWORD m_sourceStatesCount; + + bool m_bHasChanged; + eTutorial_State m_changedFromState; + + bool m_bComplete; + + Tutorial *m_tutorial; + +public: + virtual ConstraintType getType() { return e_ConstraintChangeState; } + + ChangeStateConstraint( Tutorial *tutorial, eTutorial_State targetState, eTutorial_State sourceStates[], DWORD sourceStatesCount, double x0, double y0, double z0, double x1, double y1, double z1, bool contains = true, bool changeGameMode = false, GameType *targetGameMode = NULL ); + ~ChangeStateConstraint(); + + virtual void tick(int iPad); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp new file mode 100644 index 0000000..c03166b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.cpp @@ -0,0 +1,135 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "ChoiceTask.h" +#include "..\..\..\Minecraft.World\Material.h" + +ChoiceTask::ChoiceTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, + int iConfirmMapping /*= 0*/, int iCancelMapping /*= 0*/, + eTutorial_CompletionAction cancelAction /*= e_Tutorial_Completion_None*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) + : TutorialTask( tutorial, descriptionId, false, NULL, true, false, false ) +{ + if(requiresUserInput == true) + { + constraints.push_back( new InputConstraint( iConfirmMapping ) ); + constraints.push_back( new InputConstraint( iCancelMapping ) ); + } + m_iConfirmMapping = iConfirmMapping; + m_iCancelMapping = iCancelMapping; + m_bConfirmMappingComplete = false; + m_bCancelMappingComplete = false; + + m_cancelAction = cancelAction; + + m_promptId = promptId; + tutorial->addMessage( m_promptId ); + + m_eTelemetryEvent = telemetryEvent; +} + +bool ChoiceTask::isCompleted() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_bConfirmMappingComplete || m_bCancelMappingComplete ) + { + sendTelemetry(); + enableConstraints(false, true); + return true; + } + + if(ui.GetMenuDisplayed(tutorial->getPad())) + { + // If a menu is displayed, then we use the handleUIInput to complete the task + } + else + { + // If the player is under water then allow all keypresses so they can jump out + if( pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water) ) return false; + + if(!m_bConfirmMappingComplete && InputManager.GetValue(pMinecraft->player->GetXboxPad(), m_iConfirmMapping) > 0 ) + { + m_bConfirmMappingComplete = true; + } + if(!m_bCancelMappingComplete && InputManager.GetValue(pMinecraft->player->GetXboxPad(), m_iCancelMapping) > 0 ) + { + m_bCancelMappingComplete = true; + } + } + + if(m_bConfirmMappingComplete || m_bCancelMappingComplete) + { + sendTelemetry(); + enableConstraints(false, true); + } + return m_bConfirmMappingComplete || m_bCancelMappingComplete; +} + +eTutorial_CompletionAction ChoiceTask::getCompletionAction() +{ + if(m_bCancelMappingComplete) + { + return m_cancelAction; + } + else + { + return e_Tutorial_Completion_None; + } +} + +int ChoiceTask::getPromptId() +{ + if( m_bShownForMinimumTime ) + return m_promptId; + else + return -1; +} + +void ChoiceTask::setAsCurrentTask(bool active /*= true*/) +{ + enableConstraints( active ); + TutorialTask::setAsCurrentTask(active); +} + +void ChoiceTask::handleUIInput(int iAction) +{ + if(bHasBeenActivated && m_bShownForMinimumTime) + { + if( iAction == m_iConfirmMapping ) + { + m_bConfirmMappingComplete = true; + } + else if(iAction == m_iCancelMapping ) + { + m_bCancelMappingComplete = true; + } + } +} + +void ChoiceTask::sendTelemetry() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_eTelemetryEvent != eTelemetryChallenges_Unknown ) + { + bool firstPlay = true; + // We only store first play for some of the events + switch(m_eTelemetryEvent) + { + case eTelemetryTutorial_TrialStart: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_TrialStart ); + tutorial->setCompleted( eTutorial_Telemetry_TrialStart ); + break; + case eTelemetryTutorial_Halfway: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_Halfway ); + tutorial->setCompleted( eTutorial_Telemetry_Halfway ); + break; + }; + + TelemetryManager->RecordEnemyKilledOrOvercome(pMinecraft->player->GetXboxPad(), 0, 0, 0, 0, 0, 0, m_eTelemetryEvent); + } +} diff --git a/Minecraft.Client/Common/Tutorial/ChoiceTask.h b/Minecraft.Client/Common/Tutorial/ChoiceTask.h new file mode 100644 index 0000000..79c2ba4 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ChoiceTask.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages with a choice +class ChoiceTask : public TutorialTask +{ +private: + int m_iConfirmMapping, m_iCancelMapping; + bool m_bConfirmMappingComplete, m_bCancelMappingComplete; + eTutorial_CompletionAction m_cancelAction; + + ETelemetryChallenges m_eTelemetryEvent; + + bool CompletionMaskIsValid(); +public: + ChoiceTask(Tutorial *tutorial, int descriptionId, int promptId = -1, bool requiresUserInput = false, int iConfirmMapping = 0, int iCancelMapping = 0, eTutorial_CompletionAction cancelAction = e_Tutorial_Completion_None, ETelemetryChallenges telemetryEvent = eTelemetryChallenges_Unknown); + virtual bool isCompleted(); + virtual eTutorial_CompletionAction getCompletionAction(); + virtual int getPromptId(); + virtual void setAsCurrentTask(bool active = true); + virtual void handleUIInput(int iAction); + +private: + void sendTelemetry(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp new file mode 100644 index 0000000..43b2f7f --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "CompleteUsingItemTask.h" + +CompleteUsingItemTask::CompleteUsingItemTask(Tutorial *tutorial, int descriptionId, int itemIds[], unsigned int itemIdsLength, bool enablePreCompletion) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL) +{ + m_iValidItemsA= new int [itemIdsLength]; + for(int i=0;i item) +{ + if(!hasBeenActivated() && !isPreCompletionEnabled()) return; + for(int i=0;iid == m_iValidItemsA[i] ) + { + bIsCompleted = true; + break; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h new file mode 100644 index 0000000..a905bea --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CompleteUsingItemTask.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +class CompleteUsingItemTask : public TutorialTask +{ +private: + int *m_iValidItemsA; + int m_iValidItemsCount; + bool completed; + +public: + CompleteUsingItemTask(Tutorial *tutorial, int descriptionId, int itemIds[], unsigned int itemIdsLength, bool enablePreCompletion = false); + virtual ~CompleteUsingItemTask(); + virtual bool isCompleted(); + virtual void completeUsingItem(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ControllerTask.cpp b/Minecraft.Client/Common/Tutorial/ControllerTask.cpp new file mode 100644 index 0000000..c5fe071 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ControllerTask.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "ControllerTask.h" + +ControllerTask::ControllerTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, bool showMinimumTime, + int mappings[], unsigned int mappingsLength, int iCompletionMaskA[], int iCompletionMaskACount, int iSouthpawMappings[], unsigned int uiSouthpawMappingsCount) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL, showMinimumTime ) +{ + for(unsigned int i = 0; i < mappingsLength; ++i) + { + constraints.push_back( new InputConstraint( mappings[i] ) ); + completedMappings[mappings[i]] = false; + } + if(uiSouthpawMappingsCount > 0 ) m_bHasSouthpaw = true; + for(unsigned int i = 0; i < uiSouthpawMappingsCount; ++i) + { + southpawCompletedMappings[iSouthpawMappings[i]] = false; + } + + m_iCompletionMaskA= new int [iCompletionMaskACount]; + for(int i=0;iplayer->GetXboxPad(),eGameSetting_ControlSouthPaw)) + { + for(AUTO_VAR(it, southpawCompletedMappings.begin()); it != southpawCompletedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + // TODO Use a different pad + if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + m_uiCompletionMask|=1<player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + m_uiCompletionMask|=1< completedMappings; + unordered_map southpawCompletedMappings; + bool m_bHasSouthpaw; + unsigned int m_uiCompletionMask; + int *m_iCompletionMaskA; + int m_iCompletionMaskACount; + bool CompletionMaskIsValid(); +public: + ControllerTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, bool showMinimumTime, + int mappings[], unsigned int mappingsLength, int iCompletionMaskA[]=NULL, int iCompletionMaskACount=0, int iSouthpawMappings[]=NULL, unsigned int uiSouthpawMappingsCount=0); + ~ControllerTask(); + virtual bool isCompleted(); + virtual void setAsCurrentTask(bool active = true); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CraftTask.cpp b/Minecraft.Client/Common/Tutorial/CraftTask.cpp new file mode 100644 index 0000000..6749d03 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CraftTask.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "CraftTask.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +CraftTask::CraftTask( int itemId, int auxValue, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion /*= true*/, vector *inConstraints /*= NULL*/, + bool bShowMinimumTime /*=false*/, bool bAllowFade /*=true*/, bool m_bTaskReminders /*=true*/ ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_quantity( quantity ), + m_count( 0 ) +{ + m_numItems = 1; + m_items = new int[1]; + m_items[0] = itemId; + m_auxValues = new int[1]; + m_auxValues[0] = auxValue; +} + +CraftTask::CraftTask( int *items, int *auxValues, int numItems, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion /*= true*/, vector *inConstraints /*= NULL*/, + bool bShowMinimumTime /*=false*/, bool bAllowFade /*=true*/, bool m_bTaskReminders /*=true*/ ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_quantity( quantity ), + m_count( 0 ) +{ + m_numItems = numItems; + m_items = new int[m_numItems]; + m_auxValues = new int[m_numItems]; + + for(int i = 0; i < m_numItems; ++i) + { + m_items[i] = items[i]; + m_auxValues[i] = auxValues[i]; + } +} + +CraftTask::~CraftTask() +{ + delete[] m_items; + delete[] m_auxValues; +} + +void CraftTask::onCrafted(shared_ptr item) +{ +#ifndef _CONTENT_PACKAGE + wprintf(L"CraftTask::onCrafted - %ls\n", item->toString().c_str() ); +#endif + bool itemFound = false; + for(int i = 0; i < m_numItems; ++i) + { + if(m_items[i] == item->id && (m_auxValues[i] == -1 || m_auxValues[i] == item->getAuxValue())) + { + itemFound = true; + break; + } + } + + if(itemFound) + { + ++m_count; + } + if( m_count >= m_quantity) + { + bIsCompleted = true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/CraftTask.h b/Minecraft.Client/Common/Tutorial/CraftTask.h new file mode 100644 index 0000000..1496f07 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/CraftTask.h @@ -0,0 +1,25 @@ +#pragma once +#include "TutorialTask.h" + +class CraftTask : public TutorialTask +{ +public: + CraftTask( int itemId, int auxValue, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + CraftTask( int *items, int *auxValues, int numItems, int quantity, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + + ~CraftTask(); + + virtual bool isCompleted() { return bIsCompleted; } + virtual void onCrafted(shared_ptr item); + +private: + int *m_items; + int *m_auxValues; + int m_numItems; + int m_quantity; + int m_count; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp b/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp new file mode 100644 index 0000000..1367f41 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/DiggerItemHint.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.h" +#include "Tutorial.h" +#include "DiggerItemHint.h" + + +DiggerItemHint::DiggerItemHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int items[], unsigned int itemsLength) + : TutorialHint(id, tutorial, descriptionId, e_Hint_DiggerItem) +{ + m_iItemsCount = itemsLength; + + m_iItems= new int [m_iItemsCount]; + for(unsigned int i=0;iaddMessage(IDS_TUTORIAL_HINT_ATTACK_WITH_TOOL, true); +} + +int DiggerItemHint::startDestroyBlock(shared_ptr item, Tile *tile) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + float speed = item->getDestroySpeed(tile); + if(speed == 1) + { + // Display hint + return m_descriptionId; + } + } + } + return -1; +} + +int DiggerItemHint::attack(shared_ptr item, shared_ptr entity) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + // It's also possible that we could hit TileEntities (eg falling sand) so don't want to give this hint then + if( dynamic_pointer_cast( entity ) != NULL ) + { + return IDS_TUTORIAL_HINT_ATTACK_WITH_TOOL; + } + else + { + return -1; + } + } + } + return -1; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/DiggerItemHint.h b/Minecraft.Client/Common/Tutorial/DiggerItemHint.h new file mode 100644 index 0000000..cb71742 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/DiggerItemHint.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TutorialHint.h" + +class DiggerItem; +class Level; + +class DiggerItemHint : public TutorialHint +{ +private: + int *m_iItems; + unsigned int m_iItemsCount; + +public: + DiggerItemHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int items[], unsigned int itemsLength); + virtual int startDestroyBlock(shared_ptr item, Tile *tile); + virtual int attack(shared_ptr item, shared_ptr entity); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp b/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp new file mode 100644 index 0000000..5f1b5b2 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/EffectChangedTask.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "EffectChangedTask.h" + +EffectChangedTask::EffectChangedTask(Tutorial *tutorial, int descriptionId, MobEffect *effect, bool apply, + bool enablePreCompletion, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders ) + : TutorialTask(tutorial,descriptionId,enablePreCompletion,NULL,bShowMinimumTime,bAllowFade,bTaskReminders) +{ + m_effect = effect; + m_apply = apply; +} + +bool EffectChangedTask::isCompleted() +{ + return bIsCompleted; +} + +void EffectChangedTask::onEffectChanged(MobEffect *effect, bool bRemoved /*=false*/) +{ + if(effect == m_effect) + { + if(m_apply == !bRemoved) + { + bIsCompleted = true; + } + else + { + bIsCompleted = false; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/EffectChangedTask.h b/Minecraft.Client/Common/Tutorial/EffectChangedTask.h new file mode 100644 index 0000000..23563f3 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/EffectChangedTask.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class MobEffect; + +class EffectChangedTask : public TutorialTask +{ +private: + MobEffect *m_effect; + bool m_apply; + +public: + EffectChangedTask(Tutorial *tutorial, int descriptionId, MobEffect *effect, bool apply = true, + bool enablePreCompletion = true, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + virtual bool isCompleted(); + virtual void onEffectChanged(MobEffect *effect, bool bRemoved=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorial.cpp b/Minecraft.Client/Common/Tutorial/FullTutorial.cpp new file mode 100644 index 0000000..123ab9d --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorial.cpp @@ -0,0 +1,653 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\GameRules\ConsoleGameRules.h" +#include "DiggerItemHint.h" +#include "TutorialTasks.h" +#include "AreaHint.h" +#include "FullTutorial.h" +#include "TutorialConstraints.h" + +FullTutorial::FullTutorial(int iPad, bool isTrial /*= false*/) + : Tutorial(iPad, true) +{ + m_isTrial = isTrial; + m_freezeTime = true; + m_progressFlags = 0; + + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + m_completedStates[i] = false; + } + + addMessage(IDS_TUTORIAL_COMPLETED); + + /* + * + * + * GAMEPLAY + * + */ + // START OF BASIC TUTORIAL + if( m_isTrial ) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_START_TUTORIAL, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + else + { +#ifdef _XBOX + if(getCompleted(eTutorial_Telemetry_Halfway) && !isStateCompleted(e_Tutorial_State_Redstone_And_Piston) ) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_NEW_FEATURES_CHOICE, IDS_TUTORIAL_PROMPT_NEW_FEATURES_CHOICE, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); +#else + if(getCompleted(eTutorial_Telemetry_Halfway)) + { + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_START_TUTORIAL, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_TrialStart) ); + } + else + { + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_OVERVIEW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } +#endif + } + + int lookMappings[] = {MINECRAFT_ACTION_LOOK_UP, MINECRAFT_ACTION_LOOK_DOWN, MINECRAFT_ACTION_LOOK_LEFT, MINECRAFT_ACTION_LOOK_RIGHT}; + int moveMappings[] = {MINECRAFT_ACTION_FORWARD, MINECRAFT_ACTION_BACKWARD, MINECRAFT_ACTION_LEFT, MINECRAFT_ACTION_RIGHT}; + int iLookCompletionMaskA[]= { 10, // 1010 + 9, // 1001 + 6, // 0110 + 5 // 0101 + }; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_LOOK, false, false, lookMappings, 4, iLookCompletionMaskA, 4, moveMappings, 4) ); + + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_MOVE, false, false, moveMappings, 4, iLookCompletionMaskA, 4, lookMappings, 4) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_SPRINT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + int jumpMappings[] = {MINECRAFT_ACTION_JUMP}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_JUMP, false, true, jumpMappings, 1) ); + + int mineMappings[] = {MINECRAFT_ACTION_ACTION}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_MINE, false, true, mineMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new PickupTask( Tile::treeTrunk_Id, 4, -1, this, IDS_TUTORIAL_TASK_CHOP_WOOD ) ); + + int scrollMappings[] = {MINECRAFT_ACTION_LEFT_SCROLL,MINECRAFT_ACTION_RIGHT_SCROLL}; + //int scrollMappings[] = {ACTION_MENU_LEFT_SCROLL,ACTION_MENU_RIGHT_SCROLL}; + int iScrollCompletionMaskA[]= { 2, // 10 + 1};// 01 + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_SCROLL, false, false, scrollMappings, 2,iScrollCompletionMaskA,2) ); + + int invMappings[] = {MINECRAFT_ACTION_INVENTORY}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_INVENTORY, false, false, invMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_Inventory_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_DEPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_HEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + // While they should only eat the item we give them, includ the ability to complete this task with different items + int foodItems[] = {Item::mushroomStew_Id, Item::apple_Id, Item::bread_Id, Item::porkChop_raw_Id, Item::porkChop_cooked_Id, + Item::apple_gold_Id, Item::fish_raw_Id, Item::fish_cooked_Id, Item::cookie_Id, Item::beef_cooked_Id, + Item::beef_raw_Id, Item::chicken_cooked_Id, Item::chicken_raw_Id, Item::melon_Id, Item::rotten_flesh_Id}; + addTask(e_Tutorial_State_Gameplay, new CompleteUsingItemTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_EAT_STEAK, foodItems, 15, true) ); + + int crftMappings[] = {MINECRAFT_ACTION_CRAFTING}; + addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_CRAFTING, false, false, crftMappings, 1) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_2_X_2_Crafting, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_2x2Crafting_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::wood_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_PLANKS) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::workBench_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CRAFTING_TABLE) ); + + //int useMappings[] = {MINECRAFT_ACTION_USE}; + //addTask(e_Tutorial_State_Gameplay, new ControllerTask( this, IDS_TUTORIAL_TASK_USE, false, false, useMappings, 1) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_USE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new UseItemTask( Tile::workBench_Id, this, IDS_TUTORIAL_TASK_PLACE_WORKBENCH, true ) ); + + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_NIGHT_DANGER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_NEARBY_SHELTER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, new InfoTask(this, IDS_TUTORIAL_TASK_COLLECT_RESOURCES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + // END OF BASIC TUTORIAL + + addTask(e_Tutorial_State_Gameplay, new ChoiceTask(this, IDS_TUTORIAL_TASK_BASIC_COMPLETE, IDS_TUTORIAL_PROMPT_BASIC_COMPLETE, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_Halfway) ); + + // START OF FULL TUTORIAL + + addTask(e_Tutorial_State_Gameplay, new UseTileTask( Tile::workBench_Id, this, IDS_TUTORIAL_TASK_OPEN_WORKBENCH, false ) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_3_X_3_Crafting, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_3x3Crafting_Menu, this) ); + + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::stick->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_STICKS) ); + + int shovelItems[] = {Item::shovel_wood->id, Item::shovel_stone->id, Item::shovel_iron->id, Item::shovel_gold->id, Item::shovel_diamond->id}; + int shovelAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( shovelItems, shovelAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_SHOVEL) ); + + int hatchetItems[] = {Item::hatchet_wood->id, Item::hatchet_stone->id, Item::hatchet_iron->id, Item::hatchet_gold->id, Item::hatchet_diamond->id}; + int hatchetAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( hatchetItems, hatchetAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_HATCHET) ); + + int pickaxeItems[] = {Item::pickAxe_wood->id, Item::pickAxe_stone->id, Item::pickAxe_iron->id, Item::pickAxe_gold->id, Item::pickAxe_diamond->id}; + int pickaxeAuxVals[] = {-1,-1,-1,-1,-1}; + addTask(e_Tutorial_State_Gameplay, new CraftTask( pickaxeItems, pickaxeAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_PICKAXE) ); + + addTask(e_Tutorial_State_Gameplay, new PickupTask( Tile::stoneBrick_Id, 8, -1, this, IDS_TUTORIAL_TASK_MINE_STONE ) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::furnace_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_FURNACE ) ); + addTask(e_Tutorial_State_Gameplay, new UseTileTask(Tile::furnace_Id, this, IDS_TUTORIAL_TASK_PLACE_AND_OPEN_FURNACE) ); + + addTask(e_Tutorial_State_Gameplay, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_USE_FURNACE, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Gameplay, new StateChangeTask( e_Tutorial_State_Furnace_Menu, this) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::coal->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CHARCOAL) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::glass_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_GLASS) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Item::door_wood->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_DOOR) ); + addTask(e_Tutorial_State_Gameplay, new UseItemTask(Item::door_wood->id, this, IDS_TUTORIAL_TASK_PLACE_DOOR) ); + addTask(e_Tutorial_State_Gameplay, new CraftTask( Tile::torch_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_TORCH) ); + + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"tutorialArea"); + if(area != NULL) + { + vector *areaConstraints = new vector(); + areaConstraints->push_back( new AreaConstraint( IDS_TUTORIAL_CONSTRAINT_TUTORIAL_AREA, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + addTask(e_Tutorial_State_Gameplay, new AreaTask(e_Tutorial_State_Gameplay,this, areaConstraints) ); + } + } + + // This MUST be the last task in the e_Tutorial_State_Gameplay state. Some of the earlier tasks will skip to the last + // task when complete, and this is the one that we want the player to see. + ProcedureCompoundTask *finalTask = new ProcedureCompoundTask( this ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_COMPLETED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A, eTelemetryTutorial_Complete) ); + // 4J Stu - Remove this string as it refers to things that don't exist in the current tutorial world! + //finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_FEATURES_IN_THIS_AREA, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_FEATURES_OUTSIDE_THIS_AREA, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + finalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_COMPLETED_EXPLORE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Gameplay, finalTask); + // END OF FULL TUTORIAL + + + /* + * + * + * INVENTORY + * + */ + // Some tasks already added in the super class ctor + addTask(e_Tutorial_State_Inventory_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + addTask(e_Tutorial_State_Inventory_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_INV_EXIT, -1, false, ACTION_MENU_B) ); + + /* + * + * + * CRAFTING + * + */ + // Some tasks already added in the super class ctor + + addTask(e_Tutorial_State_2x2Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + // To block progress + addTask(e_Tutorial_State_2x2Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_2_X_2_Crafting, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_2x2Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + addTask(e_Tutorial_State_2x2Crafting_Menu, new CraftTask( Tile::wood_Id, -1, 1, this, IDS_TUTORIAL_TASK_CRAFT_CREATE_PLANKS) ); + + ProcedureCompoundTask *workbenchCompound = new ProcedureCompoundTask( this ); + workbenchCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_STRUCTURES, Recipy::eGroupType_Structure) ); + workbenchCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_CRAFTING_TABLE, Tile::workBench_Id) ); + workbenchCompound->AddTask( new CraftTask( Tile::workBench_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_CRAFTING_TABLE) ); + addTask(e_Tutorial_State_2x2Crafting_Menu, workbenchCompound ); + addTask(e_Tutorial_State_2x2Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_EXIT_AND_PLACE_TABLE, -1, false, ACTION_MENU_B) ); + + // 3x3 Crafting + addTask(e_Tutorial_State_3x3Crafting_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_3_X_3_Crafting, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Item::stick->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_STICKS) ); + + ProcedureCompoundTask *shovelCompound = new ProcedureCompoundTask( this ); + shovelCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_TOOLS, Recipy::eGroupType_Tool) ); + shovelCompound->AddTask( new XuiCraftingTask( this, IDS_TUTORIAL_TASK_CRAFT_SELECT_WOODEN_SHOVEL, Item::shovel_wood->id) ); + shovelCompound->AddTask( new CraftTask( shovelItems, shovelAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_SHOVEL) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, shovelCompound ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( hatchetItems, hatchetAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_HATCHET) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( pickaxeItems, pickaxeAuxVals, 5, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_PICKAXE) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_TOOLS_BUILT, -1, false, ACTION_MENU_B) ); + + // To block progress + addTask(e_Tutorial_State_3x3Crafting_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Tile::furnace_Id, -1, 1, this, IDS_TUTORIAL_TASK_CRAFT_CREATE_FURNACE) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_EXIT_AND_PLACE_FURNACE, -1, false, ACTION_MENU_B) ); + + // No need to block here, as it's fine if the player wants to do this out of order + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Item::door_wood->id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_WOODEN_DOOR) ); + addTask(e_Tutorial_State_3x3Crafting_Menu, new CraftTask( Tile::torch_Id, -1, 1, this, IDS_TUTORIAL_TASK_CREATE_TORCH) ); + + /* + * + * + * FURNACE + * + */ + // Some tasks already added in the super class ctor + + addTask(e_Tutorial_State_Furnace_Menu, new FullTutorialActiveTask( this, e_Tutorial_Completion_Complete_State) ); + + // Blocking + addTask(e_Tutorial_State_Furnace_Menu, new ProgressFlagTask( &m_progressFlags, FULL_TUTORIAL_PROGRESS_USE_FURNACE, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + addTask(e_Tutorial_State_Furnace_Menu, new CraftTask( Item::coal->id, -1, 1, this, IDS_TUTORIAL_TASK_FURNACE_CREATE_CHARCOAL) ); + addTask(e_Tutorial_State_Furnace_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_CHARCOAL_USES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Furnace_Menu, new CraftTask( Tile::glass_Id, -1, 1, this, IDS_TUTORIAL_TASK_FURNACE_CREATE_GLASS) ); + + /* + * + * + * BREWING + * + */ + + // To block progress + addTask(e_Tutorial_State_Brewing_Menu, new ProgressFlagTask( &m_progressFlags, EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND, ProgressFlagTask::e_Progress_Flag_On, this ) ); + + int potionItems[] = {Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id,Item::potion_Id}; + int potionAuxVals[] = { MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE), + MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE) + }; + addTask(e_Tutorial_State_Brewing_Menu, new CraftTask( potionItems, potionAuxVals, 8, 1, this, IDS_TUTORIAL_TASK_BREWING_MENU_CREATE_FIRE_POTION) ); + addTask(e_Tutorial_State_Brewing_Menu, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXIT, -1, false, ACTION_MENU_B) ); + + /* + * + * + * MINECART + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"minecartArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Riding_Minecart, IDS_TUTORIAL_HINT_MINECART, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * BOAT + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"boatArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Riding_Boat, IDS_TUTORIAL_HINT_BOAT, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * FISHING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"fishingArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_Fishing, IDS_TUTORIAL_HINT_FISHING, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1 ) ); + } + } + + /* + * + * + * PISTON - SELF-REPAIRING BRIDGE + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"pistonBridgeArea"); + if(area != NULL) + { + addHint(e_Tutorial_State_Gameplay, new AreaHint(e_Tutorial_Hint_Always_On, this, e_Tutorial_State_Gameplay, e_Tutorial_State_None, IDS_TUTORIAL_HINT_PISTON_SELF_REPAIRING_BRIDGE, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1, true ) ); + } + } + + /* + * + * + * PISTON - PISTON AND REDSTONE CIRCUITS + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"pistonArea"); + if(area != NULL) + { + eTutorial_State redstoneAndPistonStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Redstone_And_Piston, redstoneAndPistonStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Redstone_And_Piston, new ChoiceTask(this, IDS_TUTORIAL_REDSTONE_OVERVIEW, IDS_TUTORIAL_PROMPT_REDSTONE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Redstone_And_Pistons) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_POWER_SOURCES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_TRIPWIRE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_POWER_SOURCES_POSITION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_DUST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_REDSTONE_REPEATER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_PISTONS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Redstone_And_Piston, new InfoTask(this, IDS_TUTORIAL_TASK_TRY_IT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * PORTAL + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"portalArea"); + if(area != NULL) + { + eTutorial_State portalStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Portal, portalStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Portal, new ChoiceTask(this, IDS_TUTORIAL_PORTAL_OVERVIEW, IDS_TUTORIAL_PROMPT_PORTAL_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Portal) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_BUILD_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_ACTIVATE_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_USE_PORTAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_NETHER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Portal, new InfoTask(this, IDS_TUTORIAL_TASK_NETHER_FAST_TRAVEL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * CREATIVE + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"creativeArea"); + if(area != NULL) + { + eTutorial_State creativeStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_CreativeMode, creativeStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1,true,true,GameType::CREATIVE) ); + + addTask(e_Tutorial_State_CreativeMode, new ChoiceTask(this, IDS_TUTORIAL_CREATIVE_OVERVIEW, IDS_TUTORIAL_PROMPT_CREATIVE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Jump_To_Last_Task, eTelemetryTutorial_CreativeMode) ); + addTask(e_Tutorial_State_CreativeMode, new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_MODE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_CreativeMode, new InfoTask(this, IDS_TUTORIAL_TASK_FLY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + int crftMappings[] = {MINECRAFT_ACTION_CRAFTING}; + addTask(e_Tutorial_State_CreativeMode, new ControllerTask( this, IDS_TUTORIAL_TASK_OPEN_CREATIVE_INVENTORY, false, false, crftMappings, 1) ); + addTask(e_Tutorial_State_CreativeMode, new StateChangeTask( e_Tutorial_State_Creative_Inventory_Menu, this) ); + + // This last task ensures that the player is still in creative mode until they exit the area (but could skip the previous instructional stuff) + ProcedureCompoundTask *creativeFinalTask = new ProcedureCompoundTask( this ); + + AABB *exitArea = app.getGameRuleDefinitions()->getNamedArea(L"creativeExitArea"); + if(exitArea != NULL) + { + vector *creativeExitAreaConstraints = new vector(); + creativeExitAreaConstraints->push_back( new AreaConstraint( -1, exitArea->x0,exitArea->y0,exitArea->z0,exitArea->x1,exitArea->y1,exitArea->z1,true,false) ); + creativeFinalTask->AddTask( new AreaTask(e_Tutorial_State_CreativeMode, this, creativeExitAreaConstraints,IDS_TUTORIAL_TASK_CREATIVE_EXIT,AreaTask::eAreaTaskCompletion_CompleteOnConstraintsSatisfied) ); + } + + vector *creativeAreaConstraints = new vector(); + creativeAreaConstraints->push_back( new AreaConstraint( IDS_TUTORIAL_CONSTRAINT_TUTORIAL_AREA, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + creativeFinalTask->AddTask( new AreaTask(e_Tutorial_State_CreativeMode, this, creativeAreaConstraints) ); + + creativeFinalTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + addTask(e_Tutorial_State_CreativeMode,creativeFinalTask); + } + } + + /* + * + * + * BREWING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"brewingArea"); + if(area != NULL) + { + eTutorial_State brewingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Brewing, brewingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Brewing, new ChoiceTask(this, IDS_TUTORIAL_TASK_BREWING_OVERVIEW, IDS_TUTORIAL_PROMPT_BREWING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Brewing) ); + + ProcedureCompoundTask *fillWaterBottleTask = new ProcedureCompoundTask( this ); + fillWaterBottleTask->AddTask( new PickupTask( Item::glassBottle_Id, 1, -1, this, IDS_TUTORIAL_TASK_BREWING_GET_GLASS_BOTTLE ) ); + fillWaterBottleTask->AddTask( new PickupTask( Item::potion_Id, 1, 0, this, IDS_TUTORIAL_TASK_BREWING_FILL_GLASS_BOTTLE ) ); + addTask(e_Tutorial_State_Brewing, fillWaterBottleTask); + + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_FILL_CAULDRON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + + addTask(e_Tutorial_State_Brewing, new ProgressFlagTask( &m_progressFlags, EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND, ProgressFlagTask::e_Progress_Set_Flag, this ) ); + addTask(e_Tutorial_State_Brewing, new CraftTask( potionItems, potionAuxVals, 8, 1, this, IDS_TUTORIAL_TASK_BREWING_CREATE_FIRE_POTION) ); + + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_USE_POTION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Brewing, new EffectChangedTask(this, IDS_TUTORIAL_TASK_BREWING_DRINK_FIRE_POTION, MobEffect::fireResistance) ); + addTask(e_Tutorial_State_Brewing, new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_USE_EFFECTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ENCHANTING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"enchantingArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Enchanting, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Enchanting, new ChoiceTask(this, IDS_TUTORIAL_TASK_ENCHANTING_OVERVIEW, IDS_TUTORIAL_PROMPT_ENCHANTING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Enchanting) ); + + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOOKS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOOKCASES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_EXPERIENCE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_BOTTLE_O_ENCHANTING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting, new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ANVIL + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"anvilArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Anvil, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Anvil, new ChoiceTask(this, IDS_TUTORIAL_TASK_ANVIL_OVERVIEW, IDS_TUTORIAL_PROMPT_ANVIL_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Anvil) ); + + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_ENCHANTED_BOOKS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_COST2, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_RENAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil, new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * TRADING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"tradingArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Trading, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Trading, new ChoiceTask(this, IDS_TUTORIAL_TASK_TRADING_OVERVIEW, IDS_TUTORIAL_PROMPT_TRADING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Trading) ); + + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_INCREASE_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_DECREASE_TRADES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading, new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_USE_CHESTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * ENDERCHEST + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"enderchestArea"); + if(area != NULL) + { + eTutorial_State enchantingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Enderchests, enchantingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Enderchests, new ChoiceTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_OVERVIEW, IDS_TUTORIAL_PROMPT_ENDERCHEST_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Enderchest) ); + + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_SUMMARY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_PLAYERS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enderchests, new InfoTask(this, IDS_TUTORIAL_TASK_ENDERCHEST_FUNCTION, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * FARMING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"farmingArea"); + if(area != NULL) + { + eTutorial_State farmingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Farming, farmingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Farming, new ChoiceTask(this, IDS_TUTORIAL_FARMING_OVERVIEW, IDS_TUTORIAL_PROMPT_FARMING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Farming) ); + + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_SEEDS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_FARMLAND, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_WHEAT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_PUMPKIN_AND_MELON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_CARROTS_AND_POTATOES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_SUGARCANE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_CACTUS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_MUSHROOM, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_BONEMEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Farming, new InfoTask(this, IDS_TUTORIAL_TASK_FARMING_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * BREEDING + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"breedingArea"); + if(area != NULL) + { + eTutorial_State breedingStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Breeding, breedingStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Breeding, new ChoiceTask(this, IDS_TUTORIAL_BREEDING_OVERVIEW, IDS_TUTORIAL_PROMPT_BREEDING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Breeding) ); + + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FEED_FOOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_BABY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_DELAY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_FOLLOW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_RIDING_PIGS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_WOLF_TAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_WOLF_COLLAR, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Breeding, new InfoTask(this, IDS_TUTORIAL_TASK_BREEDING_COMPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + } + + /* + * + * + * SNOW AND IRON GOLEM + * + */ + if(app.getGameRuleDefinitions() != NULL) + { + AABB *area = app.getGameRuleDefinitions()->getNamedArea(L"golemArea"); + if(area != NULL) + { + eTutorial_State golemStates[] = {e_Tutorial_State_Gameplay}; + AddGlobalConstraint( new ChangeStateConstraint(this, e_Tutorial_State_Golem, golemStates, 1, area->x0,area->y0,area->z0,area->x1,area->y1,area->z1) ); + + addTask(e_Tutorial_State_Golem, new ChoiceTask(this, IDS_TUTORIAL_GOLEM_OVERVIEW, IDS_TUTORIAL_PROMPT_GOLEM_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Golem) ); + + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_PUMPKIN, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_SNOW, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_IRON, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Golem, new InfoTask(this, IDS_TUTORIAL_TASK_GOLEM_IRON_VILLAGE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + + } + } + +} + +// 4J Stu - All tutorials are onby default in the full tutorial whether the player has previously completed them or not +bool FullTutorial::isStateCompleted( eTutorial_State state ) +{ + return m_completedStates[state]; +} + +void FullTutorial::setStateCompleted( eTutorial_State state ) +{ + m_completedStates[state] = true; + Tutorial::setStateCompleted(state); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorial.h b/Minecraft.Client/Common/Tutorial/FullTutorial.h new file mode 100644 index 0000000..da2641d --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorial.h @@ -0,0 +1,21 @@ +#pragma once +#include "Tutorial.h" + +#define FULL_TUTORIAL_PROGRESS_2_X_2_Crafting 1 +#define FULL_TUTORIAL_PROGRESS_3_X_3_Crafting 2 +#define FULL_TUTORIAL_PROGRESS_CRAFT_FURNACE 4 +#define FULL_TUTORIAL_PROGRESS_USE_FURNACE 8 +#define EXTENDED_TUTORIAL_PROGRESS_USE_BREWING_STAND 16 + +class FullTutorial : public Tutorial +{ +private: + bool m_isTrial; + char m_progressFlags; + bool m_completedStates[e_Tutorial_State_Max]; +public: + FullTutorial(int iPad, bool isTrial = false); + + virtual bool isStateCompleted( eTutorial_State state ); + virtual void setStateCompleted( eTutorial_State state ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp new file mode 100644 index 0000000..54985d2 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "FullTutorialActiveTask.h" + +FullTutorialActiveTask::FullTutorialActiveTask(Tutorial *tutorial, eTutorial_CompletionAction completeAction /*= e_Tutorial_Completion_None*/) + : TutorialTask( tutorial, -1, false, NULL, false, false, false ) +{ + m_completeAction = completeAction; +} + +bool FullTutorialActiveTask::isCompleted() +{ + return bHasBeenActivated; +} + +eTutorial_CompletionAction FullTutorialActiveTask::getCompletionAction() +{ + if( tutorial->m_fullTutorialComplete ) + { + return m_completeAction; + } + else + { + return e_Tutorial_Completion_None; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h new file mode 100644 index 0000000..5aa0561 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialActiveTask.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages with a choice +class FullTutorialActiveTask : public TutorialTask +{ +private: + eTutorial_CompletionAction m_completeAction; + + bool CompletionMaskIsValid(); +public: + FullTutorialActiveTask(Tutorial *tutorial, eTutorial_CompletionAction completeAction = e_Tutorial_Completion_None); + virtual bool isCompleted(); + virtual eTutorial_CompletionAction getCompletionAction(); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp b/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp new file mode 100644 index 0000000..a5ee85b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialMode.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "FullTutorial.h" +#include "FullTutorialMode.h" + +FullTutorialMode::FullTutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection) + : TutorialMode(iPad, minecraft, connection) +{ + tutorial = new FullTutorial( iPad ); + minecraft->playerStartedTutorial( iPad ); +} + +bool FullTutorialMode::isTutorial() +{ + return !tutorial->m_fullTutorialComplete; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/FullTutorialMode.h b/Minecraft.Client/Common/Tutorial/FullTutorialMode.h new file mode 100644 index 0000000..ce6f181 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/FullTutorialMode.h @@ -0,0 +1,12 @@ +#pragma once +#include "TutorialMode.h" + +class FullTutorialMode : public TutorialMode +{ +public: + FullTutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + + virtual bool isImplemented() { return true; } + + virtual bool isTutorial(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.cpp b/Minecraft.Client/Common/Tutorial/InfoTask.cpp new file mode 100644 index 0000000..5330841 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InfoTask.cpp @@ -0,0 +1,137 @@ +#include "stdafx.h" +#include +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "InfoTask.h" +#include "..\..\..\Minecraft.World\Material.h" + +InfoTask::InfoTask(Tutorial *tutorial, int descriptionId, int promptId /*= -1*/, bool requiresUserInput /*= false*/, + int iMapping /*= 0*/, ETelemetryChallenges telemetryEvent /*= eTelemetryTutorial_NoEvent*/) + : TutorialTask( tutorial, descriptionId, false, NULL, true, false, false ) +{ + if(requiresUserInput == true) + { + constraints.push_back( new InputConstraint( iMapping ) ); + } + completedMappings[iMapping]=false; + + m_promptId = promptId; + tutorial->addMessage( m_promptId ); + + m_eTelemetryEvent = telemetryEvent; +} + +bool InfoTask::isCompleted() +{ + if( bIsCompleted ) + return true; + + if( tutorial->m_hintDisplayed ) + return false; + + if( !bHasBeenActivated || !m_bShownForMinimumTime ) + return false; + + bool bAllComplete = true; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // If the player is under water then allow all keypresses so they can jump out + if( pMinecraft->localplayers[tutorial->getPad()]->isUnderLiquid(Material::water) ) return false; + + if(ui.GetMenuDisplayed(tutorial->getPad())) + { + // If a menu is displayed, then we use the handleUIInput to complete the task + bAllComplete = true; + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + bAllComplete = false; + break; + } + } + } + else + { + int iCurrent=0; + + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + bool current = (*it).second; + if(!current) + { + if( InputManager.GetValue(pMinecraft->player->GetXboxPad(), (*it).first) > 0 ) + { + (*it).second = true; + bAllComplete=true; + } + else + { + bAllComplete = false; + } + } + iCurrent++; + } + } + + if(bAllComplete==true) + { + sendTelemetry(); + enableConstraints(false, true); + } + bIsCompleted = bAllComplete; + return bAllComplete; +} + +int InfoTask::getPromptId() +{ + if( m_bShownForMinimumTime ) + return m_promptId; + else + return -1; +} + +void InfoTask::setAsCurrentTask(bool active /*= true*/) +{ + enableConstraints( active ); + TutorialTask::setAsCurrentTask(active); +} + +void InfoTask::handleUIInput(int iAction) +{ + if(bHasBeenActivated) + { + for(AUTO_VAR(it, completedMappings.begin()); it != completedMappings.end(); ++it) + { + if( iAction == (*it).first ) + { + (*it).second = true; + } + } + } +} + + +void InfoTask::sendTelemetry() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( m_eTelemetryEvent != eTelemetryChallenges_Unknown ) + { + bool firstPlay = true; + // We only store first play for some of the events + switch(m_eTelemetryEvent) + { + case eTelemetryTutorial_Complete: + firstPlay = !tutorial->getCompleted( eTutorial_Telemetry_Complete ); + tutorial->setCompleted( eTutorial_Telemetry_Complete ); + break; + }; + TelemetryManager->RecordEnemyKilledOrOvercome(pMinecraft->player->GetXboxPad(), 0, 0, 0, 0, 0, 0, m_eTelemetryEvent); + } +} diff --git a/Minecraft.Client/Common/Tutorial/InfoTask.h b/Minecraft.Client/Common/Tutorial/InfoTask.h new file mode 100644 index 0000000..e072038 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InfoTask.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +// Information messages +class InfoTask : public TutorialTask +{ +private: + unordered_map completedMappings; + + ETelemetryChallenges m_eTelemetryEvent; + + bool CompletionMaskIsValid(); +public: + InfoTask(Tutorial *tutorial, int descriptionId, int promptId = -1, bool requiresUserInput = false, int iMapping = 0, ETelemetryChallenges telemetryEvent = eTelemetryChallenges_Unknown); + virtual bool isCompleted(); + virtual int getPromptId(); + virtual void setAsCurrentTask(bool active = true); + virtual void handleUIInput(int iAction); + +private: + void sendTelemetry(); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/InputConstraint.cpp b/Minecraft.Client/Common/Tutorial/InputConstraint.cpp new file mode 100644 index 0000000..a26d3eb --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InputConstraint.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "InputConstraint.h" + +bool InputConstraint::isMappingConstrained(int iPad, int mapping) +{ + // If it's a menu button, then we ignore all inputs + if((m_inputMapping == mapping) || (mapping < ACTION_MAX_MENU)) + { + return true; + } + + // Otherwise see if they map to the same actual button + unsigned char layoutMapping = InputManager.GetJoypadMapVal( iPad ); + + // 4J HEG - Replaced the equivalance test with bitwise AND, important in some mapping configurations + // (e.g. when comparing two action map values and one has extra buttons mapped) + return (InputManager.GetGameJoypadMaps(layoutMapping,m_inputMapping) & InputManager.GetGameJoypadMaps(layoutMapping,mapping)) > 0; +} diff --git a/Minecraft.Client/Common/Tutorial/InputConstraint.h b/Minecraft.Client/Common/Tutorial/InputConstraint.h new file mode 100644 index 0000000..3d6bee6 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/InputConstraint.h @@ -0,0 +1,15 @@ +#pragma once + +#include "TutorialConstraint.h" + +class InputConstraint : public TutorialConstraint +{ +private: + int m_inputMapping; // Should be one of the EControllerActions +public: + virtual ConstraintType getType() { return e_ConstraintInput; } + + InputConstraint(int mapping) : TutorialConstraint(-1), m_inputMapping( mapping ) {} + + virtual bool isMappingConstrained(int iPad, int mapping); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp new file mode 100644 index 0000000..3b4680c --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "LookAtEntityHint.h" + + +LookAtEntityHint::LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type) + : TutorialHint(id, tutorial, descriptionId, e_Hint_LookAtEntity) +{ + m_type = type; + m_titleId = titleId; +} + +bool LookAtEntityHint::onLookAtEntity(eINSTANCEOF type) +{ + if(m_type == type) + { + // Display hint + Tutorial::PopupMessageDetails *message = new Tutorial::PopupMessageDetails(); + message->m_messageId = m_descriptionId; + message->m_titleId = m_titleId; + message->m_delay = true; + return m_tutorial->setMessage(this, message); + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h new file mode 100644 index 0000000..9913669 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtEntityHint.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "..\..\..\Minecraft.World\Class.h" +#include "TutorialHint.h" + +class ItemInstance; + +class LookAtEntityHint : public TutorialHint +{ +private: + eINSTANCEOF m_type; + int m_titleId; + +public: + LookAtEntityHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, int titleId, eINSTANCEOF type); + ~LookAtEntityHint(); + + virtual bool onLookAtEntity(eINSTANCEOF type); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp b/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp new file mode 100644 index 0000000..0a953a7 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtTileHint.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "Tutorial.h" +#include "LookAtTileHint.h" + + +LookAtTileHint::LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[], unsigned int tilesLength, int iconOverride /*= -1*/, int iData /* = -1 */, int iDataOverride /*= -1*/) + : TutorialHint(id, tutorial, -1, e_Hint_LookAtTile) +{ + m_iTilesCount = tilesLength; + + m_iTiles= new int [m_iTilesCount]; + for(unsigned int i=0;i 0 && id < 256 && (m_iData == -1 || m_iData == iData) ) + { + bool itemFound = false; + for(unsigned int i=0;im_delay = true; + if( m_iconOverride >= 0 ) + { + message->m_icon = m_iconOverride; + } + else if(m_iconOverride == -2) + { + message->m_icon = TUTORIAL_NO_ICON; + } + else + { + message->m_icon = id; + if(m_iDataOverride > -1) + { + message->m_iAuxVal = m_iDataOverride; + } + else + { + message->m_iAuxVal = iData; + } + } + message->m_messageId = Item::items[id]->getUseDescriptionId(); + message->m_titleId = Item::items[id]->getDescriptionId(message->m_iAuxVal); + return m_tutorial->setMessage(this, message); + } + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/LookAtTileHint.h b/Minecraft.Client/Common/Tutorial/LookAtTileHint.h new file mode 100644 index 0000000..34ec1b9 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/LookAtTileHint.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "TutorialHint.h" + +class ItemInstance; + +class LookAtTileHint : public TutorialHint +{ +private: + int *m_iTiles; + unsigned int m_iTilesCount; + int m_iconOverride; + int m_iData; + int m_iDataOverride; + +public: + LookAtTileHint(eTutorial_Hint id, Tutorial *tutorial, int tiles[], unsigned int tilesLength, int iconOverride = -1, int iData=-1, int iDataOverride = -1); + ~LookAtTileHint(); + + virtual bool onLookAt(int id, int iData=0); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/PickupTask.cpp b/Minecraft.Client/Common/Tutorial/PickupTask.cpp new file mode 100644 index 0000000..00bc9d1 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/PickupTask.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "PickupTask.h" + +void PickupTask::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + if(item->id == m_itemId) + { + if(m_auxValue == -1 && invItemCountAnyAux >= m_quantity) + { + bIsCompleted = true; + } + else if( m_auxValue == item->getAuxValue() && invItemCountThisAux >= m_quantity) + { + bIsCompleted = true; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/PickupTask.h b/Minecraft.Client/Common/Tutorial/PickupTask.h new file mode 100644 index 0000000..68e1d47 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/PickupTask.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; +#include "TutorialTask.h" + +class ItemInstance; + +class PickupTask : public TutorialTask +{ +public: + PickupTask( int itemId, unsigned int quantity, int auxValue, + Tutorial *tutorial, int descriptionId, bool enablePreCompletion = true, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_itemId( itemId), + m_quantity( quantity ), + m_auxValue( auxValue ) + {} + + virtual bool isCompleted() { return bIsCompleted; } + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + +private: + int m_itemId; + unsigned int m_quantity; + int m_auxValue; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp new file mode 100644 index 0000000..8603f76 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.cpp @@ -0,0 +1,263 @@ +#include "stdafx.h" +#include "ProcedureCompoundTask.h" + +ProcedureCompoundTask::~ProcedureCompoundTask() +{ + for(AUTO_VAR(it, m_taskSequence.begin()); it < m_taskSequence.end(); ++it) + { + delete (*it); + } +} + +void ProcedureCompoundTask::AddTask(TutorialTask *task) +{ + if(task != NULL) + { + m_taskSequence.push_back(task); + } +} + +int ProcedureCompoundTask::getDescriptionId() +{ + if(bIsCompleted) + return -1; + + // Return the id of the first task not completed + int descriptionId = -1; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + task->setAsCurrentTask(true); + descriptionId = task->getDescriptionId(); + break; + } + else if(task->getCompletionAction() == e_Tutorial_Completion_Complete_State) + { + bIsCompleted = true; + break; + } + } + return descriptionId; +} + +int ProcedureCompoundTask::getPromptId() +{ + if(bIsCompleted) + return -1; + + // Return the id of the first task not completed + int promptId = -1; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + promptId = task->getPromptId(); + break; + } + } + return promptId; +} + +bool ProcedureCompoundTask::isCompleted() +{ + // Return whether all tasks are completed + + bool allCompleted = true; + bool isCurrentTask = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + + if(allCompleted && isCurrentTask) + { + if(task->isCompleted()) + { + if(task->getCompletionAction() == e_Tutorial_Completion_Complete_State) + { + allCompleted = true; + break; + } + } + else + { + task->setAsCurrentTask(true); + allCompleted = false; + isCurrentTask = false; + } + } + else if (!allCompleted) + { + task->setAsCurrentTask(false); + } + } + + if(allCompleted) + { + //Disable all constraints + itEnd = m_taskSequence.end(); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->enableConstraints(false); + } + } + bIsCompleted = allCompleted; + return allCompleted; +} + +void ProcedureCompoundTask::onCrafted(shared_ptr item) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onCrafted(item); + } +} + +void ProcedureCompoundTask::handleUIInput(int iAction) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->handleUIInput(iAction); + } +} + + +void ProcedureCompoundTask::setAsCurrentTask(bool active /*= true*/) +{ + bool allCompleted = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(allCompleted && !task->isCompleted()) + { + task->setAsCurrentTask(true); + allCompleted = false; + } + else if (!allCompleted) + { + task->setAsCurrentTask(false); + } + } +} + +bool ProcedureCompoundTask::ShowMinimumTime() +{ + if(bIsCompleted) + return false; + + bool showMinimumTime = false; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + showMinimumTime = task->ShowMinimumTime(); + break; + } + } + return showMinimumTime; +} + +bool ProcedureCompoundTask::hasBeenActivated() +{ + if(bIsCompleted) + return true; + + bool hasBeenActivated = false; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + hasBeenActivated = task->hasBeenActivated(); + break; + } + } + return hasBeenActivated; +} + +void ProcedureCompoundTask::setShownForMinimumTime() +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + task->setShownForMinimumTime(); + break; + } + } +} + +bool ProcedureCompoundTask::AllowFade() +{ + if(bIsCompleted) + return true; + + bool allowFade = true; + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + if(!task->isCompleted()) + { + allowFade = task->AllowFade(); + break; + } + } + return allowFade; +} + +void ProcedureCompoundTask::useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->useItemOn(level, item, x, y, z, bTestUseOnly); + } +} + +void ProcedureCompoundTask::useItem(shared_ptr item, bool bTestUseOnly) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->useItem(item, bTestUseOnly); + } +} + +void ProcedureCompoundTask::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onTake(item, invItemCountAnyAux, invItemCountThisAux); + } +} + +void ProcedureCompoundTask::onStateChange(eTutorial_State newState) +{ + AUTO_VAR(itEnd, m_taskSequence.end()); + for(AUTO_VAR(it, m_taskSequence.begin()); it < itEnd; ++it) + { + TutorialTask *task = *it; + task->onStateChange(newState); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h new file mode 100644 index 0000000..36b3279 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProcedureCompoundTask.h @@ -0,0 +1,36 @@ +#pragma once + +#include "TutorialTask.h" + +// A tutorial task that requires each of the task to be completed in order until the last one is complete. +// If an earlier task that was complete is now not complete then it's hint should be shown. +class ProcedureCompoundTask : public TutorialTask +{ +public: + ProcedureCompoundTask(Tutorial *tutorial ) + : TutorialTask(tutorial, -1, false, NULL, false, true, false ) + {} + + ~ProcedureCompoundTask(); + + void AddTask(TutorialTask *task); + + virtual int getDescriptionId(); + virtual int getPromptId(); + virtual bool isCompleted(); + virtual void onCrafted(shared_ptr item); + virtual void handleUIInput(int iAction); + virtual void setAsCurrentTask(bool active = true); + virtual bool ShowMinimumTime(); + virtual bool hasBeenActivated(); + virtual void setShownForMinimumTime(); + virtual bool AllowFade(); + + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false); + virtual void useItem(shared_ptr item, bool bTestUseOnly=false); + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + virtual void onStateChange(eTutorial_State newState); + +private: + vector m_taskSequence; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp new file mode 100644 index 0000000..ea224ca --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "ProgressFlagTask.h" + +bool ProgressFlagTask::isCompleted() +{ + switch( m_type ) + { + case e_Progress_Set_Flag: + (*flags) |= m_mask; + bIsCompleted = true; + break; + case e_Progress_Flag_On: + bIsCompleted = ((*flags) & m_mask) == m_mask; + break; + } + return bIsCompleted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h new file mode 100644 index 0000000..b96e1bc --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/ProgressFlagTask.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; +#include "Tutorial.h" +#include "TutorialTask.h" + +class ProgressFlagTask : public TutorialTask +{ +public: + enum EProgressFlagType + { + e_Progress_Set_Flag, + e_Progress_Flag_On, + }; +private: + char *flags; // Not a member of this object + char m_mask; + EProgressFlagType m_type; +public: + ProgressFlagTask(char *flags, char mask, EProgressFlagType type, Tutorial *tutorial ) : + TutorialTask(tutorial, -1, false, NULL ), + flags( flags ), m_mask( mask ), m_type( type ) + {} + + virtual bool isCompleted(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StatTask.cpp b/Minecraft.Client/Common/Tutorial/StatTask.cpp new file mode 100644 index 0000000..5f8b215 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StatTask.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\LocalPlayer.h" +#include "..\..\StatsCounter.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "StatTask.h" + +StatTask::StatTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, Stat *stat, int variance /*= 1*/) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, NULL ) +{ + this->stat = stat; + + Minecraft *minecraft = Minecraft::GetInstance(); + targetValue = minecraft->stats[ProfileManager.GetPrimaryPad()]->getTotalValue( stat ) + variance; +} + +bool StatTask::isCompleted() +{ + if( bIsCompleted ) + return true; + + Minecraft *minecraft = Minecraft::GetInstance(); + bIsCompleted = minecraft->stats[ProfileManager.GetPrimaryPad()]->getTotalValue( stat ) >= (unsigned int)targetValue; + return bIsCompleted; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StatTask.h b/Minecraft.Client/Common/Tutorial/StatTask.h new file mode 100644 index 0000000..ba38f00 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StatTask.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Stat; + +// 4J Stu - Tutorial tasks that can use the current stat trackin code. This is things like blocks mined/items crafted. +class StatTask : public TutorialTask +{ +private: + Stat *stat; + int targetValue; + +public: + StatTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, Stat *stat, int variance = 1); + virtual bool isCompleted(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/StateChangeTask.h b/Minecraft.Client/Common/Tutorial/StateChangeTask.h new file mode 100644 index 0000000..fb9e639 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/StateChangeTask.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; +#include "Tutorial.h" +#include "TutorialTask.h" + +class StateChangeTask : public TutorialTask +{ +private: + eTutorial_State m_state; +public: + StateChangeTask(eTutorial_State state, + Tutorial *tutorial, int descriptionId = -1, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) : + TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_state( state ) + {} + + virtual bool isCompleted() { return bIsCompleted; } + + virtual void onStateChange(eTutorial_State newState) + { + if(newState == m_state) + { + bIsCompleted = true; + } + } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp b/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp new file mode 100644 index 0000000..a1a5c37 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TakeItemHint.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "Tutorial.h" +#include "TakeItemHint.h" + + +TakeItemHint::TakeItemHint(eTutorial_Hint id, Tutorial *tutorial, int items[], unsigned int itemsLength) + : TutorialHint(id, tutorial, -1, e_Hint_TakeItem) +{ + m_iItemsCount = itemsLength; + + m_iItems= new int [m_iItemsCount]; + for(unsigned int i=0;i item) +{ + if(item != NULL) + { + bool itemFound = false; + for(unsigned int i=0;iid == m_iItems[i]) + { + itemFound = true; + break; + } + } + if(itemFound) + { + // Display hint + Tutorial::PopupMessageDetails *message = new Tutorial::PopupMessageDetails(); + message->m_messageId = item->getUseDescriptionId(); + message->m_titleId = item->getDescriptionId(); + message->m_icon = item->id; + message->m_delay = true; + return m_tutorial->setMessage(this, message); + } + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TakeItemHint.h b/Minecraft.Client/Common/Tutorial/TakeItemHint.h new file mode 100644 index 0000000..f001d4c --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TakeItemHint.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "TutorialHint.h" + +class ItemInstance; + +class TakeItemHint : public TutorialHint +{ +private: + int *m_iItems; + unsigned int m_iItemsCount; + +public: + TakeItemHint(eTutorial_Hint id, Tutorial *tutorial, int items[], unsigned int itemsLength); + ~TakeItemHint(); + + virtual bool onTake( shared_ptr item ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/Tutorial b/Minecraft.Client/Common/Tutorial/Tutorial new file mode 100644 index 0000000..db58581 Binary files /dev/null and b/Minecraft.Client/Common/Tutorial/Tutorial differ diff --git a/Minecraft.Client/Common/Tutorial/Tutorial.cpp b/Minecraft.Client/Common/Tutorial/Tutorial.cpp new file mode 100644 index 0000000..b0a0d66 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/Tutorial.cpp @@ -0,0 +1,2160 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\LocalPlayer.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\SurvivalMode.h" +#include "Tutorial.h" +#include "TutorialMessage.h" +#include "TutorialTasks.h" +#include "TutorialConstraints.h" +#include "TutorialHints.h" + +vector Tutorial::s_completableTasks; + + +int Tutorial::m_iTutorialHintDelayTime = 14000; +int Tutorial::m_iTutorialDisplayMessageTime = 7000; +int Tutorial::m_iTutorialMinimumDisplayMessageTime = 2000; +int Tutorial::m_iTutorialExtraReminderTime = 13000; +int Tutorial::m_iTutorialReminderTime = m_iTutorialDisplayMessageTime + m_iTutorialExtraReminderTime; +int Tutorial::m_iTutorialConstraintDelayRemoveTicks = 15; +int Tutorial::m_iTutorialFreezeTimeValue = 8000; + +bool Tutorial::PopupMessageDetails::isSameContent(PopupMessageDetails *other) +{ + if(other == NULL) return false; + + bool textTheSame = (m_messageId == other->m_messageId) && (m_messageString.compare(other->m_messageString) == 0); + bool titleTheSame = (m_titleId == other->m_titleId) && (m_titleString.compare(other->m_titleString) == 0); + bool promptTheSame = (m_promptId == other->m_promptId) && (m_promptString.compare(other->m_promptString) == 0); + return textTheSame && titleTheSame && promptTheSame; +} + +void Tutorial::staticCtor() +{ + // + /* + ***** + ***** + THE ORDERING OF THESE SHOULD NOT CHANGE - Although the ordering may not be totally logical due to the order tasks were added, these map + to bits in the profile data in this order. New tasks/hints should be added at the end. + ***** + ***** + */ + s_completableTasks.push_back( e_Tutorial_State_Inventory_Menu ); + s_completableTasks.push_back( e_Tutorial_State_2x2Crafting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_3x3Crafting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Furnace_Menu ); + + s_completableTasks.push_back( e_Tutorial_State_Riding_Minecart ); + s_completableTasks.push_back( e_Tutorial_State_Riding_Boat ); + s_completableTasks.push_back( e_Tutorial_State_Fishing ); + s_completableTasks.push_back( e_Tutorial_State_Bed ); + + s_completableTasks.push_back( e_Tutorial_State_Container_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Trap_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Redstone_And_Piston ); + s_completableTasks.push_back( e_Tutorial_State_Portal ); + s_completableTasks.push_back( e_Tutorial_State_Creative_Inventory_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Food_Bar ); + s_completableTasks.push_back( e_Tutorial_State_CreativeMode ); + s_completableTasks.push_back( e_Tutorial_State_Brewing ); + s_completableTasks.push_back( e_Tutorial_State_Brewing_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Enchanting ); + + s_completableTasks.push_back( e_Tutorial_Hint_Hold_To_Mine ); + s_completableTasks.push_back( e_Tutorial_Hint_Tool_Damaged ); + s_completableTasks.push_back( e_Tutorial_Hint_Swim_Up ); + + s_completableTasks.push_back( e_Tutorial_Hint_Unused_2 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_3 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_4 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_5 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_6 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_7 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_8 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_9 ); + s_completableTasks.push_back( e_Tutorial_Hint_Unused_10 ); + + s_completableTasks.push_back( e_Tutorial_Hint_Rock ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Planks ); + s_completableTasks.push_back( e_Tutorial_Hint_Sapling ); + s_completableTasks.push_back( e_Tutorial_Hint_Unbreakable ); + s_completableTasks.push_back( e_Tutorial_Hint_Water ); + s_completableTasks.push_back( e_Tutorial_Hint_Lava ); + s_completableTasks.push_back( e_Tutorial_Hint_Sand ); + s_completableTasks.push_back( e_Tutorial_Hint_Gravel ); + s_completableTasks.push_back( e_Tutorial_Hint_Gold_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Coal_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Tree_Trunk ); + s_completableTasks.push_back( e_Tutorial_Hint_Glass ); + s_completableTasks.push_back( e_Tutorial_Hint_Leaves ); + s_completableTasks.push_back( e_Tutorial_Hint_Lapis_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Lapis_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Dispenser ); + s_completableTasks.push_back( e_Tutorial_Hint_Sandstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Note_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Powered_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Detector_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Tall_Grass ); + s_completableTasks.push_back( e_Tutorial_Hint_Wool ); + s_completableTasks.push_back( e_Tutorial_Hint_Flower ); + s_completableTasks.push_back( e_Tutorial_Hint_Mushroom ); + s_completableTasks.push_back( e_Tutorial_Hint_Gold_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone_Slab ); + s_completableTasks.push_back( e_Tutorial_Hint_Red_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Tnt ); + s_completableTasks.push_back( e_Tutorial_Hint_Bookshelf ); + s_completableTasks.push_back( e_Tutorial_Hint_Moss_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Obsidian ); + s_completableTasks.push_back( e_Tutorial_Hint_Torch ); + s_completableTasks.push_back( e_Tutorial_Hint_MobSpawner ); + s_completableTasks.push_back( e_Tutorial_Hint_Chest ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Diamond_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Diamond_Block ); + s_completableTasks.push_back( e_Tutorial_Hint_Crafting_Table ); + s_completableTasks.push_back( e_Tutorial_Hint_Crops ); + s_completableTasks.push_back( e_Tutorial_Hint_Farmland ); + s_completableTasks.push_back( e_Tutorial_Hint_Furnace ); + s_completableTasks.push_back( e_Tutorial_Hint_Sign ); + s_completableTasks.push_back( e_Tutorial_Hint_Door_Wood ); + s_completableTasks.push_back( e_Tutorial_Hint_Ladder ); + s_completableTasks.push_back( e_Tutorial_Hint_Rail ); + s_completableTasks.push_back( e_Tutorial_Hint_Stairs_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Lever ); + s_completableTasks.push_back( e_Tutorial_Hint_PressurePlate ); + s_completableTasks.push_back( e_Tutorial_Hint_Door_Iron ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Ore ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Torch ); + s_completableTasks.push_back( e_Tutorial_Hint_Button ); + s_completableTasks.push_back( e_Tutorial_Hint_Snow ); + s_completableTasks.push_back( e_Tutorial_Hint_Ice ); + s_completableTasks.push_back( e_Tutorial_Hint_Cactus ); + s_completableTasks.push_back( e_Tutorial_Hint_Clay ); + s_completableTasks.push_back( e_Tutorial_Hint_Sugarcane ); + s_completableTasks.push_back( e_Tutorial_Hint_Record_Player ); + s_completableTasks.push_back( e_Tutorial_Hint_Pumpkin ); + s_completableTasks.push_back( e_Tutorial_Hint_Hell_Rock ); + s_completableTasks.push_back( e_Tutorial_Hint_Hell_Sand ); + s_completableTasks.push_back( e_Tutorial_Hint_Glowstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Portal ); + s_completableTasks.push_back( e_Tutorial_Hint_Pumpkin_Lit ); + s_completableTasks.push_back( e_Tutorial_Hint_Cake ); + s_completableTasks.push_back( e_Tutorial_Hint_Redstone_Repeater ); + s_completableTasks.push_back( e_Tutorial_Hint_Trapdoor ); + s_completableTasks.push_back( e_Tutorial_Hint_Piston ); + s_completableTasks.push_back( e_Tutorial_Hint_Sticky_Piston ); + s_completableTasks.push_back( e_Tutorial_Hint_Monster_Stone_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_Stone_Brick_Smooth ); + s_completableTasks.push_back( e_Tutorial_Hint_Huge_Mushroom ); + s_completableTasks.push_back( e_Tutorial_Hint_Iron_Fence ); + s_completableTasks.push_back( e_Tutorial_Hint_Thin_Glass ); + s_completableTasks.push_back( e_Tutorial_Hint_Melon ); + s_completableTasks.push_back( e_Tutorial_Hint_Vine ); + s_completableTasks.push_back( e_Tutorial_Hint_Fence_Gate ); + s_completableTasks.push_back( e_Tutorial_Hint_Mycel ); + s_completableTasks.push_back( e_Tutorial_Hint_Water_Lily ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Fence ); + s_completableTasks.push_back( e_Tutorial_Hint_Nether_Stalk ); + s_completableTasks.push_back( e_Tutorial_Hint_Enchant_Table ); + s_completableTasks.push_back( e_Tutorial_Hint_Brewing_Stand ); + s_completableTasks.push_back( e_Tutorial_Hint_Cauldron ); + s_completableTasks.push_back( e_Tutorial_Hint_End_Portal ); + s_completableTasks.push_back( e_Tutorial_Hint_End_Portal_Frame ); + + s_completableTasks.push_back( e_Tutorial_Hint_Squid ); + s_completableTasks.push_back( e_Tutorial_Hint_Cow ); + s_completableTasks.push_back( e_Tutorial_Hint_Sheep ); + s_completableTasks.push_back( e_Tutorial_Hint_Chicken ); + s_completableTasks.push_back( e_Tutorial_Hint_Pig ); + s_completableTasks.push_back( e_Tutorial_Hint_Wolf ); + s_completableTasks.push_back( e_Tutorial_Hint_Creeper ); + s_completableTasks.push_back( e_Tutorial_Hint_Skeleton ); + s_completableTasks.push_back( e_Tutorial_Hint_Spider ); + s_completableTasks.push_back( e_Tutorial_Hint_Zombie ); + s_completableTasks.push_back( e_Tutorial_Hint_Pig_Zombie ); + s_completableTasks.push_back( e_Tutorial_Hint_Ghast ); + s_completableTasks.push_back( e_Tutorial_Hint_Slime ); + s_completableTasks.push_back( e_Tutorial_Hint_Enderman ); + s_completableTasks.push_back( e_Tutorial_Hint_Silverfish ); + s_completableTasks.push_back( e_Tutorial_Hint_Cave_Spider ); + s_completableTasks.push_back( e_Tutorial_Hint_MushroomCow ); + s_completableTasks.push_back( e_Tutorial_Hint_SnowMan ); + s_completableTasks.push_back( e_Tutorial_Hint_IronGolem ); + s_completableTasks.push_back( e_Tutorial_Hint_EnderDragon ); + s_completableTasks.push_back( e_Tutorial_Hint_Blaze ); + s_completableTasks.push_back( e_Tutorial_Hint_Lava_Slime ); + + s_completableTasks.push_back( e_Tutorial_Hint_Ozelot ); + s_completableTasks.push_back( e_Tutorial_Hint_Villager ); + + s_completableTasks.push_back( e_Tutorial_Hint_Item_Shovel ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Hatchet ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Pickaxe ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Flint_And_Steel ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Apple ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bow ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Arrow ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Coal ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Diamond ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Iron_Ingot ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Gold_Ingot ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sword ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Stick ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bowl ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Mushroom_Stew ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_String ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Feather ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sulphur ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Hoe ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Seeds ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Wheat ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bread ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Helmet ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Chestplate ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Leggings ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Boots ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Flint ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Porkchop_Raw ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Porkchop_Cooked ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Painting ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Apple_Gold ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sign ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Door_Wood ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Empty ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Water ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bucket_Lava ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Saddle ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Door_Iron ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Redstone ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Snowball ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Boat ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Leather ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Milk ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Brick ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Clay ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Reeds ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Paper ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Book ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Slimeball ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart_Chest ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Minecart_Furnace ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Compass ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Clock ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Yellow_Dust ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Fish_Raw ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Fish_Cooked ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Dye_Powder ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Bone ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Sugar ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Cake ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Diode ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Cookie ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Map ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Record ); + + s_completableTasks.push_back( e_Tutorial_Hint_White_Stone ); + s_completableTasks.push_back( e_Tutorial_Hint_Dragon_Egg ); + s_completableTasks.push_back( e_Tutorial_Hint_RedstoneLamp ); + s_completableTasks.push_back( e_Tutorial_Hint_Cocoa); + + s_completableTasks.push_back( e_Tutorial_Hint_EmeraldOre ); + s_completableTasks.push_back( e_Tutorial_Hint_EmeraldBlock ); + s_completableTasks.push_back( e_Tutorial_Hint_EnderChest ); + s_completableTasks.push_back( e_Tutorial_Hint_TripwireSource ); + s_completableTasks.push_back( e_Tutorial_Hint_Tripwire ); + s_completableTasks.push_back( e_Tutorial_Hint_CobblestoneWall ); + s_completableTasks.push_back( e_Tutorial_Hint_Flowerpot ); + s_completableTasks.push_back( e_Tutorial_Hint_Anvil ); + s_completableTasks.push_back( e_Tutorial_Hint_QuartzOre ); + s_completableTasks.push_back( e_Tutorial_Hint_QuartzBlock ); + s_completableTasks.push_back( e_Tutorial_Hint_WoolCarpet ); + + s_completableTasks.push_back( e_Tutorial_Hint_Potato ); + s_completableTasks.push_back( e_Tutorial_Hint_Carrot ); + + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_18 ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_19 ); + s_completableTasks.push_back( e_Tutorial_Hint_Item_Unused_20 ); + + s_completableTasks.push_back( eTutorial_Telemetry_TrialStart ); + s_completableTasks.push_back( eTutorial_Telemetry_Halfway ); + s_completableTasks.push_back( eTutorial_Telemetry_Complete ); + + s_completableTasks.push_back( eTutorial_Telemetry_Unused_1 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_2 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_3 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_4 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_5 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_6 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_7 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_8 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_9 ); + s_completableTasks.push_back( eTutorial_Telemetry_Unused_10 ); + + s_completableTasks.push_back( e_Tutorial_State_Enchanting_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Farming ); + s_completableTasks.push_back( e_Tutorial_State_Breeding ); + s_completableTasks.push_back( e_Tutorial_State_Golem ); + s_completableTasks.push_back( e_Tutorial_State_Trading ); + s_completableTasks.push_back( e_Tutorial_State_Trading_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Anvil ); + s_completableTasks.push_back( e_Tutorial_State_Anvil_Menu ); + s_completableTasks.push_back( e_Tutorial_State_Enderchests ); + + s_completableTasks.push_back( e_Tutorial_State_Unused_9 ); + s_completableTasks.push_back( e_Tutorial_State_Unused_10 ); + + if( s_completableTasks.size() > TUTORIAL_PROFILE_STORAGE_BITS ) + { + app.DebugPrintf("Warning: Too many tutorial completable tasks added, not enough bits allocated to stored them in the profile data"); + assert(false); + } +} + +Tutorial::Tutorial(int iPad, bool isFullTutorial /*= false*/) : m_iPad( iPad ) +{ + m_isFullTutorial = isFullTutorial; + m_fullTutorialComplete = false; + m_allTutorialsComplete = false; + hasRequestedUI = false; + uiTempDisabled = false; + m_hintDisplayed = false; + m_freezeTime = false; + m_timeFrozen = false; + m_UIScene = NULL; + m_allowShow = true; + m_bHasTickedOnce = false; + m_firstTickTime = 0; + + m_lastMessage = NULL; + + lastMessageTime = 0; + m_iTaskReminders = 0; + m_lastMessageState = e_Tutorial_State_Gameplay; + + m_CurrentState = e_Tutorial_State_Gameplay; + m_hasStateChanged = false; +#ifdef _XBOX + m_hTutorialScene=NULL; +#endif + + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + currentTask[i] = NULL; + currentFailedConstraint[i] = NULL; + } + + // DEFAULT TASKS THAT ALL TUTORIALS SHARE + /* + * + * + * GAMEPLAY + * + */ + + if(!isHintCompleted(e_Tutorial_Hint_Hold_To_Mine)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Hold_To_Mine, this, IDS_TUTORIAL_HINT_HOLD_TO_MINE, TutorialHint::e_Hint_HoldToMine) ); + if(!isHintCompleted(e_Tutorial_Hint_Tool_Damaged)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Tool_Damaged, this, IDS_TUTORIAL_HINT_TOOL_DAMAGED, TutorialHint::e_Hint_ToolDamaged) ); + if(!isHintCompleted(e_Tutorial_Hint_Swim_Up)) addHint(e_Tutorial_State_Gameplay, new TutorialHint(e_Tutorial_Hint_Swim_Up, this, IDS_TUTORIAL_HINT_SWIM_UP, TutorialHint::e_Hint_SwimUp) ); + + /* + * TILE HINTS + */ + int rockItems[] = {Tile::rock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Rock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Rock, this, rockItems, 1 ) ); + + int stoneItems[] = {Tile::stoneBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone, this, stoneItems, 1 ) ); + + int plankItems[] = {Tile::wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Planks)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Planks, this, plankItems, 1 ) ); + + int saplingItems[] = {Tile::sapling_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sapling)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sapling, this, saplingItems, 1 ) ); + + int unbreakableItems[] = {Tile::unbreakable_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Unbreakable)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Unbreakable, this, unbreakableItems, 1 ) ); + + int waterItems[] = {Tile::water_Id, Tile::calmWater_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Water)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Water, this, waterItems, 2 ) ); + + int lavaItems[] = {Tile::lava_Id, Tile::calmLava_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lava)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lava, this, lavaItems, 2 ) ); + + int sandItems[] = {Tile::sand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sand, this, sandItems, 1 ) ); + + int gravelItems[] = {Tile::gravel_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gravel)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gravel, this, gravelItems, 1 ) ); + + int goldOreItems[] = {Tile::goldOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gold_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gold_Ore, this, goldOreItems, 1 ) ); + + int ironOreItems[] = {Tile::ironOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Ore, this, ironOreItems, 1 ) ); + + int coalOreItems[] = {Tile::coalOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Coal_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Coal_Ore, this, coalOreItems, 1 ) ); + + int treeTrunkItems[] = {Tile::treeTrunk_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tree_Trunk)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tree_Trunk, this, treeTrunkItems, 1 ) ); + + int leavesItems[] = {Tile::leaves_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Leaves)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Leaves, this, leavesItems, 1 ) ); + + int glassItems[] = {Tile::glass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Glass)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Glass, this, glassItems, 1 ) ); + + int lapisOreItems[] = {Tile::lapisOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lapis_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lapis_Ore, this, lapisOreItems, 1 ) ); + + int lapisBlockItems[] = {Tile::lapisBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lapis_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lapis_Block, this, lapisBlockItems, 1 ) ); + + int dispenserItems[] = {Tile::dispenser_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Dispenser)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Dispenser, this, dispenserItems, 1 ) ); + + int sandstoneItems[] = {Tile::sandStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sandstone)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_DEFAULT ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_HEIROGLYPHS ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sandstone, this, sandstoneItems, 1, -1, SandStoneTile::TYPE_SMOOTHSIDE ) ); + } + + int noteBlockItems[] = {Tile::musicBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Note_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Note_Block, this, noteBlockItems, 1 ) ); + + int poweredRailItems[] = {Tile::goldenRail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Powered_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Powered_Rail, this, poweredRailItems, 1 ) ); + + int detectorRailItems[] = {Tile::detectorRail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Detector_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Detector_Rail, this, detectorRailItems, 1 ) ); + + int tallGrassItems[] = {Tile::tallgrass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tall_Grass)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::DEAD_SHRUB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::TALL_GRASS ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tall_Grass, this, tallGrassItems, 1, -1, TallGrass::FERN ) ); + } + + int woolItems[] = {Tile::cloth_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Wool)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Wool, this, woolItems, 1 ) ); + + int flowerItems[] = {Tile::flower_Id, Tile::rose_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Flower)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Flower, this, flowerItems, 2 ) ); + + int mushroomItems[] = {Tile::mushroom1_Id, Tile::mushroom2_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Mushroom)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Mushroom, this, mushroomItems, 2 ) ); + + int goldBlockItems[] = {Tile::goldBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Gold_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Gold_Block, this, goldBlockItems, 1 ) ); + + int ironBlockItems[] = {Tile::ironBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Block, this, ironBlockItems, 1 ) ); + + int stoneSlabItems[] = {Tile::stoneSlabHalf_Id, Tile::stoneSlab_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Slab)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::STONE_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::SAND_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::WOOD_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::COBBLESTONE_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::BRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::SMOOTHBRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::NETHERBRICK_SLAB ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, stoneSlabItems, 2, -1, StoneSlabTile::QUARTZ_SLAB ) ); + } + + int woodSlabItems[] = {Tile::woodSlabHalf_Id, Tile::woodSlab_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Slab)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, woodSlabItems, 2, -1, TreeTile::BIRCH_TRUNK ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Slab, this, woodSlabItems, 2, -1, TreeTile::DARK_TRUNK ) ); + } + + int redBrickItems[] = {Tile::redBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Red_Brick)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Red_Brick, this, redBrickItems, 1 ) ); + + int tntItems[] = {Tile::tnt_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tnt)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tnt, this, tntItems, 1 ) ); + + int bookshelfItems[] = {Tile::bookshelf_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Bookshelf)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Bookshelf, this, bookshelfItems, 1 ) ); + + int mossStoneItems[] = {Tile::mossStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Moss_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Moss_Stone, this, mossStoneItems, 1 ) ); + + int obsidianItems[] = {Tile::obsidian_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Obsidian)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Obsidian, this, obsidianItems, 1 ) ); + + int torchItems[] = {Tile::torch_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Torch)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Torch, this, torchItems, 1 ) ); + + int mobSpawnerItems[] = {Tile::mobSpawner_Id}; + if(!isHintCompleted(e_Tutorial_Hint_MobSpawner)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_MobSpawner, this, mobSpawnerItems, 1 ) ); + + int chestItems[] = {Tile::chest_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Chest)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Chest, this, chestItems, 1 ) ); + + int redstoneItems[] = {Tile::redStoneDust_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone, this, redstoneItems, 1, Item::redStone_Id ) ); + + int diamondOreItems[] = {Tile::diamondOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Diamond_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Diamond_Ore, this, diamondOreItems, 1 ) ); + + int diamondBlockItems[] = {Tile::diamondBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Diamond_Block)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Diamond_Block, this, diamondBlockItems, 1 ) ); + + int craftingTableItems[] = {Tile::workBench_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Crafting_Table)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Crafting_Table, this, craftingTableItems, 1 ) ); + + int cropsItems[] = {Tile::crops_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Crops)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Crops, this, cropsItems, 1, -1, -1, 7 ) ); + + int farmlandItems[] = {Tile::farmland_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Farmland)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Farmland, this, farmlandItems, 1 ) ); + + int furnaceItems[] = {Tile::furnace_Id, Tile::furnace_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Furnace)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Furnace, this, furnaceItems, 2 ) ); + + int signItems[] = {Tile::sign_Id, Tile::wallSign_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sign)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sign, this, signItems, 2, Item::sign_Id ) ); + + int doorWoodItems[] = {Tile::door_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Door_Wood)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Door_Wood, this, doorWoodItems, 1, Item::door_wood->id ) ); + + int ladderItems[] = {Tile::ladder_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Ladder)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Ladder, this, ladderItems, 1 ) ); + + int stairsStoneItems[] = {Tile::stairs_stone_Id,Tile::stairs_bricks_Id,Tile::stairs_stoneBrickSmooth_Id,Tile::stairs_wood_Id,Tile::stairs_sprucewood_Id,Tile::stairs_birchwood_Id,Tile::stairs_netherBricks_Id,Tile::stairs_sandstone_Id,Tile::stairs_quartz_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stairs_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stairs_Stone, this, stairsStoneItems, 9 ) ); + + int railItems[] = {Tile::rail_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Rail)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Rail, this, railItems, 1 ) ); + + int leverItems[] = {Tile::lever_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Lever)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Lever, this, leverItems, 1 ) ); + + int pressurePlateItems[] = {Tile::pressurePlate_stone_Id, Tile::pressurePlate_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_PressurePlate)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_PressurePlate, this, pressurePlateItems, 2 ) ); + + int doorIronItems[] = {Tile::door_iron_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Door_Iron)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Door_Iron, this, doorIronItems, 1, Item::door_iron->id ) ); + + int redstoneOreItems[] = {Tile::redStoneOre_Id, Tile::redStoneOre_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Ore)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Ore, this, redstoneOreItems, 2 ) ); + + int redstoneTorchItems[] = {Tile::notGate_off_Id, Tile::notGate_on_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Torch)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Torch, this, redstoneTorchItems, 2 ) ); + + int buttonItems[] = {Tile::button_stone_Id, Tile::button_wood_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Button)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Button, this, buttonItems, 2 ) ); + + int snowItems[] = {Tile::snow_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Snow)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Snow, this, snowItems, 1 ) ); + + int iceItems[] = {Tile::ice_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Ice)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Ice, this, iceItems, 1 ) ); + + int cactusItems[] = {Tile::cactus_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cactus)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cactus, this, cactusItems, 1 ) ); + + int clayItems[] = {Tile::clay_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Clay)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Clay, this, clayItems, 1 ) ); + + int sugarCaneItems[] = {Tile::reeds_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sugarcane)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sugarcane, this, sugarCaneItems, 1 ) ); + + int recordPlayerItems[] = {Tile::recordPlayer_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Record_Player)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Record_Player, this, recordPlayerItems, 1 ) ); + + int pumpkinItems[] = {Tile::pumpkin_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Pumpkin)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Pumpkin, this, pumpkinItems, 1, -1, -1, 0 ) ); + + int hellRockItems[] = {Tile::hellRock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Hell_Rock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Hell_Rock, this, hellRockItems, 1 ) ); + + int hellSandItems[] = {Tile::hellSand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Hell_Sand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Hell_Sand, this, hellSandItems, 1 ) ); + + int glowstoneItems[] = {Tile::lightGem_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Glowstone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Glowstone, this, glowstoneItems, 1 ) ); + + int portalItems[] = {Tile::portalTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Portal)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Portal, this, portalItems, 1 ) ); + + int pumpkinLitItems[] = {Tile::litPumpkin_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Pumpkin_Lit)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Pumpkin_Lit, this, pumpkinLitItems, 1, -1, -1, 0 ) ); + + int cakeItems[] = {Tile::cake_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cake)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cake, this, cakeItems, 1 ) ); + + int redstoneRepeaterItems[] = {Tile::diode_on_Id, Tile::diode_off_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Redstone_Repeater)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Redstone_Repeater, this, redstoneRepeaterItems, 2, Item::diode_Id ) ); + + int trapdoorItems[] = {Tile::trapdoor_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Trapdoor)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Trapdoor, this, trapdoorItems, 1 ) ); + + int pistonItems[] = {Tile::pistonBase_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Piston)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Piston, this, pistonItems, 1 ) ); + + int stickyPistonItems[] = {Tile::pistonStickyBase_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Sticky_Piston)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Sticky_Piston, this, stickyPistonItems, 1 ) ); + + int monsterStoneEggItems[] = {Tile::monsterStoneEgg_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Monster_Stone_Egg)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Monster_Stone_Egg, this, monsterStoneEggItems, 1 ) ); + + int stoneBrickSmoothItems[] = {Tile::stoneBrickSmooth_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Stone_Brick_Smooth)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Stone_Brick_Smooth, this, stoneBrickSmoothItems, 1 ) ); + + int hugeMushroomItems[] = {Tile::hugeMushroom1_Id,Tile::hugeMushroom2_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Huge_Mushroom)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Huge_Mushroom, this, hugeMushroomItems, 2 ) ); + + int ironFenceItems[] = {Tile::ironFence_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Iron_Fence)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Iron_Fence, this, ironFenceItems, 1 ) ); + + int thisGlassItems[] = {Tile::thinGlass_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Thin_Glass)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Thin_Glass, this, thisGlassItems, 1 ) ); + + int melonItems[] = {Tile::melon_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Melon)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Melon, this, melonItems, 1 ) ); + + int vineItems[] = {Tile::vine_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Vine)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Vine, this, vineItems, 1 ) ); + + int fenceGateItems[] = {Tile::fenceGate_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Fence_Gate)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Fence_Gate, this, fenceGateItems, 1 ) ); + + int mycelItems[] = {Tile::mycel_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Mycel)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Mycel, this, mycelItems, 1 ) ); + + int waterLilyItems[] = {Tile::waterLily_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Water_Lily)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Water_Lily, this, waterLilyItems, 1 ) ); + + int netherBrickItems[] = {Tile::netherBrick_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Brick)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Brick, this, netherBrickItems, 1 ) ); + + int netherFenceItems[] = {Tile::netherFence_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Fence)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Fence, this, netherFenceItems, 1 ) ); + + int netherStalkItems[] = {Tile::netherStalk_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Nether_Stalk)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Nether_Stalk, this, netherStalkItems, 1 ) ); + + int enchantTableItems[] = {Tile::enchantTable_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Enchant_Table)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Enchant_Table, this, enchantTableItems, 1 ) ); + + int brewingStandItems[] = {Tile::brewingStand_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Brewing_Stand)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Brewing_Stand, this, brewingStandItems, 1, Item::brewingStand_Id ) ); + + int cauldronItems[] = {Tile::cauldron_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cauldron)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cauldron, this, cauldronItems, 1, Item::cauldron_Id ) ); + + int endPortalItems[] = {Tile::endPortalTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_End_Portal)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_End_Portal, this, endPortalItems, 1, -2 ) ); + + int endPortalFrameItems[] = {Tile::endPortalFrameTile_Id}; + if(!isHintCompleted(e_Tutorial_Hint_End_Portal_Frame)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_End_Portal_Frame, this, endPortalFrameItems, 1 ) ); + + int whiteStoneItems[] = {Tile::whiteStone_Id}; + if(!isHintCompleted(e_Tutorial_Hint_White_Stone)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_White_Stone, this, whiteStoneItems, 1 ) ); + + int dragonEggItems[] = {Tile::dragonEgg_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Dragon_Egg)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Dragon_Egg, this, dragonEggItems, 1 ) ); + + int redstoneLampItems[] = {Tile::redstoneLight_Id, Tile::redstoneLight_lit_Id}; + if(!isHintCompleted(e_Tutorial_Hint_RedstoneLamp)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_RedstoneLamp, this, redstoneLampItems, 2 ) ); + + int cocoaItems[] = {Tile::cocoa_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Cocoa)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Cocoa, this, cocoaItems, 1 ) ); + + int emeraldOreItems[] = {Tile::emeraldOre_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EmeraldOre)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EmeraldOre, this, emeraldOreItems, 1 ) ); + + int emeraldBlockItems[] = {Tile::emeraldBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EmeraldBlock)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EmeraldBlock, this, emeraldBlockItems, 1 ) ); + + int enderChestItems[] = {Tile::enderChest_Id}; + if(!isHintCompleted(e_Tutorial_Hint_EnderChest)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_EnderChest, this, enderChestItems, 1 ) ); + + int tripwireSourceItems[] = {Tile::tripWireSource_Id}; + if(!isHintCompleted(e_Tutorial_Hint_TripwireSource)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_TripwireSource, this, tripwireSourceItems, 1 ) ); + + int tripwireItems[] = {Tile::tripWire_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Tripwire)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Tripwire, this, tripwireItems, 1, Item::string_Id ) ); + + int cobblestoneWallItems[] = {Tile::cobbleWall_Id}; + if(!isHintCompleted(e_Tutorial_Hint_CobblestoneWall)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_CobblestoneWall, this, cobblestoneWallItems, 1, -1, WallTile::TYPE_NORMAL ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_CobblestoneWall, this, cobblestoneWallItems, 1, -1, WallTile::TYPE_MOSSY ) ); + } + + int flowerpotItems[] = {Tile::flowerPot_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Flowerpot)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Flowerpot, this, flowerpotItems, 1, Item::flowerPot_Id ) ); + + int anvilItems[] = {Tile::anvil_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Anvil)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Anvil, this, anvilItems, 1 ) ); + + int quartzOreItems[] = {Tile::netherQuartz_Id}; + if(!isHintCompleted(e_Tutorial_Hint_QuartzOre)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzOre, this, quartzOreItems, 1 ) ); + + int quartzBlockItems[] = {Tile::quartzBlock_Id}; + if(!isHintCompleted(e_Tutorial_Hint_QuartzBlock)) + { + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_DEFAULT ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_CHISELED ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_Y ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_X ) ); + addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_QuartzBlock, this, quartzBlockItems, 1, -1, QuartzBlockTile::TYPE_LINES_Z ) ); + } + + int carpetItems[] = {Tile::woolCarpet_Id}; + if(!isHintCompleted(e_Tutorial_Hint_WoolCarpet)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_WoolCarpet, this, carpetItems, 1 ) ); + + int potatoItems[] = {Tile::potatoes_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Potato)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Potato, this, potatoItems, 1, -1, -1, 7 ) ); + + int carrotItems[] = {Tile::carrots_Id}; + if(!isHintCompleted(e_Tutorial_Hint_Carrot)) addHint(e_Tutorial_State_Gameplay, new LookAtTileHint(e_Tutorial_Hint_Carrot, this, carrotItems, 1, -1, -1, 7 ) ); + + /* + * ENTITY HINTS + */ + if(!isHintCompleted(e_Tutorial_Hint_Squid)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Squid, this, IDS_DESC_SQUID, IDS_SQUID, eTYPE_SQUID ) ); + if(!isHintCompleted(e_Tutorial_Hint_Cow)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Cow, this, IDS_DESC_COW, IDS_COW, eTYPE_COW ) ); + if(!isHintCompleted(e_Tutorial_Hint_Sheep)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Sheep, this, IDS_DESC_SHEEP, IDS_SHEEP, eTYPE_SHEEP ) ); + if(!isHintCompleted(e_Tutorial_Hint_Chicken)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Chicken, this, IDS_DESC_CHICKEN, IDS_CHICKEN, eTYPE_CHICKEN ) ); + if(!isHintCompleted(e_Tutorial_Hint_Pig)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Pig, this, IDS_DESC_PIG, IDS_PIG, eTYPE_PIG ) ); + if(!isHintCompleted(e_Tutorial_Hint_Wolf)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Wolf, this, IDS_DESC_WOLF, IDS_WOLF, eTYPE_WOLF ) ); + if(!isHintCompleted(e_Tutorial_Hint_Creeper)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Creeper, this, IDS_DESC_CREEPER, IDS_CREEPER, eTYPE_CREEPER ) ); + if(!isHintCompleted(e_Tutorial_Hint_Skeleton)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Skeleton, this, IDS_DESC_SKELETON, IDS_SKELETON, eTYPE_SKELETON ) ); + if(!isHintCompleted(e_Tutorial_Hint_Spider)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Spider, this, IDS_DESC_SPIDER, IDS_SPIDER, eTYPE_SPIDER ) ); + if(!isHintCompleted(e_Tutorial_Hint_Zombie)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Zombie, this, IDS_DESC_ZOMBIE, IDS_ZOMBIE, eTYPE_ZOMBIE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Pig_Zombie)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Pig_Zombie, this, IDS_DESC_PIGZOMBIE, IDS_PIGZOMBIE, eTYPE_PIGZOMBIE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Ghast)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Ghast, this, IDS_DESC_GHAST, IDS_GHAST, eTYPE_GHAST ) ); + if(!isHintCompleted(e_Tutorial_Hint_Slime)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Slime, this, IDS_DESC_SLIME, IDS_SLIME, eTYPE_SLIME ) ); + if(!isHintCompleted(e_Tutorial_Hint_Enderman)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Enderman, this, IDS_DESC_ENDERMAN, IDS_ENDERMAN, eTYPE_ENDERMAN ) ); + if(!isHintCompleted(e_Tutorial_Hint_Silverfish)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Silverfish, this, IDS_DESC_SILVERFISH, IDS_SILVERFISH, eTYPE_SILVERFISH ) ); + if(!isHintCompleted(e_Tutorial_Hint_Cave_Spider)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Cave_Spider, this, IDS_DESC_CAVE_SPIDER, IDS_CAVE_SPIDER, eTYPE_CAVESPIDER ) ); + if(!isHintCompleted(e_Tutorial_Hint_MushroomCow)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_MushroomCow, this, IDS_DESC_MUSHROOM_COW, IDS_MUSHROOM_COW, eTYPE_MUSHROOMCOW) ); + if(!isHintCompleted(e_Tutorial_Hint_SnowMan)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_SnowMan, this, IDS_DESC_SNOWMAN, IDS_SNOWMAN, eTYPE_SNOWMAN ) ); + if(!isHintCompleted(e_Tutorial_Hint_IronGolem)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_IronGolem, this, IDS_DESC_IRONGOLEM, IDS_IRONGOLEM, eTYPE_VILLAGERGOLEM ) ); + if(!isHintCompleted(e_Tutorial_Hint_EnderDragon)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_EnderDragon, this, IDS_DESC_ENDERDRAGON, IDS_ENDERDRAGON, eTYPE_ENDERDRAGON ) ); + if(!isHintCompleted(e_Tutorial_Hint_Blaze)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Blaze, this, IDS_DESC_BLAZE, IDS_BLAZE, eTYPE_BLAZE ) ); + if(!isHintCompleted(e_Tutorial_Hint_Lava_Slime)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Lava_Slime, this, IDS_DESC_LAVA_SLIME, IDS_LAVA_SLIME, eTYPE_LAVASLIME ) ); + if(!isHintCompleted(e_Tutorial_Hint_Ozelot)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Ozelot, this, IDS_DESC_OZELOT, IDS_OZELOT, eTYPE_OZELOT ) ); + if(!isHintCompleted(e_Tutorial_Hint_Villager)) addHint(e_Tutorial_State_Gameplay, new LookAtEntityHint(e_Tutorial_Hint_Villager, this, IDS_DESC_VILLAGER, IDS_VILLAGER, eTYPE_VILLAGER) ); + + + /* + * ITEM HINTS + */ + int shovelItems[] = {Item::shovel_wood->id, Item::shovel_stone->id, Item::shovel_iron->id, Item::shovel_gold->id, Item::shovel_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Shovel)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Shovel, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_SHOVEL, shovelItems, 5) ); + + int hatchetItems[] = {Item::hatchet_wood->id, Item::hatchet_stone->id, Item::hatchet_iron->id, Item::hatchet_gold->id, Item::hatchet_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Hatchet)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Hatchet, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_HATCHET, hatchetItems, 5 ) ); + + int pickaxeItems[] = {Item::pickAxe_wood->id, Item::pickAxe_stone->id, Item::pickAxe_iron->id, Item::pickAxe_gold->id, Item::pickAxe_diamond->id}; + if(!isHintCompleted(e_Tutorial_Hint_Item_Pickaxe)) addHint(e_Tutorial_State_Gameplay, new DiggerItemHint(e_Tutorial_Hint_Item_Pickaxe, this, IDS_TUTORIAL_HINT_DIGGER_ITEM_PICKAXE, pickaxeItems, 5 ) ); + + /* + * + * + * INVENTORY + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Inventory_Menu) ) + { + ProcedureCompoundTask *inventoryOverviewTask = new ProcedureCompoundTask( this ); + inventoryOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_INV_OVERVIEW, IDS_TUTORIAL_PROMPT_INV_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Inventory) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_PICK_UP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_MOVE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_DROP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + inventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_INV_INFO, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Inventory_Menu, inventoryOverviewTask ); + } + + /* + * + * + * CREATIVE INVENTORY + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Creative_Inventory_Menu) ) + { + ProcedureCompoundTask *creativeInventoryOverviewTask = new ProcedureCompoundTask( this ); + creativeInventoryOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_OVERVIEW, IDS_TUTORIAL_PROMPT_CREATIVE_INV_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_CreativeInventory) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_PICK_UP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_MOVE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_DROP, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_NAV, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + creativeInventoryOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CREATIVE_INV_INFO, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Creative_Inventory_Menu, creativeInventoryOverviewTask ); + } + + /* + * + * + * CRAFTING + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_2x2Crafting_Menu ) ) + { + ProcedureCompoundTask *craftingOverviewTask = new ProcedureCompoundTask( this ); + craftingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_CRAFT_OVERVIEW, IDS_TUTORIAL_PROMPT_CRAFT_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Crafting) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_NAV, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_CREATE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_CRAFT_TABLE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_INVENTORY, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_DESCRIPTION, false, ACTION_MENU_X) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_DESCRIPTION, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_INGREDIENTS, false, ACTION_MENU_X) ); + craftingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_CRAFT_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_X_TO_TOGGLE_INVENTORY, false, ACTION_MENU_X) ); + addTask(e_Tutorial_State_2x2Crafting_Menu, craftingOverviewTask ); + } + // Other tasks can be added in the derived classes + + addHint(e_Tutorial_State_2x2Crafting_Menu, new TutorialHint(e_Tutorial_Hint_Always_On, this, IDS_TUTORIAL_HINT_CRAFT_NO_INGREDIENTS, TutorialHint::e_Hint_NoIngredients) ); + + addHint(e_Tutorial_State_3x3Crafting_Menu, new TutorialHint(e_Tutorial_Hint_Always_On, this, IDS_TUTORIAL_HINT_CRAFT_NO_INGREDIENTS, TutorialHint::e_Hint_NoIngredients) ); + + /* + * + * + * FURNACE + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Furnace_Menu ) ) + { + ProcedureCompoundTask *furnaceOverviewTask = new ProcedureCompoundTask( this ); + furnaceOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_FURNACE_OVERVIEW, IDS_TUTORIAL_PROMPT_FURNACE_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_Furnace) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_METHOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_FUELS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + furnaceOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_FURNACE_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Furnace_Menu, furnaceOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * BREWING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Brewing_Menu ) ) + { + ProcedureCompoundTask *brewingOverviewTask = new ProcedureCompoundTask( this ); + brewingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_BREWING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_BrewingMenu) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_METHOD, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_BASIC_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXTENDED_INGREDIENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + brewingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_BREWING_MENU_EXTENDED_INGREDIENTS_2, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Brewing_Menu, brewingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * ENCHANTING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Enchanting_Menu ) ) + { + ProcedureCompoundTask *enchantingOverviewTask = new ProcedureCompoundTask( this ); + enchantingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_ENCHANTING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_EnchantingMenu) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_ENCHANTMENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_ENCHANT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + enchantingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ENCHANTING_MENU_BETTER_ENCHANTMENTS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Enchanting_Menu, enchantingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * ANVIL MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Anvil_Menu ) ) + { + ProcedureCompoundTask *anvilOverviewTask = new ProcedureCompoundTask( this ); + anvilOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_ANVIL_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_AnvilMenu) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_REPAIR, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_SACRIFICE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_ENCHANT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_COST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_RENAMING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + anvilOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_ANVIL_MENU_SMITH, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Anvil_Menu, anvilOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * TRADING MENU + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Trading_Menu ) ) + { + ProcedureCompoundTask *tradingOverviewTask = new ProcedureCompoundTask( this ); + tradingOverviewTask->AddTask( new ChoiceTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_OVERVIEW, IDS_TUTORIAL_PROMPT_TRADING_MENU_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State, eTelemetryTutorial_TradingMenu) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_START, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_UNAVAILABLE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_DETAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_INVENTORY, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + tradingOverviewTask->AddTask( new InfoTask(this, IDS_TUTORIAL_TASK_TRADING_MENU_TRADE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Trading_Menu, tradingOverviewTask ); + } + // Other tasks can be added in the derived classes + + /* + * + * + * MINECART + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Riding_Minecart ) ) + { + addTask(e_Tutorial_State_Riding_Minecart, new ChoiceTask(this, IDS_TUTORIAL_TASK_MINECART_OVERVIEW, IDS_TUTORIAL_PROMPT_MINECART_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Minecart) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_RAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_POWERED_RAILS, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Riding_Minecart, new InfoTask(this, IDS_TUTORIAL_TASK_MINECART_PUSHING, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * BOAT + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Riding_Boat ) ) + { + addTask(e_Tutorial_State_Riding_Boat, new ChoiceTask(this, IDS_TUTORIAL_TASK_BOAT_OVERVIEW, IDS_TUTORIAL_PROMPT_BOAT_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Boat) ); + addTask(e_Tutorial_State_Riding_Boat, new InfoTask(this, IDS_TUTORIAL_TASK_BOAT_STEER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * FISHING + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Fishing ) ) + { + addTask(e_Tutorial_State_Fishing, new ChoiceTask(this, IDS_TUTORIAL_TASK_FISHING_OVERVIEW, IDS_TUTORIAL_PROMPT_FISHING_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Fishing) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_CAST, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_FISH, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Fishing, new InfoTask(this, IDS_TUTORIAL_TASK_FISHING_USES, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * BED + * + */ + if(isFullTutorial || !isStateCompleted( e_Tutorial_State_Bed ) ) + { + addTask(e_Tutorial_State_Bed, new ChoiceTask(this, IDS_TUTORIAL_TASK_BED_OVERVIEW, IDS_TUTORIAL_PROMPT_BED_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_Bed) ); + addTask(e_Tutorial_State_Bed, new InfoTask(this, IDS_TUTORIAL_TASK_BED_PLACEMENT, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Bed, new InfoTask(this, IDS_TUTORIAL_TASK_BED_MULTIPLAYER, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } + + /* + * + * + * FOOD BAR + * + */ + if(!isFullTutorial && !isStateCompleted( e_Tutorial_State_Food_Bar ) ) + { + addTask(e_Tutorial_State_Food_Bar, new ChoiceTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_OVERVIEW, IDS_TUTORIAL_PROMPT_FOOD_BAR_OVERVIEW, true, ACTION_MENU_A, ACTION_MENU_B, e_Tutorial_Completion_Complete_State_Gameplay_Constraints, eTelemetryTutorial_FoodBar) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_DEPLETE, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_HEAL, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + addTask(e_Tutorial_State_Food_Bar, new InfoTask(this, IDS_TUTORIAL_TASK_FOOD_BAR_FEED, IDS_TUTORIAL_PROMPT_PRESS_A_TO_CONTINUE, true, ACTION_MENU_A) ); + } +} + +Tutorial::~Tutorial() +{ + for(AUTO_VAR(it, m_globalConstraints.begin()); it != m_globalConstraints.end(); ++it) + { + delete (*it); + } + for(unordered_map::iterator it = messages.begin(); it != messages.end(); ++it) + { + delete (*it).second; + } + for(unsigned int i = 0; i < e_Tutorial_State_Max; ++i) + { + for(AUTO_VAR(it, activeTasks[i].begin()); it < activeTasks[i].end(); ++it) + { + delete (*it); + } + for(AUTO_VAR(it, hints[i].begin()); it < hints[i].end(); ++it) + { + delete (*it); + } + + currentTask[i] = NULL; + currentFailedConstraint[i] = NULL; + } +} + +void Tutorial::debugResetPlayerSavedProgress(int iPad) +{ +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(iPad); +#endif + ZeroMemory( pGameSettings->ucTutorialCompletion, TUTORIAL_PROFILE_STORAGE_BYTES ); + pGameSettings->uiSpecialTutorialBitmask = 0; +} + +void Tutorial::setCompleted( int completableId ) +{ + //if(app.GetGameSettingsDebugMask(m_iPad) && app.GetGameSettingsDebugMask()&(1L<= 0 && completableIndex < TUTORIAL_PROFILE_STORAGE_BITS ) + { + // Set the bit for this position +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(m_iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(m_iPad); +#endif + int arrayIndex = completableIndex >> 3; + int bitIndex = 7 - (completableIndex % 8); + pGameSettings->ucTutorialCompletion[arrayIndex] |= 1<bSettingsChanged=true; + } +} + +bool Tutorial::getCompleted( int completableId ) +{ + //if(app.GetGameSettingsDebugMask(m_iPad) && app.GetGameSettingsDebugMask()&(1L<= 0 && completableIndex < TUTORIAL_PROFILE_STORAGE_BITS ) + { + // Read the bit for this position + //Retrieve the data pointer from the profile +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)StorageManager.GetGameDefinedProfileData(m_iPad); +#else + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)ProfileManager.GetGameDefinedProfileData(m_iPad); +#endif + int arrayIndex = completableIndex >> 3; + int bitIndex = 7 - (completableIndex % 8); + return (pGameSettings->ucTutorialCompletion[arrayIndex] & 1<getId(); + + if( hintId != e_Tutorial_Hint_Always_On ) + { + setHintCompleted( hint->getId() ); + hints[m_CurrentState].erase( find(hints[m_CurrentState].begin(), hints[m_CurrentState].end(), hint) ); + delete hint; + } + // else + // { + // find(hints[m_CurrentState].begin(), hints[m_CurrentState].end(), hint); + // } +} + +void Tutorial::tick() +{ + // Don't do anything for the first 2 seconds so that the loading screen is gone + if(!m_bHasTickedOnce) + { + int time = GetTickCount(); + if(m_firstTickTime == 0) + { + m_firstTickTime = time; + } + else if ( time - m_firstTickTime > 1500 ) + { + m_bHasTickedOnce = true; + } + } + if(!m_bHasTickedOnce) + { + return; + } + + bool constraintChanged = false; + bool taskChanged = false; + + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + AUTO_VAR(it, constraintsToRemove[state].begin()); + while(it < constraintsToRemove[state].end() ) + { + ++(*it).second; + if( (*it).second > m_iTutorialConstraintDelayRemoveTicks ) + { + TutorialConstraint *c = (*it).first; + constraints[state].erase( find( constraints[state].begin(), constraints[state].end(), c) ); + c->setQueuedForRemoval(false); + it = constraintsToRemove[state].erase( it ); + + if( c->getDeleteOnDeactivate() ) + { + delete c; + } + } + else + { + ++it; + } + } + } + + // 4J Stu TODO - Make this a constraint + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(m_freezeTime && !m_timeFrozen && !m_fullTutorialComplete ) + { + // Need to set the time on both levels to stop the flickering as the local level + // tries to predict the time + MinecraftServer::SetTimeOfDay(m_iTutorialFreezeTimeValue); + pMinecraft->level->setOverrideTimeOfDay(m_iTutorialFreezeTimeValue); // Always daytime + m_timeFrozen = true; + } + else if(m_freezeTime && m_timeFrozen && m_fullTutorialComplete) + { + __int64 currentTime = pMinecraft->level->getTime(); + int currentDayTime = (currentTime % Level::TICKS_PER_DAY); + int timeToAdd = 0; + if(currentDayTime > m_iTutorialFreezeTimeValue) + { + timeToAdd = (Level::TICKS_PER_DAY - currentDayTime) + m_iTutorialFreezeTimeValue; + } + else + { + timeToAdd = m_iTutorialFreezeTimeValue - currentDayTime; + } + __int64 targetTime = currentTime + timeToAdd; + MinecraftServer::SetTimeOfDay(-1); + MinecraftServer::SetTime(targetTime); + pMinecraft->level->setOverrideTimeOfDay(-1); + pMinecraft->level->setTime(targetTime); + m_timeFrozen = false; + } + + if(!m_allowShow) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, false ); + return; + } + + + if(!hasRequestedUI ) + { +#ifdef _XBOX + m_bSceneIsSplitscreen=app.GetLocalPlayerCount()>1; + if(m_bSceneIsSplitscreen) + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } + else + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } +#else + ui.SetTutorial(m_iPad, this); +#endif + hasRequestedUI = true; + } + else + { + // if we've changed mode, we may need to change scene + if(m_bSceneIsSplitscreen!=(app.GetLocalPlayerCount()>1)) + { +#ifdef _XBOX + app.TutorialSceneNavigateBack(m_iPad); + m_bSceneIsSplitscreen=app.GetLocalPlayerCount()>1; + if(m_bSceneIsSplitscreen) + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } + else + { + app.NavigateToScene(m_iPad, eUIComponent_TutorialPopup,(void *)this, false, false, &m_hTutorialScene); + } +#else + ui.SetTutorial(m_iPad, this); +#endif + } + } + + if(ui.IsPauseMenuDisplayed( m_iPad ) ) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, false ); + return; + } + if( uiTempDisabled ) + { + ui.SetTutorialVisible( m_iPad, true ); + lastMessageTime = GetTickCount(); + uiTempDisabled = false; + } + + // Check constraints + for(AUTO_VAR(it, m_globalConstraints.begin()); it < m_globalConstraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + constraint->tick(m_iPad); + } + + // Check hints + int hintNeeded = -1; + if(!m_hintDisplayed) + { + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || app.GetGameSettings(m_iPad,eGameSetting_Hints); + + if(hintsOn) + { + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->tick(); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + message->m_allowFade = hint->allowFade(); + message->m_forceDisplay = true; + setMessage( hint, message ); + break; + } + } + } + } + + // Check constraints + // Only need to update these if we aren't already failing something + if( !m_allTutorialsComplete && (currentFailedConstraint[m_CurrentState] == NULL || currentFailedConstraint[m_CurrentState]->isConstraintSatisfied(m_iPad)) ) + { + if( currentFailedConstraint[m_CurrentState] != NULL && currentFailedConstraint[m_CurrentState]->isConstraintSatisfied(m_iPad) ) + { + constraintChanged = true; + currentFailedConstraint[m_CurrentState] = NULL; + } + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( !constraint->isConstraintSatisfied(m_iPad) && constraint->isConstraintRestrictive(m_iPad) ) + { + constraintChanged = true; + currentFailedConstraint[m_CurrentState] = constraint; + } + } + } + + if( !m_allTutorialsComplete && currentFailedConstraint[m_CurrentState] == NULL ) + { + // Update tasks + bool isCurrentTask = true; + AUTO_VAR(it, activeTasks[m_CurrentState].begin()); + while(activeTasks[m_CurrentState].size() > 0 && it < activeTasks[m_CurrentState].end()) + { + TutorialTask *task = *it; + if( isCurrentTask || task->isPreCompletionEnabled() ) + { + isCurrentTask = false; + if( + ( !task->ShowMinimumTime() || ( task->hasBeenActivated() && (lastMessageTime + m_iTutorialMinimumDisplayMessageTime ) < GetTickCount() ) ) + && task->isCompleted() + ) + { + eTutorial_CompletionAction compAction = task->getCompletionAction(); + it = activeTasks[m_CurrentState].erase( it ); + delete task; + task = NULL; + + if( activeTasks[m_CurrentState].size() > 0 ) + { + switch( compAction ) + { + case e_Tutorial_Completion_Complete_State_Gameplay_Constraints: + { + // 4J Stu - Move the delayed constraints to the gameplay state so that they are in + // effect for a bit longer + AUTO_VAR(itCon, constraintsToRemove[m_CurrentState].begin()); + while(itCon != constraintsToRemove[m_CurrentState].end() ) + { + constraints[e_Tutorial_State_Gameplay].push_back(itCon->first); + constraintsToRemove[e_Tutorial_State_Gameplay].push_back( pair(itCon->first, itCon->second) ); + + constraints[m_CurrentState].erase( find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), itCon->first) ); + itCon = constraintsToRemove[m_CurrentState].erase(itCon); + } + } + // Fall through the the normal complete state + case e_Tutorial_Completion_Complete_State: + for(AUTO_VAR(itRem, activeTasks[m_CurrentState].begin()); itRem < activeTasks[m_CurrentState].end(); ++itRem) + { + delete (*itRem); + } + activeTasks[m_CurrentState].clear(); + break; + case e_Tutorial_Completion_Jump_To_Last_Task: + { + TutorialTask *lastTask = activeTasks[m_CurrentState].at( activeTasks[m_CurrentState].size() - 1 ); + activeTasks[m_CurrentState].pop_back(); + for(AUTO_VAR(itRem, activeTasks[m_CurrentState].begin()); itRem < activeTasks[m_CurrentState].end(); ++itRem) + { + delete (*itRem); + } + activeTasks[m_CurrentState].clear(); + activeTasks[m_CurrentState].push_back( lastTask ); + it = activeTasks[m_CurrentState].begin(); + } + break; + case e_Tutorial_Completion_None: + default: + break; + } + } + + if( activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + } + else + { + setStateCompleted( m_CurrentState ); + + currentTask[m_CurrentState] = NULL; + } + taskChanged = true; + + // If we can complete this early, check if we can complete it right now + if( currentTask[m_CurrentState] != NULL && currentTask[m_CurrentState]->isPreCompletionEnabled() ) + { + isCurrentTask = true; + } + } + else + { + ++it; + } + if( task != NULL && task->ShowMinimumTime() && task->hasBeenActivated() && (lastMessageTime + m_iTutorialMinimumDisplayMessageTime ) < GetTickCount() ) + { + task->setShownForMinimumTime(); + + if( !m_hintDisplayed ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = task->getDescriptionId(); + message->m_promptId = task->getPromptId(); + message->m_allowFade = task->AllowFade(); + message->m_replaceCurrent = true; + setMessage( message ); + } + } + } + else + { + ++it; + } + } + + if( currentTask[m_CurrentState] == NULL && activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + taskChanged = true; + } + } + + if(!m_allTutorialsComplete && (taskChanged || m_hasStateChanged) ) + { + bool allComplete = true; + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + if(activeTasks[state].size() > 0 ) + { + allComplete = false; + break; + } + if(state==e_Tutorial_State_Gameplay) + { + m_fullTutorialComplete = true; + Minecraft::GetInstance()->playerLeftTutorial(m_iPad); + } + } + if(allComplete) + m_allTutorialsComplete = true; + } + + if( constraintChanged || taskChanged || m_hasStateChanged || + (currentFailedConstraint[m_CurrentState] == NULL && currentTask[m_CurrentState] != NULL && (m_lastMessage == NULL || currentTask[m_CurrentState]->getDescriptionId() != m_lastMessage->m_messageId) && !m_hintDisplayed) + ) + { + if( currentFailedConstraint[m_CurrentState] != NULL ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentFailedConstraint[m_CurrentState]->getDescriptionId(); + message->m_allowFade = false; + setMessage( message ); + } + else if( currentTask[m_CurrentState] != NULL ) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentTask[m_CurrentState]->getDescriptionId(); + message->m_promptId = currentTask[m_CurrentState]->getPromptId(); + message->m_allowFade = currentTask[m_CurrentState]->AllowFade(); + setMessage( message ); + currentTask[m_CurrentState]->TaskReminders()? m_iTaskReminders = 1 : m_iTaskReminders = 0; + } + else + { + setMessage( NULL ); + } + } + + if(m_hintDisplayed && (lastMessageTime + m_iTutorialDisplayMessageTime ) < GetTickCount() ) + { + m_hintDisplayed = false; + } + + if( currentFailedConstraint[m_CurrentState] == NULL && currentTask[m_CurrentState] != NULL && (m_iTaskReminders!=0) && (lastMessageTime + (m_iTaskReminders * m_iTutorialReminderTime) ) < GetTickCount() ) + { + // Reminder + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = currentTask[m_CurrentState]->getDescriptionId(); + message->m_promptId = currentTask[m_CurrentState]->getPromptId(); + message->m_allowFade = currentTask[m_CurrentState]->AllowFade(); + message->m_isReminder = true; + setMessage( message ); + ++m_iTaskReminders; + if( m_iTaskReminders > 1 ) + m_iTaskReminders = 1; + } + + m_hasStateChanged = false; + + // If we have completed this state, and it is one that occurs during normal gameplay then change back to the gameplay track + if( m_CurrentState != e_Tutorial_State_Gameplay && activeTasks[m_CurrentState].size() == 0 && (isSelectedItemState() || !ui.GetMenuDisplayed(m_iPad) ) ) + { + this->changeTutorialState( e_Tutorial_State_Gameplay ); + } +} + +bool Tutorial::setMessage(PopupMessageDetails *message) +{ + if(message != NULL && !message->m_forceDisplay && + m_lastMessageState == m_CurrentState && + message->isSameContent(m_lastMessage) && + ( !message->m_isReminder || ( (lastMessageTime + m_iTutorialReminderTime ) > GetTickCount() && message->m_isReminder ) ) + ) + { + delete message; + return false; + } + + if(message != NULL && (message->m_messageId > 0 || !message->m_messageString.empty()) ) + { + m_lastMessageState = m_CurrentState; + + if(!message->m_replaceCurrent) lastMessageTime = GetTickCount(); + + wstring text; + if(!message->m_messageString.empty()) + { + text = message->m_messageString; + } + else + { + AUTO_VAR(it, messages.find(message->m_messageId)); + if( it != messages.end() && it->second != NULL ) + { + TutorialMessage *messageString = it->second; + text = wstring( messageString->getMessageForDisplay() ); + } + else + { + text = wstring( app.GetString(message->m_messageId) ); + } + } + + if(!message->m_promptString.empty()) + { + text.append(message->m_promptString); + } + else if(message->m_promptId >= 0) + { + AUTO_VAR(it, messages.find(message->m_promptId)); + if(it != messages.end() && it->second != NULL) + { + TutorialMessage *prompt = it->second; + text.append( prompt->getMessageForDisplay() ); + } + } + + wstring title; + TutorialPopupInfo popupInfo; + popupInfo.interactScene = m_UIScene; + popupInfo.desc = text.c_str(); + popupInfo.icon = message->m_icon; + popupInfo.iAuxVal = message->m_iAuxVal; + popupInfo.allowFade = message->m_allowFade; + popupInfo.isReminder = message->m_isReminder; + popupInfo.tutorial = this; + if( !message->m_titleString.empty() || message->m_titleId > 0 ) + { + if(message->m_titleString.empty()) title = wstring( app.GetString(message->m_titleId) ); + else title = message->m_titleString; + + popupInfo.title = title.c_str(); + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + else + { + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + } + else if( (m_lastMessage != NULL && m_lastMessage->m_messageId != -1) ) //&& (lastMessageTime + m_iTutorialReminderTime ) > GetTickCount() ) + { + // This should cause the popup to dissappear + TutorialPopupInfo popupInfo; + popupInfo.interactScene = m_UIScene; + popupInfo.tutorial = this; + ui.SetTutorialDescription( m_iPad, &popupInfo ); + } + + if(m_lastMessage != NULL) delete m_lastMessage; + m_lastMessage = message; + + return true; +} + +bool Tutorial::setMessage(TutorialHint *hint, PopupMessageDetails *message) +{ + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || (app.GetGameSettings(m_iPad,eGameSetting_Hints) && app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)); + + bool messageShown = false; + DWORD time = GetTickCount(); + if(message != NULL && (message->m_forceDisplay || hintsOn) && + (!message->m_delay || + ( + (m_hintDisplayed && (time - m_lastHintDisplayedTime) > m_iTutorialHintDelayTime ) || + (!m_hintDisplayed && (time - lastMessageTime) > m_iTutorialMinimumDisplayMessageTime ) + ) + ) + ) + { + messageShown = setMessage( message ); + + if(messageShown) + { + m_lastHintDisplayedTime = time; + m_hintDisplayed = true; + if(hint!=NULL) setHintCompleted( hint ); + } + } + return messageShown; +} + +bool Tutorial::setMessage(const wstring &messageString, int icon, int auxValue) +{ + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageString = messageString; + message->m_icon = icon; + message->m_iAuxVal = auxValue; + message->m_forceDisplay = true; + + return setMessage(message); +} + +void Tutorial::showTutorialPopup(bool show) +{ + m_allowShow = show; + + if(!show) + { + if( currentTask[m_CurrentState] != NULL && (!currentTask[m_CurrentState]->AllowFade() || (lastMessageTime + m_iTutorialDisplayMessageTime ) > GetTickCount() ) ) + { + uiTempDisabled = true; + } + ui.SetTutorialVisible( m_iPad, show ); + } +} + +void Tutorial::useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->useItemOn(level, item, x, y, z, bTestUseOnly); + } +} + +void Tutorial::useItemOn(shared_ptr item, bool bTestUseOnly) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->useItem(item, bTestUseOnly); + } +} + +void Tutorial::completeUsingItem(shared_ptr item) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->completeUsingItem(item); + } + + // Fix for #46922 - TU5: UI: Player receives a reminder that he is hungry while "hunger bar" is full (triggered in split-screen mode) + if(m_CurrentState != e_Tutorial_State_Gameplay) + { + for(AUTO_VAR(it, activeTasks[e_Tutorial_State_Gameplay].begin()); it < activeTasks[e_Tutorial_State_Gameplay].end(); ++it) + { + TutorialTask *task = *it; + task->completeUsingItem(item); + } + } +} + +void Tutorial::startDestroyBlock(shared_ptr item, Tile *tile) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->startDestroyBlock(item, tile); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::destroyBlock(Tile *tile) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->destroyBlock(tile); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::attack(shared_ptr player, shared_ptr entity) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->attack(player->inventory->getSelected(), entity); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::itemDamaged(shared_ptr item) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->itemDamaged(item); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::handleUIInput(int iAction) +{ + if( m_hintDisplayed ) return; + + //for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + //{ + // TutorialTask *task = *it; + // task->handleUIInput(iAction); + //} + if(currentTask[m_CurrentState] != NULL) + currentTask[m_CurrentState]->handleUIInput(iAction); +} + +void Tutorial::createItemSelected(shared_ptr item, bool canMake) +{ + int hintNeeded = -1; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->createItemSelected(item, canMake); + if(hintNeeded >= 0) + { + PopupMessageDetails *message = new PopupMessageDetails(); + message->m_messageId = hintNeeded; + setMessage( hint, message ); + break; + } + + } +} + +void Tutorial::onCrafted(shared_ptr item) +{ + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + for(AUTO_VAR(it, activeTasks[state].begin()); it < activeTasks[state].end(); ++it) + { + TutorialTask *task = *it; + task->onCrafted(item); + } + } +} + +void Tutorial::onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) +{ + if( !m_hintDisplayed ) + { + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onTake(item); + if(hintNeeded) + { + break; + } + + } + } + + for(unsigned int state = 0; state < e_Tutorial_State_Max; ++state) + { + for(AUTO_VAR(it, activeTasks[state].begin()); it < activeTasks[state].end(); ++it) + { + TutorialTask *task = *it; + task->onTake(item, invItemCountAnyAux, invItemCountThisAux); + } + } +} + +void Tutorial::onSelectedItemChanged(shared_ptr item) +{ + // We only handle this if we are in a state that allows changing based on the selected item + // Menus and states like riding in a minecart will NOT allow this + if( isSelectedItemState() ) + { + if(item != NULL) + { + switch(item->id) + { + case Item::fishingRod_Id: + changeTutorialState(e_Tutorial_State_Fishing); + break; + default: + changeTutorialState(e_Tutorial_State_Gameplay); + break; + } + } + else + { + changeTutorialState(e_Tutorial_State_Gameplay); + } + } +} + +void Tutorial::onLookAt(int id, int iData) +{ + if( m_hintDisplayed ) return; + + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onLookAt(id, iData); + if(hintNeeded) + { + break; + } + } + + if( m_CurrentState == e_Tutorial_State_Gameplay ) + { + if(id > 0) + { + switch(id) + { + case Tile::bed_Id: + changeTutorialState(e_Tutorial_State_Bed); + break; + default: + break; + } + } + } +} + +void Tutorial::onLookAtEntity(eINSTANCEOF type) +{ + if( m_hintDisplayed ) return; + + bool hintNeeded = false; + for(AUTO_VAR(it, hints[m_CurrentState].begin()); it < hints[m_CurrentState].end(); ++it) + { + TutorialHint *hint = *it; + hintNeeded = hint->onLookAtEntity(type); + if(hintNeeded) + { + break; + } + } +} + +void Tutorial::onEffectChanged(MobEffect *effect, bool bRemoved) +{ + for(AUTO_VAR(it, activeTasks[m_CurrentState].begin()); it < activeTasks[m_CurrentState].end(); ++it) + { + TutorialTask *task = *it; + task->onEffectChanged(effect,bRemoved); + } +} + +bool Tutorial::canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) +{ + bool allowed = true; + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( !constraint->isConstraintSatisfied(m_iPad) && !constraint->canMoveToPosition(xo,yo,zo,xt,yt,zt) ) + { + allowed = false; + break; + } + } + return allowed; +} + +bool Tutorial::isInputAllowed(int mapping) +{ + if( m_hintDisplayed ) return true; + + // If the player is under water then allow all keypresses so they can jump out + if( Minecraft::GetInstance()->localplayers[m_iPad]->isUnderLiquid(Material::water) ) return true; + + bool allowed = true; + for(AUTO_VAR(it, constraints[m_CurrentState].begin()); it < constraints[m_CurrentState].end(); ++it) + { + TutorialConstraint *constraint = *it; + if( constraint->isMappingConstrained( m_iPad, mapping ) ) + { + allowed = false; + break; + } + } + return allowed; +} + +vector *Tutorial::getTasks() +{ + return &tasks; +} + +unsigned int Tutorial::getCurrentTaskIndex() +{ + unsigned int index = 0; + for(AUTO_VAR(it, tasks.begin()); it < tasks.end(); ++it) + { + if(*it == currentTask[e_Tutorial_State_Gameplay]) + break; + + ++index; + } + return index; +} + +void Tutorial::AddGlobalConstraint(TutorialConstraint *c) +{ + m_globalConstraints.push_back(c); +} + +void Tutorial::AddConstraint(TutorialConstraint *c) +{ + constraints[m_CurrentState].push_back(c); +} + +void Tutorial::RemoveConstraint(TutorialConstraint *c, bool delayedRemove /*= false*/) +{ + if( currentFailedConstraint[m_CurrentState] == c ) + currentFailedConstraint[m_CurrentState] = NULL; + + if( c->getQueuedForRemoval() ) + { + // If it is already queued for removal, remove it on the next tick + /*for(AUTO_VAR(it, constraintsToRemove[m_CurrentState].begin()); it < constraintsToRemove[m_CurrentState].end(); ++it) + { + if( it->first == c ) + { + it->second = m_iTutorialConstraintDelayRemoveTicks; + break; + } + }*/ + } + else if(delayedRemove) + { + c->setQueuedForRemoval(true); + constraintsToRemove[m_CurrentState].push_back( pair(c, 0) ); + } + else + { + for( AUTO_VAR(it, constraintsToRemove[m_CurrentState].begin()); it < constraintsToRemove[m_CurrentState].end(); ++it) + { + if( it->first == c ) + { + constraintsToRemove[m_CurrentState].erase( it ); + break; + } + } + + AUTO_VAR(it, find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), c)); + if( it != constraints[m_CurrentState].end() ) constraints[m_CurrentState].erase( find( constraints[m_CurrentState].begin(), constraints[m_CurrentState].end(), c) ); + + // It may be in the gameplay list, so remove it from there if it is + it = find( constraints[e_Tutorial_State_Gameplay].begin(), constraints[e_Tutorial_State_Gameplay].end(), c); + if( it != constraints[e_Tutorial_State_Gameplay].end() ) constraints[e_Tutorial_State_Gameplay].erase( find( constraints[e_Tutorial_State_Gameplay].begin(), constraints[e_Tutorial_State_Gameplay].end(), c) ); + } +} + +void Tutorial::addTask(eTutorial_State state, TutorialTask *t) +{ + if( state == e_Tutorial_State_Gameplay ) + { + tasks.push_back(t); + } + activeTasks[state].push_back(t); +} + +void Tutorial::addHint(eTutorial_State state, TutorialHint *h) +{ + hints[state].push_back(h); +} + +void Tutorial::addMessage(int messageId, bool limitRepeats /*= false*/, unsigned char numRepeats /*= TUTORIAL_MESSAGE_DEFAULT_SHOW*/) +{ + if(messageId >= 0 && messages.find(messageId)==messages.end()) + messages[messageId] = new TutorialMessage(messageId, limitRepeats, numRepeats); +} + +#ifdef _XBOX +void Tutorial::changeTutorialState(eTutorial_State newState, CXuiScene *scene /*= NULL*/) +#else +void Tutorial::changeTutorialState(eTutorial_State newState, UIScene *scene /*= NULL*/) +#endif +{ + if(newState == m_CurrentState) + { + // If clearing the scene, make sure that the tutorial popup has its reference to this scene removed +#ifndef _XBOX + if( scene == NULL ) + { + ui.RemoveInteractSceneReference(m_iPad, m_UIScene); + } +#endif + m_UIScene = scene; + return; + } + // 4J Stu - TU-1 interim + // Allow turning off all the hints + bool hintsOn = m_isFullTutorial || app.GetGameSettings(m_iPad,eGameSetting_Hints); + + if(hintsOn) + { + // If we have completed this state, and it is one that occurs during normal gameplay then change back to the gameplay track + if( newState != e_Tutorial_State_Gameplay && activeTasks[newState].size() == 0 && !ui.GetMenuDisplayed(m_iPad) ) + { + return; + } + + // The action that caused the change of state may also have completed the current task + if( currentTask[m_CurrentState] != NULL && currentTask[m_CurrentState]->isCompleted() ) + { + activeTasks[m_CurrentState].erase( find( activeTasks[m_CurrentState].begin(), activeTasks[m_CurrentState].end(), currentTask[m_CurrentState]) ); + + if( activeTasks[m_CurrentState].size() > 0 ) + { + currentTask[m_CurrentState] = activeTasks[m_CurrentState][0]; + currentTask[m_CurrentState]->setAsCurrentTask(); + } + else + { + currentTask[m_CurrentState] = NULL; + } + } + + if( currentTask[m_CurrentState] != NULL ) + { + currentTask[m_CurrentState]->onStateChange(newState); + } + + // Make sure that the current message is cleared + setMessage( NULL ); + + // If clearing the scene, make sure that the tutorial popup has its reference to this scene removed +#ifndef _XBOX + if( scene == NULL ) + { + ui.RemoveInteractSceneReference(m_iPad, m_UIScene); + } +#endif + m_UIScene = scene; + + + if( m_CurrentState != newState ) + { + for(AUTO_VAR(it, activeTasks[newState].begin()); it < activeTasks[newState].end(); ++it) + { + TutorialTask *task = *it; + task->onStateChange(newState); + } + m_CurrentState = newState; + m_hasStateChanged = true; + m_hintDisplayed = false; + } + } +} + +bool Tutorial::isSelectedItemState() +{ + bool isSelectedItemState = false; + switch(m_CurrentState) + { + case e_Tutorial_State_Gameplay: + case e_Tutorial_State_Fishing: + isSelectedItemState = true; + break; + default: + break; + } + return isSelectedItemState; +} diff --git a/Minecraft.Client/Common/Tutorial/Tutorial.h b/Minecraft.Client/Common/Tutorial/Tutorial.h new file mode 100644 index 0000000..aaaaba0 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/Tutorial.h @@ -0,0 +1,199 @@ +#pragma once +using namespace std; +#include "TutorialTask.h" +#include "TutorialConstraint.h" +#include "TutorialHint.h" +#include "TutorialMessage.h" +#include "TutorialEnum.h" + +// #define TUTORIAL_HINT_DELAY_TIME 14000 // How long we should wait from displaying one hint to the next +// #define TUTORIAL_DISPLAY_MESSAGE_TIME 7000 +// #define TUTORIAL_MINIMUM_DISPLAY_MESSAGE_TIME 2000 +// #define TUTORIAL_REMINDER_TIME (TUTORIAL_DISPLAY_MESSAGE_TIME + 20000) +// #define TUTORIAL_CONSTRAINT_DELAY_REMOVE_TICKS 15 +// +// // 0-24000 +// #define TUTORIAL_FREEZE_TIME_VALUE 8000 + +class Level; +class CXuiScene; + +class Tutorial +{ +public: + class PopupMessageDetails + { + public: + int m_messageId; + int m_promptId; + int m_titleId; + wstring m_messageString; + wstring m_promptString; + wstring m_titleString; + int m_icon; + int m_iAuxVal; + bool m_allowFade; + bool m_isReminder; + bool m_replaceCurrent; + bool m_forceDisplay; + bool m_delay; + + PopupMessageDetails() + { + m_messageId = -1; + m_promptId = -1; + m_titleId = -1; + m_messageString = L""; + m_promptString = L""; + m_titleString = L""; + m_icon = TUTORIAL_NO_ICON; + m_iAuxVal = 0; + m_allowFade = true; + m_isReminder = false; + m_replaceCurrent = false; + m_forceDisplay = false; + m_delay = false; + } + + bool isSameContent(PopupMessageDetails *other); + + }; + +private: + static int m_iTutorialHintDelayTime; + static int m_iTutorialDisplayMessageTime; + static int m_iTutorialMinimumDisplayMessageTime; + static int m_iTutorialExtraReminderTime; + static int m_iTutorialReminderTime; + static int m_iTutorialConstraintDelayRemoveTicks; + static int m_iTutorialFreezeTimeValue; + eTutorial_State m_CurrentState; + bool m_hasStateChanged; +#ifdef _XBOX + HXUIOBJ m_hTutorialScene; // to store the popup scene (splitscreen or normal) +#endif + bool m_bSceneIsSplitscreen; + + bool m_bHasTickedOnce; + int m_firstTickTime; + +protected: + unordered_map messages; + vector m_globalConstraints; + vector constraints[e_Tutorial_State_Max]; + vector< pair > constraintsToRemove[e_Tutorial_State_Max]; + vector tasks; // We store a copy of the tasks for the main gameplay tutorial so that we could display an overview menu + vector activeTasks[e_Tutorial_State_Max]; + vector hints[e_Tutorial_State_Max]; + TutorialTask *currentTask[e_Tutorial_State_Max]; + TutorialConstraint *currentFailedConstraint[e_Tutorial_State_Max]; + + bool m_freezeTime; + bool m_timeFrozen; + //D3DXVECTOR3 m_OriginalPosition; + +public: + DWORD lastMessageTime; + DWORD m_lastHintDisplayedTime; +private: + PopupMessageDetails *m_lastMessage; + + eTutorial_State m_lastMessageState; + unsigned int m_iTaskReminders; + + bool m_allowShow; + +public: + bool m_hintDisplayed; + +private: + bool hasRequestedUI; + bool uiTempDisabled; + +#ifdef _XBOX + CXuiScene *m_UIScene; +#else + UIScene *m_UIScene; +#endif + + int m_iPad; +public: + bool m_allTutorialsComplete; + bool m_fullTutorialComplete; + bool m_isFullTutorial; +public: + Tutorial(int iPad, bool isFullTutorial = false); + ~Tutorial(); + void tick(); + + int getPad() { return m_iPad; } + + virtual bool isStateCompleted( eTutorial_State state ); + virtual void setStateCompleted( eTutorial_State state ); + bool isHintCompleted( eTutorial_Hint hint ); + void setHintCompleted( eTutorial_Hint hint ); + void setHintCompleted( TutorialHint *hint ); + + // completableId will be either a eTutorial_State value or eTutorial_Hint + void setCompleted( int completableId ); + bool getCompleted( int completableId ); + +#ifdef _XBOX + void changeTutorialState(eTutorial_State newState, CXuiScene *scene = NULL); +#else + void changeTutorialState(eTutorial_State newState, UIScene *scene = NULL); +#endif + bool isSelectedItemState(); + + bool setMessage(PopupMessageDetails *message); + bool setMessage(TutorialHint *hint, PopupMessageDetails *message); + bool setMessage(const wstring &message, int icon, int auxValue); + + void showTutorialPopup(bool show); + + void useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly=false); + void useItemOn(shared_ptr item, bool bTestUseOnly=false); + void completeUsingItem(shared_ptr item); + void startDestroyBlock(shared_ptr item, Tile *tile); + void destroyBlock(Tile *tile); + void attack(shared_ptr player, shared_ptr entity); + void itemDamaged(shared_ptr item); + + void handleUIInput(int iAction); + void createItemSelected(shared_ptr item, bool canMake); + void onCrafted(shared_ptr item); + void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux); + void onSelectedItemChanged(shared_ptr item); + void onLookAt(int id, int iData=0); + void onLookAtEntity(eINSTANCEOF type); + void onEffectChanged(MobEffect *effect, bool bRemoved=false); + + bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt); + bool isInputAllowed(int mapping); + + void AddGlobalConstraint(TutorialConstraint *c); + void AddConstraint(TutorialConstraint *c); + void RemoveConstraint(TutorialConstraint *c, bool delayedRemove = false); + void addTask(eTutorial_State state, TutorialTask *t); + void addHint(eTutorial_State state, TutorialHint *h); + void addMessage(int messageId, bool limitRepeats = false, unsigned char numRepeats = TUTORIAL_MESSAGE_DEFAULT_SHOW); + + int GetTutorialDisplayMessageTime() {return m_iTutorialDisplayMessageTime;} + + // Only for the main gameplay tutorial + vector *getTasks(); + unsigned int getCurrentTaskIndex(); + +#ifdef _XBOX + CXuiScene *getScene() { return m_UIScene; } +#else + UIScene *getScene() { return m_UIScene; } +#endif + eTutorial_State getCurrentState() { return m_CurrentState; } + + // These are required so that we have a consistent mapping of the completion bits stored in the profile data + static void staticCtor(); + static vector s_completableTasks; + + static void debugResetPlayerSavedProgress(int iPad); +}; diff --git a/Minecraft.Client/Common/Tutorial/TutorialConstraint.h b/Minecraft.Client/Common/Tutorial/TutorialConstraint.h new file mode 100644 index 0000000..877fd57 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialConstraint.h @@ -0,0 +1,41 @@ +#pragma once + +// 4J Stu - An abstract class that represents a constraint on what the user is able to do +class TutorialConstraint +{ +private: + int descriptionId; + bool m_deleteOnDeactivate; + bool m_queuedForRemoval; +public: + enum ConstraintType + { + e_ConstraintInput = 0, // Constraint on controller input + e_ConstraintArea, + e_ConstraintAllInput, + e_ConstraintXuiInput, + e_ConstraintChangeState, + }; + + TutorialConstraint(int descriptionId) : descriptionId( descriptionId ), m_deleteOnDeactivate( false ), m_queuedForRemoval( false ) {} + virtual ~TutorialConstraint() {} + + int getDescriptionId() { return descriptionId; } + + virtual ConstraintType getType() = 0; + + virtual void tick(int iPad) {} + virtual bool isConstraintSatisfied(int iPad) { return true; } + virtual bool isConstraintRestrictive(int iPad) { return true; } + + virtual bool isMappingConstrained(int iPad, int mapping) { return false;} + virtual bool isXuiInputConstrained(int vk) { return false;} + + void setDeleteOnDeactivate(bool deleteOnDeactivated) { m_deleteOnDeactivate = deleteOnDeactivated; } + bool getDeleteOnDeactivate() { return m_deleteOnDeactivate; } + + void setQueuedForRemoval(bool queued) { m_queuedForRemoval = queued; } + bool getQueuedForRemoval() { return m_queuedForRemoval; } + + virtual bool canMoveToPosition(double xo, double yo, double zo, double xt, double yt, double zt) { return true; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialConstraints.h b/Minecraft.Client/Common/Tutorial/TutorialConstraints.h new file mode 100644 index 0000000..74bb893 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialConstraints.h @@ -0,0 +1,4 @@ +#include "TutorialConstraint.h" +#include "AreaConstraint.h" +#include "ChangeStateConstraint.h" +#include "InputConstraint.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialEnum.h b/Minecraft.Client/Common/Tutorial/TutorialEnum.h new file mode 100644 index 0000000..33f2e67 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialEnum.h @@ -0,0 +1,329 @@ +#pragma once + +typedef struct { + WORD index; + DWORD diffsSize; + BYTE *diffs; + DWORD lastByteChanged; +} TutorialDiff_Chunk; + +typedef struct { + DWORD diffCount; + TutorialDiff_Chunk *diffs; +} TutorialDiff_File; + +#define TUTORIAL_NO_TEXT -1 +#define TUTORIAL_NO_ICON -1 + +// If you want to make these bigger, be aware that that will affect what is stored after the tutorial data in the profile data +// See Xbox_App.h for the struct +#define TUTORIAL_PROFILE_STORAGE_BITS 512 +#define TUTORIAL_PROFILE_STORAGE_BYTES (TUTORIAL_PROFILE_STORAGE_BITS/8) + +// 4J Stu - The total number of eTutorial_State and eTutorial_Hint must be less than 512, as we only have 512 bits of profile +// data to flag whether or not the player has seen them +// In general a block or tool will have one each. We have a state if we need more than one message, or a hint if just once +// message will suffice +// Tasks added here should also be added in the Tutorial::staticCtor() if you wish to store completion in the profile data +enum eTutorial_State +{ + e_Tutorial_State_Any = -2, + e_Tutorial_State_None = -1, + + e_Tutorial_State_Gameplay = 0, + + e_Tutorial_State_Inventory_Menu, + e_Tutorial_State_2x2Crafting_Menu, + e_Tutorial_State_3x3Crafting_Menu, + e_Tutorial_State_Furnace_Menu, + + e_Tutorial_State_Riding_Minecart, + e_Tutorial_State_Riding_Boat, + e_Tutorial_State_Fishing, + + e_Tutorial_State_Bed, + + e_Tutorial_State_Container_Menu, + e_Tutorial_State_Trap_Menu, + e_Tutorial_State_Redstone_And_Piston, + e_Tutorial_State_Portal, + e_Tutorial_State_Creative_Inventory_Menu, // Added TU5 + e_Tutorial_State_Food_Bar, // Added TU5 + e_Tutorial_State_CreativeMode, // Added TU7 + e_Tutorial_State_Brewing, + e_Tutorial_State_Brewing_Menu, + e_Tutorial_State_Enchanting, + e_Tutorial_State_Enchanting_Menu, + e_Tutorial_State_Farming, + e_Tutorial_State_Breeding, + e_Tutorial_State_Golem, + e_Tutorial_State_Trading, + e_Tutorial_State_Trading_Menu, + e_Tutorial_State_Anvil, + e_Tutorial_State_Anvil_Menu, + e_Tutorial_State_Enderchests, + + e_Tutorial_State_Unused_9, + e_Tutorial_State_Unused_10, + + e_Tutorial_State_Max +}; + +// Hints added here should also be added in the Tutorial::staticCtor() if you wish to store completion in the profile data +enum eTutorial_Hint +{ + e_Tutorial_Hint_Always_On = e_Tutorial_State_Max, + + e_Tutorial_Hint_Hold_To_Mine, + e_Tutorial_Hint_Tool_Damaged, + e_Tutorial_Hint_Swim_Up, + + e_Tutorial_Hint_Unused_2, + e_Tutorial_Hint_Unused_3, + e_Tutorial_Hint_Unused_4, + e_Tutorial_Hint_Unused_5, + e_Tutorial_Hint_Unused_6, + e_Tutorial_Hint_Unused_7, + e_Tutorial_Hint_Unused_8, + e_Tutorial_Hint_Unused_9, + e_Tutorial_Hint_Unused_10, + + e_Tutorial_Hint_Rock, + e_Tutorial_Hint_Stone, + e_Tutorial_Hint_Planks, + e_Tutorial_Hint_Sapling, + e_Tutorial_Hint_Unbreakable, + e_Tutorial_Hint_Water, + e_Tutorial_Hint_Lava, + e_Tutorial_Hint_Sand, + e_Tutorial_Hint_Gravel, + e_Tutorial_Hint_Gold_Ore, + e_Tutorial_Hint_Iron_Ore, + e_Tutorial_Hint_Coal_Ore, + e_Tutorial_Hint_Tree_Trunk, + e_Tutorial_Hint_Leaves, + e_Tutorial_Hint_Glass, + e_Tutorial_Hint_Lapis_Ore, + e_Tutorial_Hint_Lapis_Block, + e_Tutorial_Hint_Dispenser, + e_Tutorial_Hint_Sandstone, + e_Tutorial_Hint_Note_Block, + e_Tutorial_Hint_Powered_Rail, + e_Tutorial_Hint_Detector_Rail, + e_Tutorial_Hint_Tall_Grass, + e_Tutorial_Hint_Wool, + e_Tutorial_Hint_Flower, + e_Tutorial_Hint_Mushroom, + e_Tutorial_Hint_Gold_Block, + e_Tutorial_Hint_Iron_Block, + e_Tutorial_Hint_Stone_Slab, + e_Tutorial_Hint_Red_Brick, + e_Tutorial_Hint_Tnt, + e_Tutorial_Hint_Bookshelf, + e_Tutorial_Hint_Moss_Stone, + e_Tutorial_Hint_Obsidian, + e_Tutorial_Hint_Torch, + e_Tutorial_Hint_MobSpawner, + e_Tutorial_Hint_Chest, + e_Tutorial_Hint_Redstone, + e_Tutorial_Hint_Diamond_Ore, + e_Tutorial_Hint_Diamond_Block, + e_Tutorial_Hint_Crafting_Table, + e_Tutorial_Hint_Crops, + e_Tutorial_Hint_Farmland, + e_Tutorial_Hint_Furnace, + e_Tutorial_Hint_Sign, + e_Tutorial_Hint_Door_Wood, + e_Tutorial_Hint_Ladder, + e_Tutorial_Hint_Stairs_Stone, + e_Tutorial_Hint_Rail, + e_Tutorial_Hint_Lever, + e_Tutorial_Hint_PressurePlate, + e_Tutorial_Hint_Door_Iron, + e_Tutorial_Hint_Redstone_Ore, + e_Tutorial_Hint_Redstone_Torch, + e_Tutorial_Hint_Button, + e_Tutorial_Hint_Snow, + e_Tutorial_Hint_Ice, + e_Tutorial_Hint_Cactus, + e_Tutorial_Hint_Clay, + e_Tutorial_Hint_Sugarcane, + e_Tutorial_Hint_Record_Player, + e_Tutorial_Hint_Pumpkin, + e_Tutorial_Hint_Hell_Rock, + e_Tutorial_Hint_Hell_Sand, + e_Tutorial_Hint_Glowstone, + e_Tutorial_Hint_Portal, + e_Tutorial_Hint_Pumpkin_Lit, + e_Tutorial_Hint_Cake, + e_Tutorial_Hint_Redstone_Repeater, + e_Tutorial_Hint_Trapdoor, + e_Tutorial_Hint_Piston, + e_Tutorial_Hint_Sticky_Piston, + e_Tutorial_Hint_Monster_Stone_Egg, + e_Tutorial_Hint_Stone_Brick_Smooth, + e_Tutorial_Hint_Huge_Mushroom, + e_Tutorial_Hint_Iron_Fence, + e_Tutorial_Hint_Thin_Glass, + e_Tutorial_Hint_Melon, + e_Tutorial_Hint_Vine, + e_Tutorial_Hint_Fence_Gate, + e_Tutorial_Hint_Mycel, + e_Tutorial_Hint_Water_Lily, + e_Tutorial_Hint_Nether_Brick, + e_Tutorial_Hint_Nether_Fence, + e_Tutorial_Hint_Nether_Stalk, + e_Tutorial_Hint_Enchant_Table, + e_Tutorial_Hint_Brewing_Stand, + e_Tutorial_Hint_Cauldron, + e_Tutorial_Hint_End_Portal, + e_Tutorial_Hint_End_Portal_Frame, + + e_Tutorial_Hint_Squid, + e_Tutorial_Hint_Cow, + e_Tutorial_Hint_Sheep, + e_Tutorial_Hint_Chicken, + e_Tutorial_Hint_Pig, + e_Tutorial_Hint_Wolf, + e_Tutorial_Hint_Creeper, + e_Tutorial_Hint_Skeleton, + e_Tutorial_Hint_Spider, + e_Tutorial_Hint_Zombie, + e_Tutorial_Hint_Pig_Zombie, + e_Tutorial_Hint_Ghast, + e_Tutorial_Hint_Slime, + e_Tutorial_Hint_Enderman, + e_Tutorial_Hint_Silverfish, + e_Tutorial_Hint_Cave_Spider, + e_Tutorial_Hint_MushroomCow, + e_Tutorial_Hint_SnowMan, + e_Tutorial_Hint_IronGolem, + e_Tutorial_Hint_EnderDragon, + e_Tutorial_Hint_Blaze, + e_Tutorial_Hint_Lava_Slime, + + e_Tutorial_Hint_Ozelot, + e_Tutorial_Hint_Villager, + + e_Tutorial_Hint_Item_Shovel, + e_Tutorial_Hint_Item_Hatchet, + e_Tutorial_Hint_Item_Pickaxe, + e_Tutorial_Hint_Item_Flint_And_Steel, + e_Tutorial_Hint_Item_Apple, + e_Tutorial_Hint_Item_Bow, + e_Tutorial_Hint_Item_Arrow, + e_Tutorial_Hint_Item_Coal, + e_Tutorial_Hint_Item_Diamond, + e_Tutorial_Hint_Item_Iron_Ingot, + e_Tutorial_Hint_Item_Gold_Ingot, + e_Tutorial_Hint_Item_Sword, + e_Tutorial_Hint_Item_Stick, + e_Tutorial_Hint_Item_Bowl, + e_Tutorial_Hint_Item_Mushroom_Stew, + e_Tutorial_Hint_Item_String, + e_Tutorial_Hint_Item_Feather, + e_Tutorial_Hint_Item_Sulphur, + e_Tutorial_Hint_Item_Hoe, + e_Tutorial_Hint_Item_Seeds, + e_Tutorial_Hint_Item_Wheat, + e_Tutorial_Hint_Item_Bread, + e_Tutorial_Hint_Item_Helmet, + e_Tutorial_Hint_Item_Chestplate, + e_Tutorial_Hint_Item_Leggings, + e_Tutorial_Hint_Item_Boots, + e_Tutorial_Hint_Item_Flint, + e_Tutorial_Hint_Item_Porkchop_Raw, + e_Tutorial_Hint_Item_Porkchop_Cooked, + e_Tutorial_Hint_Item_Painting, + e_Tutorial_Hint_Item_Apple_Gold, + e_Tutorial_Hint_Item_Sign, + e_Tutorial_Hint_Item_Door_Wood, + e_Tutorial_Hint_Item_Bucket_Empty, + e_Tutorial_Hint_Item_Bucket_Water, + e_Tutorial_Hint_Item_Bucket_Lava, + e_Tutorial_Hint_Item_Minecart, + e_Tutorial_Hint_Item_Saddle, + e_Tutorial_Hint_Item_Door_Iron, + e_Tutorial_Hint_Item_Redstone, + e_Tutorial_Hint_Item_Snowball, + e_Tutorial_Hint_Item_Boat, + e_Tutorial_Hint_Item_Leather, + e_Tutorial_Hint_Item_Milk, + e_Tutorial_Hint_Item_Brick, + e_Tutorial_Hint_Item_Clay, + e_Tutorial_Hint_Item_Reeds, + e_Tutorial_Hint_Item_Paper, + e_Tutorial_Hint_Item_Book, + e_Tutorial_Hint_Item_Slimeball, + e_Tutorial_Hint_Item_Minecart_Chest, + e_Tutorial_Hint_Item_Minecart_Furnace, + e_Tutorial_Hint_Item_Egg, + e_Tutorial_Hint_Item_Compass, + e_Tutorial_Hint_Item_Clock, + e_Tutorial_Hint_Item_Yellow_Dust, + e_Tutorial_Hint_Item_Fish_Raw, + e_Tutorial_Hint_Item_Fish_Cooked, + e_Tutorial_Hint_Item_Dye_Powder, + e_Tutorial_Hint_Item_Bone, + e_Tutorial_Hint_Item_Sugar, + e_Tutorial_Hint_Item_Cake, + e_Tutorial_Hint_Item_Diode, + e_Tutorial_Hint_Item_Cookie, + e_Tutorial_Hint_Item_Map, + e_Tutorial_Hint_Item_Record, + + e_Tutorial_Hint_White_Stone, + e_Tutorial_Hint_Dragon_Egg, + e_Tutorial_Hint_RedstoneLamp, + e_Tutorial_Hint_Cocoa, + + e_Tutorial_Hint_EmeraldOre, + e_Tutorial_Hint_EmeraldBlock, + e_Tutorial_Hint_EnderChest, + e_Tutorial_Hint_TripwireSource, + e_Tutorial_Hint_Tripwire, + e_Tutorial_Hint_CobblestoneWall, + e_Tutorial_Hint_Flowerpot, + e_Tutorial_Hint_Anvil, + e_Tutorial_Hint_QuartzOre, + e_Tutorial_Hint_QuartzBlock, + e_Tutorial_Hint_WoolCarpet, + + e_Tutorial_Hint_Potato, + e_Tutorial_Hint_Carrot, + + e_Tutorial_Hint_Item_Unused_18, + e_Tutorial_Hint_Item_Unused_19, + e_Tutorial_Hint_Item_Unused_20, + + e_Tutorial_Hint_Item_Max, +}; + +// We store the first time that we complete these tasks to be used in telemetry +enum eTutorial_Telemetry +{ + eTutorial_Telemetry_None = e_Tutorial_Hint_Item_Max, + + eTutorial_Telemetry_TrialStart, + eTutorial_Telemetry_Halfway, + eTutorial_Telemetry_Complete, + + eTutorial_Telemetry_Unused_1, + eTutorial_Telemetry_Unused_2, + eTutorial_Telemetry_Unused_3, + eTutorial_Telemetry_Unused_4, + eTutorial_Telemetry_Unused_5, + eTutorial_Telemetry_Unused_6, + eTutorial_Telemetry_Unused_7, + eTutorial_Telemetry_Unused_8, + eTutorial_Telemetry_Unused_9, + eTutorial_Telemetry_Unused_10, +}; + +enum eTutorial_CompletionAction +{ + e_Tutorial_Completion_None, + e_Tutorial_Completion_Complete_State, // This will make the current tutorial state complete + e_Tutorial_Completion_Complete_State_Gameplay_Constraints, // This will make the current tutorial state complete, and move the delayed constraints to the gameplay state + e_Tutorial_Completion_Jump_To_Last_Task, +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHint.cpp b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp new file mode 100644 index 0000000..5f0808b --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHint.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "Tutorial.h" +#include "TutorialHint.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" + +TutorialHint::TutorialHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, eHintType type, bool allowFade /*= true*/) + : m_id( id ), m_tutorial(tutorial), m_descriptionId( descriptionId ), m_type( type ), m_counter( 0 ), + m_lastTile( NULL ), m_hintNeeded( true ), m_allowFade(allowFade) +{ + tutorial->addMessage(descriptionId, type != e_Hint_NoIngredients); +} + +int TutorialHint::startDestroyBlock(shared_ptr item, Tile *tile) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_HoldToMine: + if( tile == m_lastTile && m_hintNeeded ) + { + ++m_counter; + if(m_counter > TUTORIAL_HINT_MAX_MINE_REPEATS) + { + returnVal = m_descriptionId; + } + } + else + { + m_counter = 0; + } + m_lastTile = tile; + break; + default: + break; + } + + return returnVal; +} + +int TutorialHint::destroyBlock(Tile *tile) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_HoldToMine: + if(tile == m_lastTile && m_counter > 0) + { + m_hintNeeded = false; + } + break; + default: + break; + } + + return returnVal; +} + +int TutorialHint::attack(shared_ptr item, shared_ptr entity) +{ + /* + switch(m_type) + { + default: + return -1; + } + */ + return -1; +} + +int TutorialHint::createItemSelected(shared_ptr item, bool canMake) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_NoIngredients: + if(!canMake) + returnVal = m_descriptionId; + break; + default: + break; + } + return returnVal; +} + +int TutorialHint::itemDamaged(shared_ptr item) +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_ToolDamaged: + returnVal = m_descriptionId; + break; + default: + break; + } + return returnVal; +} + +bool TutorialHint::onTake( shared_ptr item ) +{ + return false; +} + +bool TutorialHint::onLookAt(int id, int iData) +{ + return false; +} + +bool TutorialHint::onLookAtEntity(eINSTANCEOF type) +{ + return false; +} + +int TutorialHint::tick() +{ + int returnVal = -1; + switch(m_type) + { + case e_Hint_SwimUp: + if( Minecraft::GetInstance()->localplayers[m_tutorial->getPad()]->isUnderLiquid(Material::water) ) returnVal = m_descriptionId; + break; + } + return returnVal; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHint.h b/Minecraft.Client/Common/Tutorial/TutorialHint.h new file mode 100644 index 0000000..8ca543c --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHint.h @@ -0,0 +1,53 @@ +#pragma once +using namespace std; + +#include "TutorialEnum.h" + +#define TUTORIAL_HINT_MAX_MINE_REPEATS 20 + +class Level; +class Tutorial; + +class TutorialHint +{ +public: + enum eHintType + { + e_Hint_DiggerItem, + e_Hint_HoldToMine, + e_Hint_NoIngredients, + e_Hint_ToolDamaged, + e_Hint_TakeItem, + e_Hint_Area, + e_Hint_LookAtTile, + e_Hint_LookAtEntity, + e_Hint_SwimUp, + }; + +protected: + eHintType m_type; + int m_descriptionId; + Tutorial *m_tutorial; + eTutorial_Hint m_id; + + int m_counter; + Tile *m_lastTile; + bool m_hintNeeded; + bool m_allowFade; + +public: + TutorialHint(eTutorial_Hint id, Tutorial *tutorial, int descriptionId, eHintType type, bool allowFade = true); + + eTutorial_Hint getId() { return m_id; } + + virtual int startDestroyBlock(shared_ptr item, Tile *tile); + virtual int destroyBlock(Tile *tile); + virtual int attack(shared_ptr item, shared_ptr entity); + virtual int createItemSelected(shared_ptr item, bool canMake); + virtual int itemDamaged(shared_ptr item); + virtual bool onTake( shared_ptr item ); + virtual bool onLookAt(int id, int iData=0); + virtual bool onLookAtEntity(eINSTANCEOF type); + virtual int tick(); + virtual bool allowFade() { return m_allowFade; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialHints.h b/Minecraft.Client/Common/Tutorial/TutorialHints.h new file mode 100644 index 0000000..5c7381a --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialHints.h @@ -0,0 +1,7 @@ +#pragma once + +#include "AreaHint.h" +#include "DiggerItemHint.h" +#include "LookAtTileHint.h" +#include "TakeItemHint.h" +#include "LookAtEntityHint.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp b/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp new file mode 100644 index 0000000..1f00703 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMessage.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "TutorialMessage.h" + +TutorialMessage::TutorialMessage(int messageId, bool limitRepeats /*= false*/, unsigned char numRepeats /*= TUTORIAL_MESSAGE_DEFAULT_SHOW*/) + : messageId( messageId ), limitRepeats( limitRepeats ), numRepeats( numRepeats ), timesShown( 0 ) +{ +} + +bool TutorialMessage::canDisplay() +{ + return !limitRepeats || (timesShown < numRepeats); +} + +LPCWSTR TutorialMessage::getMessageForDisplay() +{ + if(!canDisplay()) + return L""; + + if(limitRepeats) + ++timesShown; + + return app.GetString( messageId ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMessage.h b/Minecraft.Client/Common/Tutorial/TutorialMessage.h new file mode 100644 index 0000000..6a0b4d4 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMessage.h @@ -0,0 +1,20 @@ +#pragma once + +// The default number of times any message should be shown +#define TUTORIAL_MESSAGE_DEFAULT_SHOW 3 + +class TutorialMessage +{ +private: + int messageId; + bool limitRepeats; + unsigned char numRepeats; + unsigned char timesShown; + DWORD lastDisplayed; + +public: + TutorialMessage(int messageId, bool limitRepeats = false, unsigned char numRepeats = TUTORIAL_MESSAGE_DEFAULT_SHOW); + + bool canDisplay(); + LPCWSTR getMessageForDisplay(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMode.cpp b/Minecraft.Client/Common/Tutorial/TutorialMode.cpp new file mode 100644 index 0000000..82c8159 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMode.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\..\Minecraft.World\Inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "TutorialMode.h" + +TutorialMode::TutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection) : MultiPlayerGameMode( minecraft, connection ), m_iPad( iPad ) +{ +} + +TutorialMode::~TutorialMode() +{ + if(tutorial != NULL) + delete tutorial; +} + +void TutorialMode::startDestroyBlock(int x, int y, int z, int face) +{ + if(!tutorial->m_allTutorialsComplete) + { + int t = minecraft->level->getTile(x, y, z); + tutorial->startDestroyBlock(minecraft->player->inventory->getSelected(), Tile::tiles[t]); + } + MultiPlayerGameMode::startDestroyBlock( x, y, z, face ); +} + +bool TutorialMode::destroyBlock(int x, int y, int z, int face) +{ + if(!tutorial->m_allTutorialsComplete) + { + int t = minecraft->level->getTile(x, y, z); + tutorial->destroyBlock(Tile::tiles[t]); + } + shared_ptr item = minecraft->player->getSelectedItem(); + int damageBefore; + if(item != NULL) + { + damageBefore = item->getDamageValue(); + } + bool changed = MultiPlayerGameMode::destroyBlock( x, y, z, face ); + + if(!tutorial->m_allTutorialsComplete) + { + if ( item != NULL && item->isDamageableItem() ) + { + int max = item->getMaxDamage(); + int damageNow = item->getDamageValue(); + + if(damageNow > damageBefore && damageNow > (max/2) ) + { + tutorial->itemDamaged( item ); + } + } + } + + return changed; +} + +void TutorialMode::tick() +{ + MultiPlayerGameMode::tick(); + + if(!tutorial->m_allTutorialsComplete) + tutorial->tick(); + + /* + if( tutorial.m_allTutorialsComplete && (tutorial.lastMessageTime + m_iTutorialDisplayMessageTime) < GetTickCount() ) + { + // Exit tutorial + minecraft->gameMode = new SurvivalMode( this ); + delete this; + } + */ +} + +bool TutorialMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly, bool *pbUsedItem) +{ + bool haveItem = false; + int itemCount = 0; + if(!tutorial->m_allTutorialsComplete) + { + tutorial->useItemOn(level, item, x, y, z, bTestUseOnly); + + if(!bTestUseOnly) + { + if(item != NULL) + { + haveItem = true; + itemCount = item->count; + } + } + } + bool result = MultiPlayerGameMode::useItemOn( player, level, item, x, y, z, face, hit, bTestUseOnly, pbUsedItem ); + + if(!bTestUseOnly) + { + if(!tutorial->m_allTutorialsComplete) + { + if( result && haveItem && itemCount > item->count ) + { + tutorial->useItemOn(item); + } + } + } + return result; +} + +void TutorialMode::attack(shared_ptr player, shared_ptr entity) +{ + if(!tutorial->m_allTutorialsComplete) + tutorial->attack(player, entity); + + MultiPlayerGameMode::attack( player, entity ); +} + +bool TutorialMode::isInputAllowed(int mapping) +{ + return tutorial->m_allTutorialsComplete || tutorial->isInputAllowed( mapping ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialMode.h b/Minecraft.Client/Common/Tutorial/TutorialMode.h new file mode 100644 index 0000000..75e24ed --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialMode.h @@ -0,0 +1,28 @@ +#pragma once +using namespace std; + +#include "..\..\MultiPlayerGameMode.h" +#include "Tutorial.h" + +class TutorialMode : public MultiPlayerGameMode +{ +protected: + Tutorial *tutorial; + int m_iPad; + + // Function to make this an abstract class + virtual bool isImplemented() = 0; +public: + TutorialMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + virtual ~TutorialMode(); + + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual bool destroyBlock(int x, int y, int z, int face); + virtual void tick(); + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly=false, bool *pbUsedItem=NULL); + virtual void attack(shared_ptr player, shared_ptr entity); + + virtual bool isInputAllowed(int mapping); + + Tutorial *getTutorial() { return tutorial; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTask.cpp b/Minecraft.Client/Common/Tutorial/TutorialTask.cpp new file mode 100644 index 0000000..2251ab0 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTask.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "Tutorial.h" +#include "TutorialConstraints.h" +#include "TutorialTask.h" + +TutorialTask::TutorialTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, vector *inConstraints, + bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : tutorial( tutorial ), descriptionId( descriptionId ), m_promptId( -1 ), enablePreCompletion( enablePreCompletion ), + areConstraintsEnabled( false ), bIsCompleted( false ), bHasBeenActivated( false ), + m_bAllowFade(bAllowFade), m_bTaskReminders(bTaskReminders), m_bShowMinimumTime( bShowMinimumTime), m_bShownForMinimumTime( false ) +{ + if(inConstraints != NULL) + { + for(AUTO_VAR(it, inConstraints->begin()); it < inConstraints->end(); ++it) + { + TutorialConstraint *constraint = *it; + constraints.push_back( constraint ); + } + delete inConstraints; + } + + tutorial->addMessage(descriptionId); +} + +TutorialTask::~TutorialTask() +{ + enableConstraints(false); + + for(AUTO_VAR(it, constraints.begin()); it < constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + + if( constraint->getQueuedForRemoval() ) + { + constraint->setDeleteOnDeactivate(true); + } + else + { + delete constraint; + } + } +} + +void TutorialTask::taskCompleted() +{ + if( areConstraintsEnabled == true ) + enableConstraints( false ); +} + +void TutorialTask::enableConstraints(bool enable, bool delayRemove /*= false*/) +{ + if( !enable && (areConstraintsEnabled || !delayRemove) ) + { + // Remove + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + //app.DebugPrintf(">>>>>>>> %i\n", constraints.size()); + tutorial->RemoveConstraint( constraint, delayRemove ); + } + areConstraintsEnabled = false; + } + else if( !areConstraintsEnabled && enable ) + { + // Add + for(AUTO_VAR(it, constraints.begin()); it != constraints.end(); ++it) + { + TutorialConstraint *constraint = *it; + tutorial->AddConstraint( constraint ); + } + areConstraintsEnabled = true; + } +} + +void TutorialTask::setAsCurrentTask(bool active /*= true*/) +{ + bHasBeenActivated = active; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTask.h b/Minecraft.Client/Common/Tutorial/TutorialTask.h new file mode 100644 index 0000000..92cb599 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTask.h @@ -0,0 +1,63 @@ +#pragma once +using namespace std; +#include "TutorialEnum.h" + +class Level; +class Tutorial; +class TutorialConstraint; +class MobEffect; + +// A class that represents each individual task in the tutorial. +// +// Members: +// enablePreCompletion - If this is true, then the player can complete this task out of sequence. +// This stops us asking them to do things they have already done +// constraints - A list of constraints which can be activated (as a whole). +// If they are active, then the constraints are removed when the task is completed +// areConstraintsEnabled- A flag which records whether or not we have added the constraints to the tutorial +class TutorialTask +{ +protected: + int descriptionId; + int m_promptId; + Tutorial *tutorial; + bool enablePreCompletion; + bool bHasBeenActivated; + bool m_bAllowFade; + bool m_bTaskReminders; + bool m_bShowMinimumTime; + +protected: + bool bIsCompleted; + bool m_bShownForMinimumTime; + vector constraints; + bool areConstraintsEnabled; +public: + TutorialTask(Tutorial *tutorial, int descriptionId, bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ); + virtual ~TutorialTask(); + + virtual int getDescriptionId() { return descriptionId; } + virtual int getPromptId() { return m_promptId; } + + virtual bool isCompleted() = 0; + virtual eTutorial_CompletionAction getCompletionAction() { return e_Tutorial_Completion_None; } + virtual bool isPreCompletionEnabled() { return enablePreCompletion; } + virtual void taskCompleted(); + virtual void enableConstraints(bool enable, bool delayRemove = false); + virtual void setAsCurrentTask(bool active = true); + + virtual void setShownForMinimumTime() { m_bShownForMinimumTime = true; } + virtual bool hasBeenActivated() { return bHasBeenActivated; } + virtual bool AllowFade() { return m_bAllowFade;} + bool TaskReminders() { return m_bTaskReminders;} + virtual bool ShowMinimumTime() { return m_bShowMinimumTime;} + + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false) { } + virtual void useItem(shared_ptr item,bool bTestUseOnly=false) { } + virtual void completeUsingItem(shared_ptr item) { } + virtual void handleUIInput(int iAction) { } + virtual void onCrafted(shared_ptr item) { } + virtual void onTake(shared_ptr item, unsigned int invItemCountAnyAux, unsigned int invItemCountThisAux) { } + virtual void onStateChange(eTutorial_State newState) { } + virtual void onEffectChanged(MobEffect *effect, bool bRemoved=false) { } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/TutorialTasks.h b/Minecraft.Client/Common/Tutorial/TutorialTasks.h new file mode 100644 index 0000000..591e956 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/TutorialTasks.h @@ -0,0 +1,16 @@ +#include "StatTask.h" +#include "CraftTask.h" +#include "PickupTask.h" +#include "UseTileTask.h" +#include "UseItemTask.h" +#include "InfoTask.h" +#include "ControllerTask.h" +#include "ProcedureCompoundTask.h" +#include "XuiCraftingTask.h" +#include "StateChangeTask.h" +#include "ChoiceTask.h" +#include "FullTutorialActiveTask.h" +#include "AreaTask.h" +#include "ProgressFlagTask.h" +#include "CompleteUsingItemTask.h" +#include "EffectChangedTask.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseItemTask.cpp b/Minecraft.Client/Common/Tutorial/UseItemTask.cpp new file mode 100644 index 0000000..09bac4d --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseItemTask.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "UseItemTask.h" + +UseItemTask::UseItemTask(const int itemId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + itemId( itemId ) +{ +} + +bool UseItemTask::isCompleted() +{ + return bIsCompleted; +} + +void UseItemTask::useItem(shared_ptr item,bool bTestUseOnly) +{ + if(bTestUseOnly) return; + + if( item->id == itemId ) + bIsCompleted = true; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseItemTask.h b/Minecraft.Client/Common/Tutorial/UseItemTask.h new file mode 100644 index 0000000..46d71be --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseItemTask.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +// 4J Stu - Tasks that involve placing a tile +class UseItemTask : public TutorialTask +{ +private: + const int itemId; + bool completed; + +public: + UseItemTask(const int itemId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + virtual bool isCompleted(); + virtual void useItem(shared_ptr item, bool bTestUseOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseTileTask.cpp b/Minecraft.Client/Common/Tutorial/UseTileTask.cpp new file mode 100644 index 0000000..1f4ed4c --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseTileTask.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "UseTileTask.h" + +UseTileTask::UseTileTask(const int tileId, int x, int y, int z, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + x( x ), y( y ), z( z ), tileId( tileId ) +{ + useLocation = true; +} + +UseTileTask::UseTileTask(const int tileId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion, vector *inConstraints, bool bShowMinimumTime, bool bAllowFade, bool bTaskReminders) + : TutorialTask( tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, bTaskReminders ), + tileId( tileId ) +{ + useLocation = false; +} + +bool UseTileTask::isCompleted() +{ + return bIsCompleted; +} + +void UseTileTask::useItemOn(Level *level, shared_ptr item, int x, int y, int z,bool bTestUseOnly) +{ + if(bTestUseOnly) return; + + if( !enablePreCompletion && !bHasBeenActivated) return; + + if( !useLocation || ( x == this->x && y == this->y && z == this->z ) ) + { + int t = level->getTile(x, y, z); + if( t == tileId ) + bIsCompleted = true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/UseTileTask.h b/Minecraft.Client/Common/Tutorial/UseTileTask.h new file mode 100644 index 0000000..74b3a40 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/UseTileTask.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "TutorialTask.h" + +class Level; + +// 4J Stu - Tasks that involve using a tile, with or without an item. e.g. Opening a chest +class UseTileTask : public TutorialTask +{ +private: + int x,y,z; + const int tileId; + bool useLocation; + bool completed; + +public: + UseTileTask(const int tileId, int x, int y, int z, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true ); + UseTileTask(const int tileId, Tutorial *tutorial, int descriptionId, + bool enablePreCompletion = false, vector *inConstraints = NULL, bool bShowMinimumTime = false, bool bAllowFade = true, bool bTaskReminders = true); + virtual bool isCompleted(); + virtual void useItemOn(Level *level, shared_ptr item, int x, int y, int z, bool bTestUseOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp new file mode 100644 index 0000000..71b8847 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#if !(defined _XBOX) && !(defined __PSVITA__) +#include "..\UI\UI.h" +#endif +#include "Tutorial.h" +#include "XuiCraftingTask.h" + +bool XuiCraftingTask::isCompleted() +{ +#ifndef __PSVITA__ + // This doesn't seem to work + //IUIScene_CraftingMenu *craftScene = reinterpret_cast(tutorial->getScene()); +#ifdef _XBOX + CXuiSceneCraftingPanel *craftScene = (CXuiSceneCraftingPanel *)(tutorial->getScene()); +#else + UIScene_CraftingMenu *craftScene = reinterpret_cast(tutorial->getScene()); +#endif + + bool completed = false; + + switch(m_type) + { + case e_Crafting_SelectGroup: + if(craftScene != NULL && craftScene->getCurrentGroup() == m_group) + { + completed = true; + } + break; + case e_Crafting_SelectItem: + if(craftScene != NULL && craftScene->isItemSelected(m_item)) + { + completed = true; + } + break; + } + + return completed; +#else + return true; +#endif +} diff --git a/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h new file mode 100644 index 0000000..2dc4870 --- /dev/null +++ b/Minecraft.Client/Common/Tutorial/XuiCraftingTask.h @@ -0,0 +1,36 @@ +#pragma once +#include "TutorialTask.h" +#include "..\..\..\Minecraft.World\Recipy.h" + +class XuiCraftingTask : public TutorialTask +{ +public: + enum eCraftingTaskType + { + e_Crafting_SelectGroup, + e_Crafting_SelectItem, + }; + + // Select group + XuiCraftingTask(Tutorial *tutorial, int descriptionId, Recipy::_eGroupType groupToSelect, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_group(groupToSelect), + m_type( e_Crafting_SelectGroup ) + {} + + // Select Item + XuiCraftingTask(Tutorial *tutorial, int descriptionId, int itemId, bool enablePreCompletion = false, vector *inConstraints = NULL, + bool bShowMinimumTime=false, bool bAllowFade=true, bool m_bTaskReminders=true ) + : TutorialTask(tutorial, descriptionId, enablePreCompletion, inConstraints, bShowMinimumTime, bAllowFade, m_bTaskReminders ), + m_item(itemId), + m_type( e_Crafting_SelectItem ) + {} + + virtual bool isCompleted(); + +private: + eCraftingTaskType m_type; + Recipy::_eGroupType m_group; + int m_item; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIController.h b/Minecraft.Client/Common/UI/IUIController.h new file mode 100644 index 0000000..319185d --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIController.h @@ -0,0 +1,77 @@ +#pragma once + +#include "UIEnums.h" + +// 4J Stu - An interface class that defines all the public functions that we use within the game code. This allows us to build the Xbox 360 version without +// using the base UIController class used by the other platforms +class IUIController +{ +public: + virtual void tick() = 0; + virtual void render() = 0; + virtual void StartReloadSkinThread() = 0; + virtual bool IsReloadingSkin() = 0; + virtual void CleanUpSkinReload() = 0; + virtual bool NavigateToScene(int iPad, EUIScene scene, void *initData = NULL, EUILayer layer = eUILayer_Scene, EUIGroup group = eUIGroup_PAD) = 0; + virtual bool NavigateBack(int iPad, bool forceUsePad = false, EUIScene eScene = eUIScene_COUNT, EUILayer eLayer = eUILayer_COUNT) = 0; + virtual void CloseUIScenes(int iPad, bool forceIPad = false) = 0; + virtual void CloseAllPlayersScenes() = 0; + + virtual bool IsPauseMenuDisplayed(int iPad) = 0; + virtual bool IsContainerMenuDisplayed(int iPad) = 0; + virtual bool IsIgnorePlayerJoinMenuDisplayed(int iPad) = 0; + virtual bool IsIgnoreAutosaveMenuDisplayed(int iPad) = 0; + virtual void SetIgnoreAutosaveMenuDisplayed(int iPad, bool displayed) = 0; + virtual bool IsSceneInStack(int iPad, EUIScene eScene) = 0; + virtual bool GetMenuDisplayed(int iPad) = 0; + virtual void CheckMenuDisplayed() = 0; + + virtual void SetTooltipText( unsigned int iPad, unsigned int tooltip, int iTextID ) = 0; + virtual void SetEnableTooltips( unsigned int iPad, BOOL bVal ) = 0; + virtual void ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ) = 0; + virtual void SetTooltips( unsigned int iPad, int iA, int iB=-1, int iX=-1, int iY=-1 , int iLT=-1, int iRT=-1, int iLB=-1, int iRB=-1, int iLS=-1, bool forceUpdate = false) = 0; + virtual void EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ) = 0; + virtual void RefreshTooltips(unsigned int iPad) = 0; + + virtual void PlayUISFX(ESoundEffect eSound) = 0; + + virtual void ShowUIDebugConsole(bool show) {} + virtual void ShowUIDebugMarketingGuide(bool show) {} + + virtual void DisplayGamertag(unsigned int iPad, bool show) = 0; + virtual void SetSelectedItem(unsigned int iPad, const wstring &name) = 0; + virtual void UpdateSelectedItemPos(unsigned int iPad) = 0; + + virtual void HandleDLCMountingComplete() = 0; + virtual void HandleDLCInstalled(int iPad) = 0; +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange() = 0; +#endif + virtual void HandleTMSDLCFileRetrieved(int iPad) = 0; + virtual void HandleTMSBanFileRetrieved(int iPad) = 0; + virtual void HandleInventoryUpdated(int iPad) = 0; + virtual void HandleGameTick() = 0; + + virtual void SetTutorialDescription(int iPad, TutorialPopupInfo *info) = 0; + virtual void SetTutorialVisible(int iPad, bool visible) = 0; + virtual bool IsTutorialVisible(int iPad) = 0; + + virtual void UpdatePlayerBasePositions() = 0; + virtual void SetEmptyQuadrantLogo(int iSection) = 0; + virtual void HideAllGameUIElements() = 0; + virtual void ShowOtherPlayersBaseScene(unsigned int iPad, bool show) = 0; + + virtual void ShowTrialTimer(bool show) = 0; + virtual void SetTrialTimerLimitSecs(unsigned int uiSeconds) = 0; + virtual void UpdateTrialTimer(unsigned int iPad) = 0; + virtual void ReduceTrialTimerValue() = 0; + + virtual void ShowAutosaveCountdownTimer(bool show) = 0; + virtual void UpdateAutosaveCountdownTimer(unsigned int uiSeconds) = 0; + virtual void ShowSavingMessage(unsigned int iPad, C4JStorage::ESavingMessage eVal) = 0; + + virtual bool PressStartPlaying(unsigned int iPad) = 0; + virtual void ShowPressStart(unsigned int iPad) = 0; + + virtual void SetWinUserIndex(unsigned int iPad) = 0; +}; diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp new file mode 100644 index 0000000..97e7e9d --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.cpp @@ -0,0 +1,1661 @@ +#include "stdafx.h" + +#include "IUIScene_AbstractContainerMenu.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.crafting.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Minecraft.h" + +#ifdef __ORBIS__ +#include +#endif + +#ifdef _WINDOWS64 +#include "..\..\KeyboardMouseInput.h" +#endif + +IUIScene_AbstractContainerMenu::IUIScene_AbstractContainerMenu() +{ + m_menu = NULL; + m_autoDeleteMenu = false; + m_lastPointerLabelSlot = NULL; + + m_pointerPos.x = 0.0f; + m_pointerPos.y = 0.0f; + +} + +IUIScene_AbstractContainerMenu::~IUIScene_AbstractContainerMenu() +{ + // Delete associated menu if we were requested to on initialisation. Most menus are + // created just before calling CXuiSceneAbstractContainer::Initialize, but the player's inventorymenu + // is also passed directly and we don't want to go deleting that + if( m_autoDeleteMenu ) delete m_menu; +} + +void IUIScene_AbstractContainerMenu::Initialize(int iPad, AbstractContainerMenu* menu, bool autoDeleteMenu, int startIndex,ESceneSection firstSection,ESceneSection maxSection, bool bNavigateBack) +{ + assert( menu != NULL ); + + m_menu = menu; + m_autoDeleteMenu = autoDeleteMenu; + + Minecraft::GetInstance()->localplayers[iPad]->containerMenu = menu; + + // 4J WESTY - New tool tips to support pointer prototype. + //UpdateTooltips(); + // Default tooltips. + for ( int i = 0; i < eToolTipNumButtons; ++i ) + { + m_aeToolTipSettings[ i ] = eToolTipNone; + } + // 4J-PB - don't set the eToolTipPickupPlace_OLD here - let the timer do it. + /*SetToolTip( eToolTipButtonA, eToolTipPickupPlace_OLD );*/ + SetToolTip( eToolTipButtonB, eToolTipExit ); + SetToolTip( eToolTipButtonA, eToolTipNone ); + SetToolTip( eToolTipButtonX, eToolTipNone ); + SetToolTip( eToolTipButtonY, eToolTipNone ); + + // 4J WESTY : To indicate if pointer has left menu window area. + m_bPointerOutsideMenu = false; + + // 4J Stu - Store the enum range for the current scene + m_eFirstSection = firstSection; + m_eMaxSection = maxSection; + + m_iConsectiveInputTicks = 0; + + m_bNavigateBack = bNavigateBack; + + // Put the pointer over first item in use row to start with. +#ifdef TAP_DETECTION + m_eCurrSection = firstSection; + m_eCurrTapState = eTapStateNoInput; + m_iCurrSlotX = 0; + m_iCurrSlotY = 0; +#endif // TAP_DETECTION + // + // for(int i=0;i= columns) + { + *piTargetX = columns - 1; + } + else + { + *piTargetX = offsetX; + } + } + else + { + // Update X + int offsetX = (*piTargetX) - xOffset; + if( offsetX < 0 ) + { + *piTargetX = columns - 1; + } + else if (offsetX >= columns) + { + *piTargetX = 0; + } + else + { + *piTargetX = offsetX; + } + } +} + +#ifdef TAP_DETECTION +IUIScene_AbstractContainerMenu::ETapState IUIScene_AbstractContainerMenu::GetTapInputType( float fInputX, float fInputY ) +{ + if ( ( fabs( fInputX ) < 0.3f ) && ( fabs( fInputY ) < 0.3f ) ) + { + return eTapStateNoInput; + } + else if ( ( fInputX < -0.3f ) && ( fabs( fInputY ) < 0.3f ) ) + { + return eTapStateLeft; + } + else if ( ( fInputX > 0.3f ) && ( fabs( fInputY ) < 0.3f ) ) + { + return eTapStateRight; + } + else if ( ( fInputY < -0.3f ) && ( fabs( fInputX ) < 0.3f ) ) + { + return eTapStateDown; + } + else if ( ( fInputY > 0.3f ) && ( fabs( fInputX ) < 0.3f ) ) + { + return eTapStateUp; + } + else + { + return eTapNone; + } +} +#endif // TAP_DETECTION + +void IUIScene_AbstractContainerMenu::SetToolTip( EToolTipButton eButton, EToolTipItem eItem ) +{ + if ( m_aeToolTipSettings[ eButton ] != eItem ) + { + m_aeToolTipSettings[ eButton ] = eItem; + UpdateTooltips(); + } +} + +void IUIScene_AbstractContainerMenu::UpdateTooltips() +{ + // Table gives us text id for tooltip. + static const DWORD kaToolTipextIds[ eNumToolTips ] = + { + IDS_TOOLTIPS_PICKUPPLACE, //eToolTipPickupPlace_OLD + IDS_TOOLTIPS_EXIT, // eToolTipExit + IDS_TOOLTIPS_PICKUP_GENERIC, // eToolTipPickUpGeneric + IDS_TOOLTIPS_PICKUP_ALL, // eToolTipPickUpAll + IDS_TOOLTIPS_PICKUP_HALF, // eToolTipPickUpHalf + IDS_TOOLTIPS_PLACE_GENERIC, // eToolTipPlaceGeneric + IDS_TOOLTIPS_PLACE_ONE, // eToolTipPlaceOne + IDS_TOOLTIPS_PLACE_ALL, // eToolTipPlaceAll + IDS_TOOLTIPS_DROP_GENERIC, // eToolTipDropGeneric + IDS_TOOLTIPS_DROP_ONE, // eToolTipDropOne + IDS_TOOLTIPS_DROP_ALL, // eToolTipDropAll + IDS_TOOLTIPS_SWAP, // eToolTipSwap + IDS_TOOLTIPS_QUICK_MOVE, // eToolTipQuickMove + IDS_TOOLTIPS_QUICK_MOVE_INGREDIENT, // eToolTipQuickMoveIngredient + IDS_TOOLTIPS_QUICK_MOVE_FUEL, // eToolTipQuickMoveTool + IDS_TOOLTIPS_WHAT_IS_THIS, // eToolTipWhatIsThis + IDS_TOOLTIPS_EQUIP, // eToolTipEquip + IDS_TOOLTIPS_CLEAR_QUICK_SELECT, // eToolTipClearQuickSelect + IDS_TOOLTIPS_QUICK_MOVE_TOOL, // eToolTipQuickMoveTool + IDS_TOOLTIPS_QUICK_MOVE_ARMOR, // eToolTipQuickMoveTool + IDS_TOOLTIPS_QUICK_MOVE_WEAPON, // eToolTipQuickMoveTool + IDS_TOOLTIPS_DYE, // eToolTipDye + IDS_TOOLTIPS_REPAIR, // eToolTipRepair + }; + + BYTE focusUser = getPad(); + + for ( int i = 0; i < eToolTipNumButtons; ++i ) + { + if ( m_aeToolTipSettings[ i ] == eToolTipNone ) + { + ui.ShowTooltip( focusUser, i, FALSE ); + } + else + { + ui.SetTooltipText( focusUser, i, kaToolTipextIds[ m_aeToolTipSettings[ i ] ] ); + ui.ShowTooltip( focusUser, i, TRUE ); + } + } +} + +void IUIScene_AbstractContainerMenu::onMouseTick() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[getPad()] != NULL) + { + Tutorial *tutorial = pMinecraft->localgameModes[getPad()]->getTutorial(); + if(tutorial != NULL) + { + if(ui.IsTutorialVisible(getPad()) && !tutorial->isInputAllowed(ACTION_MENU_UP)) + { + return; + } + } + } + + // Offset to display carried item attached to pointer. + // static const float kfCarriedItemOffsetX = -5.0f; + // static const float kfCarriedItemOffsetY = -5.0f; + float fInputDirX=0.0f; + float fInputDirY=0.0f; + + // Get current pointer position. + UIVec2D vPointerPos = m_pointerPos; + + // Offset to image centre. + vPointerPos.x += m_fPointerImageOffsetX; + vPointerPos.y += m_fPointerImageOffsetY; + + // Get stick input. + int iPad = getPad(); + + bool bStickInput = false; + float fInputX = InputManager.GetJoypadStick_LX( iPad, false )*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f); // apply the sensitivity + float fInputY = InputManager.GetJoypadStick_LY( iPad, false )*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f); // apply the sensitivity + +#ifdef __ORBIS__ + // should have sensitivity for the touchpad + //(float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_TouchPadInMenu)/100.0f + + // get the touchpad input and treat it as a map to the window + ScePadTouchData *pTouchPadData=InputManager.GetTouchPadData(iPad); + + // make sure the touchpad button isn't down (it's the pausemenu) + + if((!InputManager.ButtonDown(iPad, ACTION_MENU_TOUCHPAD_PRESS)) && (pTouchPadData->touchNum>0)) + { + if(m_bFirstTouchStored[iPad]==false) + { + m_oldvTouchPos.x=(float)pTouchPadData->touch[0].x; + m_oldvTouchPos.y=(float)pTouchPadData->touch[0].y; + m_oldvPointerPos.x=vPointerPos.x; + m_oldvPointerPos.y=vPointerPos.y; + m_bFirstTouchStored[iPad]=true; + } + + // should take the average of multiple touch points + + float fNewX=(((float)pTouchPadData->touch[0].x)-m_oldvTouchPos.x) * m_fTouchPadMulX; + float fNewY=(((float)pTouchPadData->touch[0].y)-m_oldvTouchPos.y) * m_fTouchPadMulY; + // relative positions - needs a deadzone + + if(fNewX>m_fTouchPadDeadZoneX) + { + vPointerPos.x=m_oldvPointerPos.x+((fNewX-m_fTouchPadDeadZoneX)*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f)); + } + else if(fNewX<-m_fTouchPadDeadZoneX) + { + vPointerPos.x=m_oldvPointerPos.x+((fNewX+m_fTouchPadDeadZoneX)*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f)); + } + + if(fNewY>m_fTouchPadDeadZoneY) + { + vPointerPos.y=m_oldvPointerPos.y+((fNewY-m_fTouchPadDeadZoneY)*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f)); + } + else if(fNewY<-m_fTouchPadDeadZoneY) + { + vPointerPos.y=m_oldvPointerPos.y+((fNewY+m_fTouchPadDeadZoneY)*((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InMenu)/100.0f)); + } + + // Clamp to pointer extents. + if ( vPointerPos.x < m_fPointerMinX ) vPointerPos.x = m_fPointerMinX; + else if ( vPointerPos.x > m_fPointerMaxX ) vPointerPos.x = m_fPointerMaxX; + if ( vPointerPos.y < m_fPointerMinY ) vPointerPos.y = m_fPointerMinY; + else if ( vPointerPos.y > m_fPointerMaxY ) vPointerPos.y = m_fPointerMaxY; + + bStickInput = true; + m_eCurrTapState=eTapStateNoInput; + } + else + { + // reset the touch flag + m_bFirstTouchStored[iPad]=false; + +#endif + + + + // If there is any input on sticks, move the pointer. + if ( ( fabs( fInputX ) >= 0.01f ) || ( fabs( fInputY ) >= 0.01f ) ) + { + fInputDirX = ( fInputX > 0.0f ) ? 1.0f : ( fInputX < 0.0f )?-1.0f : 0.0f; + fInputDirY = ( fInputY > 0.0f ) ? 1.0f : ( fInputY < 0.0f )?-1.0f : 0.0f; + +#ifdef TAP_DETECTION + // Check for potential tap input to jump slot. + ETapState eNewTapInput = GetTapInputType( fInputX, fInputY ); + + switch( m_eCurrTapState ) + { + case eTapStateNoInput: + m_eCurrTapState = eNewTapInput; + break; + + case eTapStateUp: + case eTapStateDown: + case eTapStateLeft: + case eTapStateRight: + if ( ( eNewTapInput != m_eCurrTapState ) && ( eNewTapInput != eTapStateNoInput ) ) + { + // Input is no longer suitable for tap. + m_eCurrTapState = eTapNone; + } + break; + + case eTapNone: + /// Nothing to do, input is not a tap. + break; + } +#endif // TAP_DETECTION + + // Square it so we get more precision for small inputs. + fInputX = fInputX * fInputX * fInputDirX * POINTER_SPEED_FACTOR; + fInputY = fInputY * fInputY * fInputDirY * POINTER_SPEED_FACTOR; + //fInputX = fInputX * POINTER_SPEED_FACTOR; + //fInputY = fInputY * POINTER_SPEED_FACTOR; + float fInputScale = 1.0f; + + // Ramp up input from zero when new input is recieved over INPUT_TICKS_FOR_SCALING ticks. This is to try to improve tapping stick to move 1 box. + if ( m_iConsectiveInputTicks < MAX_INPUT_TICKS_FOR_SCALING ) + { + ++m_iConsectiveInputTicks; + fInputScale = ( (float)( m_iConsectiveInputTicks) / (float)(MAX_INPUT_TICKS_FOR_SCALING) ); + } +#ifdef TAP_DETECTION + else if ( m_iConsectiveInputTicks < MAX_INPUT_TICKS_FOR_TAPPING ) + { + ++m_iConsectiveInputTicks; + } + else + { + m_eCurrTapState = eTapNone; + } +#endif + // 4J Stu - The cursor moves too fast in SD mode + // The SD/splitscreen scenes are approximately 0.6 times the size of the fullscreen on + if(!RenderManager.IsHiDef() || app.GetLocalPlayerCount() > 1) fInputScale *= 0.6f; + + fInputX *= fInputScale; + fInputY *= fInputScale; + +#ifdef USE_POINTER_ACCEL + m_fPointerAccelX += fInputX / 50.0f; + m_fPointerAccelY += fInputY / 50.0f; + + if ( fabsf( fInputX ) > fabsf( m_fPointerVelX + m_fPointerAccelX ) ) + { + m_fPointerVelX += m_fPointerAccelX; + } + else + { + m_fPointerAccelX = fInputX - m_fPointerVelX; + m_fPointerVelX = fInputX; + } + + if ( fabsf( fInputY ) > fabsf( m_fPointerVelY + m_fPointerAccelY ) ) + { + m_fPointerVelY += m_fPointerAccelY; + } + else + { + m_fPointerAccelY = fInputY - m_fPointerVelY; + m_fPointerVelY = fInputY; + } + //printf( "IN %.2f VEL %.2f ACC %.2f\n", fInputY, m_fPointerVelY, m_fPointerAccelY ); + + vPointerPos.x += m_fPointerVelX; + vPointerPos.y -= m_fPointerVelY; +#else + // Add input to pointer position. + vPointerPos.x += fInputX; + vPointerPos.y -= fInputY; +#endif + // Clamp to pointer extents. + if ( vPointerPos.x < m_fPointerMinX ) vPointerPos.x = m_fPointerMinX; + else if ( vPointerPos.x > m_fPointerMaxX ) vPointerPos.x = m_fPointerMaxX; + if ( vPointerPos.y < m_fPointerMinY ) vPointerPos.y = m_fPointerMinY; + else if ( vPointerPos.y > m_fPointerMaxY ) vPointerPos.y = m_fPointerMaxY; + + bStickInput = true; + } + else + { + m_iConsectiveInputTicks = 0; +#ifdef USE_POINTER_ACCEL + m_fPointerVelX = 0.0f; + m_fPointerVelY = 0.0f; + m_fPointerAccelX = 0.0f; + m_fPointerAccelY = 0.0f; +#endif + } + +#ifdef __ORBIS__ + } +#endif + +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed()) + { + int dx = g_KBMInput.GetMouseDeltaX(); + int dy = g_KBMInput.GetMouseDeltaY(); + if (dx != 0 || dy != 0) + { + float sensitivity = (float)app.GetGameSettings(iPad, eGameSetting_Sensitivity_InMenu) / 100.0f; + float mouseScale = sensitivity * 1.0f; + vPointerPos.x += (float)dx * mouseScale; + vPointerPos.y += (float)dy * mouseScale; + + if (vPointerPos.x < m_fPointerMinX) vPointerPos.x = m_fPointerMinX; + else if (vPointerPos.x > m_fPointerMaxX) vPointerPos.x = m_fPointerMaxX; + if (vPointerPos.y < m_fPointerMinY) vPointerPos.y = m_fPointerMinY; + else if (vPointerPos.y > m_fPointerMaxY) vPointerPos.y = m_fPointerMaxY; + + bStickInput = true; + } + } +#endif + + // Determine which slot the pointer is currently over. + ESceneSection eSectionUnderPointer = eSectionNone; + int iNewSlotX = -1; + int iNewSlotY = -1; + int iNewSlotIndex = -1; + bool bPointerIsOverSlot = false; + + // Centre position of item under pointer, use this to snap pointer to item. + D3DXVECTOR3 vSnapPos; + + for ( int iSection = m_eFirstSection; iSection < m_eMaxSection; ++iSection ) + { + // Do not check any further if we have already found the item under the pointer. + if(m_eCurrTapState == eTapStateJump) + { + eSectionUnderPointer = m_eCurrSection; + } + else if ( eSectionUnderPointer == eSectionNone ) + { + ESceneSection eSection = ( ESceneSection )( iSection ); + + // Get position of this section. + UIVec2D sectionPos; + GetPositionOfSection( eSection, &( sectionPos ) ); + + if(!IsSectionSlotList(eSection)) + { + UIVec2D itemPos; + UIVec2D itemSize; + GetItemScreenData( eSection, 0, &( itemPos ), &( itemSize ) ); + + UIVec2D itemMax = itemSize; + itemMax += itemPos; + + if ( ( vPointerPos.x >= sectionPos.x ) && ( vPointerPos.x <= itemMax.x ) && + ( vPointerPos.y >= sectionPos.y ) && ( vPointerPos.y <= itemMax.y ) ) + { + // Pointer is over this control! + eSectionUnderPointer = eSection; + + vSnapPos.x = itemPos.x + ( itemSize.x / 2.0f ); + vSnapPos.y = itemPos.y + ( itemSize.y / 2.0f ); + + // Does this section already have focus. + if ( !doesSectionTreeHaveFocus( eSection ) ) + { + // Give focus to this section. + setSectionFocus(eSection, getPad()); + } + + bPointerIsOverSlot = false; + + // Have we actually changed slot? If so, input cannot be a tap. + if ( ( eSectionUnderPointer != m_eCurrSection ) || ( iNewSlotX != m_iCurrSlotX ) || ( iNewSlotY != m_iCurrSlotY ) ) + { + m_eCurrTapState = eTapNone; + } + + // Store what is currently under the pointer. + m_eCurrSection = eSectionUnderPointer; + } + } + else + { + + // Get dimensions of this section. + int iNumRows; + int iNumColumns; + int iNumItems = GetSectionDimensions( eSection, &( iNumColumns ), &( iNumRows ) ); + + // Check each item to see if pointer is over it. + for ( int iItem = 0; iItem < iNumItems; ++iItem ) + { + UIVec2D itemPos; + UIVec2D itemSize; + GetItemScreenData( eSection, iItem, &( itemPos ), &( itemSize ) ); + + itemPos += sectionPos; + + UIVec2D itemMax = itemSize; + itemMax += itemPos; + + if ( ( vPointerPos.x >= itemPos.x ) && ( vPointerPos.x <= itemMax.x ) && + ( vPointerPos.y >= itemPos.y ) && ( vPointerPos.y <= itemMax.y ) ) + { + // Pointer is over this slot! + eSectionUnderPointer = eSection; + iNewSlotIndex = iItem; + iNewSlotX = iNewSlotIndex % iNumColumns; + iNewSlotY = iNewSlotIndex / iNumColumns; + + vSnapPos.x = itemPos.x + ( itemSize.x / 2.0f ); + vSnapPos.y = itemPos.y + ( itemSize.y / 2.0f ); + + // Does this section already have focus. + if ( !doesSectionTreeHaveFocus( eSection ) ) + { + // Give focus to this section. + setSectionFocus(eSection, getPad()); + } + + // Set the highlight marker. + setSectionSelectedSlot(eSection, iNewSlotX, iNewSlotY ); + + bPointerIsOverSlot = true; + +#ifdef TAP_DETECTION + // Have we actually changed slot? If so, input cannot be a tap. + if ( ( eSectionUnderPointer != m_eCurrSection ) || ( iNewSlotX != m_iCurrSlotX ) || ( iNewSlotY != m_iCurrSlotY ) ) + { + m_eCurrTapState = eTapNone; + } + + // Store what is currently under the pointer. + m_eCurrSection = eSectionUnderPointer; + m_iCurrSlotX = iNewSlotX; + m_iCurrSlotY = iNewSlotY; +#endif // TAP_DETECTION + // No need to check any further slots, the pointer can only ever be over one. + break; + } + } + } + } + } + + // If we are not over any slot, set focus elsewhere. + if ( eSectionUnderPointer == eSectionNone ) + { + setFocusToPointer( getPad() ); +#ifdef TAP_DETECTION + // Input cannot be a tap. + m_eCurrTapState = eTapNone; + + // Store what is currently under the pointer. + m_eCurrSection = eSectionNone; + m_iCurrSlotX = -1; + m_iCurrSlotY = -1; +#endif // TAP_DETECTION + } + else + { + if ( !bStickInput ) + { + // Did we get a tap input? + int iDesiredSlotX = -1; + int iDesiredSlotY = -1; + + switch( m_eCurrTapState ) + { + case eTapStateUp: + iDesiredSlotX = m_iCurrSlotX; + iDesiredSlotY = m_iCurrSlotY - 1; + break; + case eTapStateDown: + iDesiredSlotX = m_iCurrSlotX; + iDesiredSlotY = m_iCurrSlotY + 1; + break; + case eTapStateLeft: + iDesiredSlotX = m_iCurrSlotX - 1; + iDesiredSlotY = m_iCurrSlotY; + break; + case eTapStateRight: + iDesiredSlotX = m_iCurrSlotX + 1; + iDesiredSlotY = m_iCurrSlotY; + break; + case eTapStateJump: + iDesiredSlotX = m_iCurrSlotX; + iDesiredSlotY = m_iCurrSlotY; + break; + } + + int iNumRows; + int iNumColumns; + int iNumItems = GetSectionDimensions( eSectionUnderPointer, &( iNumColumns ), &( iNumRows ) ); + + + if ( (m_eCurrTapState != eTapNone && m_eCurrTapState != eTapStateNoInput) && + ( !IsSectionSlotList(eSectionUnderPointer) || + ( ( iDesiredSlotX < 0 ) || ( iDesiredSlotX >= iNumColumns ) || ( iDesiredSlotY < 0 ) || ( iDesiredSlotY >= iNumRows ) ) + )) + { + + eSectionUnderPointer = GetSectionAndSlotInDirection( eSectionUnderPointer, m_eCurrTapState, &iDesiredSlotX, &iDesiredSlotY ); + + if(!IsSectionSlotList(eSectionUnderPointer)) bPointerIsOverSlot = false; + + // Get the details for the new section + iNumItems = GetSectionDimensions( eSectionUnderPointer, &( iNumColumns ), &( iNumRows ) ); + } + + if ( !IsSectionSlotList(eSectionUnderPointer) || ( ( iDesiredSlotX >= 0 ) && ( iDesiredSlotX < iNumColumns ) && ( iDesiredSlotY >= 0 ) && ( iDesiredSlotY < iNumRows ) ) ) + { + // Desired slot after tap input is valid, so make the jump to this slot. + UIVec2D sectionPos; + GetPositionOfSection( eSectionUnderPointer, &( sectionPos ) ); + + iNewSlotIndex = ( iDesiredSlotY * iNumColumns ) + iDesiredSlotX; + + UIVec2D itemPos; + UIVec2D itemSize; + GetItemScreenData( eSectionUnderPointer, iNewSlotIndex, &( itemPos ), &( itemSize ) ); + + if(IsSectionSlotList(eSectionUnderPointer)) itemPos += sectionPos; + + vSnapPos.x = itemPos.x + ( itemSize.x / 2.0f); + vSnapPos.y = itemPos.y + ( itemSize.y / 2.0f); + + m_eCurrSection = eSectionUnderPointer; + m_iCurrSlotX = iDesiredSlotX; + m_iCurrSlotY = iDesiredSlotY; + } + + m_eCurrTapState = eTapStateNoInput; + + // If there is no stick input, and we are over a slot, then snap pointer to slot centre. + // 4J - TomK - only if this particular component allows so! + if(CanHaveFocus(eSectionUnderPointer)) + { + vPointerPos.x = vSnapPos.x; + vPointerPos.y = vSnapPos.y; + } + } + } + + // Clamp to pointer extents. + if ( vPointerPos.x < m_fPointerMinX ) vPointerPos.x = m_fPointerMinX; + else if ( vPointerPos.x > m_fPointerMaxX ) vPointerPos.x = m_fPointerMaxX; + if ( vPointerPos.y < m_fPointerMinY ) vPointerPos.y = m_fPointerMinY; + else if ( vPointerPos.y > m_fPointerMaxY ) vPointerPos.y = m_fPointerMaxY; + + // Check if the pointer is outside of the panel. + bool bPointerIsOutsidePanel = false; + if ( ( vPointerPos.x < m_fPanelMinX ) || ( vPointerPos.x > m_fPanelMaxX ) || ( vPointerPos.y < m_fPanelMinY ) || ( vPointerPos.y > m_fPanelMaxY ) ) + { + bPointerIsOutsidePanel = true; + } + + // Determine appropriate context sensitive tool tips, based on what is carried on the pointer and what is under the pointer. + + // What are we carrying on pointer. + shared_ptr player = Minecraft::GetInstance()->localplayers[getPad()]; + shared_ptr carriedItem = nullptr; + if(player != NULL) carriedItem = player->inventory->getCarried(); + + shared_ptr slotItem = nullptr; + Slot *slot = NULL; + int slotIndex = 0; + if(bPointerIsOverSlot) + { + slotIndex = iNewSlotIndex + getSectionStartOffset( eSectionUnderPointer ); + slot = m_menu->getSlot(slotIndex); + } + bool bIsItemCarried = carriedItem != NULL; + int iCarriedCount = 0; + bool bCarriedIsSameAsSlot = false; // Indicates if same item is carried on pointer as is in slot under pointer. + if ( bIsItemCarried ) + { + iCarriedCount = carriedItem->count; + } + + // What is in the slot that we are over. + bool bSlotHasItem = false; + bool bMayPlace = false; + bool bCanPlaceOne = false; + bool bCanPlaceAll = false; + bool bCanCombine = false; + bool bCanDye = false; + int iSlotCount = 0; + int iSlotStackSizeRemaining = 0; // How many more items can be stacked on this slot. + if ( bPointerIsOverSlot ) + { + slotItem = slot->getItem(); + bSlotHasItem = slotItem != NULL; + if ( bSlotHasItem ) + { + iSlotCount = slotItem->GetCount(); + + if ( bIsItemCarried ) + { + bCarriedIsSameAsSlot = IsSameItemAs(carriedItem, slotItem); + bCanCombine = m_menu->mayCombine(slot,carriedItem); + bCanDye = bCanCombine && dynamic_cast(slot->getItem()->getItem()); + + if ( bCarriedIsSameAsSlot ) + { + iSlotStackSizeRemaining = GetEmptyStackSpace( m_menu->getSlot(slotIndex) ); + } + } + } + + if( bIsItemCarried) + { + bMayPlace = slot->mayPlace(carriedItem); + + if ( bSlotHasItem ) iSlotStackSizeRemaining = GetEmptyStackSpace( slot ); + else iSlotStackSizeRemaining = slot->getMaxStackSize(); + + if(bMayPlace && iSlotStackSizeRemaining > 0) bCanPlaceOne = true; + if(bMayPlace && iSlotStackSizeRemaining > 1 && carriedItem->count > 1) bCanPlaceAll = true; + } + } + + if( bPointerIsOverSlot && bSlotHasItem ) + { + vector unformattedStrings; + wstring desc = GetItemDescription( slot, unformattedStrings ); + SetPointerText(desc, unformattedStrings, slot != m_lastPointerLabelSlot); + m_lastPointerLabelSlot = slot; + } + else + { + vector unformattedStrings; + SetPointerText(L"", unformattedStrings, false); + m_lastPointerLabelSlot = NULL; + } + + EToolTipItem buttonA, buttonX, buttonY, buttonRT; + buttonA = buttonX = buttonY = buttonRT = eToolTipNone; + if ( bPointerIsOverSlot ) + { + SetPointerOutsideMenu( false ); + if ( bIsItemCarried ) + { + if ( bSlotHasItem ) + { + // Item in hand and item in slot ... is item in slot the same as in out hand? If so, can we stack on to it? + if ( bCarriedIsSameAsSlot ) + { + // Can we stack more into this slot? + if ( iSlotStackSizeRemaining == 0 ) + { + // Cannot stack any more. + buttonRT = eToolTipWhatIsThis; + } + else if ( iSlotStackSizeRemaining == 1 ) + { + // Can only put 1 more on the stack. + buttonA = eToolTipPlaceGeneric; + buttonRT = eToolTipWhatIsThis; + } + else // can put 1 or all. + { + if(bCanPlaceAll) + { + // Multiple items in hand. + buttonA = eToolTipPlaceAll; + buttonX = eToolTipPlaceOne; + } + else if(bCanPlaceOne) + { + if(iCarriedCount > 1) buttonA = eToolTipPlaceOne; + else buttonA = eToolTipPlaceGeneric; + } + buttonRT = eToolTipWhatIsThis; + } + } + else // items are different, click here will swap them. + { + + if(bMayPlace) buttonA = eToolTipSwap; + buttonRT = eToolTipWhatIsThis; + } + if(bCanDye) + { + buttonX = eToolTipDye; + } + else if(bCanCombine) + { + buttonX = eToolTipRepair; + } + } + else // slot empty. + { + // Item in hand, slot is empty. + if ( iCarriedCount == 1 ) + { + // Only one item in hand. + buttonA = eToolTipPlaceGeneric; + } + else + { + if(bCanPlaceAll) + { + // Multiple items in hand. + buttonA = eToolTipPlaceAll; + buttonX = eToolTipPlaceOne; + } + else if(bCanPlaceOne) + { + buttonA = eToolTipPlaceOne; + } + } + } + } + else // no object in hand + { + if ( bSlotHasItem ) + { + if ( iSlotCount == 1 ) + { + buttonA = eToolTipPickUpGeneric; + buttonRT = eToolTipWhatIsThis; + } + else + { + // Multiple items in slot. + buttonA = eToolTipPickUpAll; + buttonX = eToolTipPickUpHalf; + buttonRT = eToolTipWhatIsThis; + } + } + else + { + // Nothing in slot and nothing in hand. + } + } + + if ( bSlotHasItem ) + { + // Item in slot + + // 4J-PB - show tooltips for quick use of armour + + if((eSectionUnderPointer==eSectionInventoryUsing)||(eSectionUnderPointer==eSectionInventoryInventory)) + { + shared_ptr item = getSlotItem(eSectionUnderPointer, iNewSlotIndex); + ArmorRecipes::_eArmorType eArmourType=ArmorRecipes::GetArmorType(item->id); + + if(eArmourType==ArmorRecipes::eArmorType_None) + { + buttonY = eToolTipQuickMove; + } + else + { + // check that the slot required is empty + switch(eArmourType) + { + case ArmorRecipes::eArmorType_Helmet: + if(isSlotEmpty(eSectionInventoryArmor,0)) + { + buttonY = eToolTipEquip; + } + else + { + buttonY = eToolTipQuickMove; + } + break; + case ArmorRecipes::eArmorType_Chestplate: + if(isSlotEmpty(eSectionInventoryArmor,1)) + { + buttonY = eToolTipEquip; + } + else + { + buttonY = eToolTipQuickMove; + } + break; + case ArmorRecipes::eArmorType_Leggings: + if(isSlotEmpty(eSectionInventoryArmor,2)) + { + buttonY = eToolTipEquip; + } + else + { + buttonY = eToolTipQuickMove; + } + break; + case ArmorRecipes::eArmorType_Boots: + if(isSlotEmpty(eSectionInventoryArmor,3)) + { + buttonY = eToolTipEquip; + } + else + { + buttonY = eToolTipQuickMove; + } + break; + default: + buttonY = eToolTipQuickMove; + break; + } + + } + } + // 4J-PB - show tooltips for quick use of fuel or ingredient + else if((eSectionUnderPointer==eSectionFurnaceUsing)||(eSectionUnderPointer==eSectionFurnaceInventory)) + { + // Get the info on this item. + shared_ptr item = getSlotItem(eSectionUnderPointer, iNewSlotIndex); + bool bValidFuel = FurnaceTileEntity::isFuel(item); + bool bValidIngredient = FurnaceRecipes::getInstance()->getResult(item->getItem()->id) != NULL; + + if(bValidIngredient) + { + // is there already something in the ingredient slot? + if(!isSlotEmpty(eSectionFurnaceIngredient,0)) + { + // is it the same as this item + shared_ptr IngredientItem = getSlotItem(eSectionFurnaceIngredient,0); + if(IngredientItem->id == item->id) + { + buttonY = eToolTipQuickMoveIngredient; + } + else + { + if(FurnaceRecipes::getInstance()->getResult(item->id)==NULL) + { + buttonY = eToolTipQuickMove; + } + else + { + buttonY = eToolTipQuickMoveIngredient; + } + } + } + else + { + // ingredient slot empty + buttonY = eToolTipQuickMoveIngredient; + } + } + else if(bValidFuel) + { + // Is there already something in the fuel slot? + if(!isSlotEmpty(eSectionFurnaceFuel,0)) + { + // is it the same as this item + shared_ptr fuelItem = getSlotItem(eSectionFurnaceFuel,0); + if(fuelItem->id == item->id) + { + buttonY = eToolTipQuickMoveFuel; + } + else if(bValidIngredient) + { + // check if the ingredient slot is empty, or the same as this + if(!isSlotEmpty(eSectionFurnaceIngredient,0)) + { + // is it the same as this item + shared_ptr IngredientItem = getSlotItem(eSectionFurnaceIngredient,0); + if(IngredientItem->id == item->id) + { + buttonY = eToolTipQuickMoveIngredient; + } + else + { + if(FurnaceRecipes::getInstance()->getResult(item->id)==NULL) + { + buttonY = eToolTipQuickMove; + } + else + { + buttonY = eToolTipQuickMoveIngredient; + } + } + } + else + { + // ingredient slot empty + buttonY = eToolTipQuickMoveIngredient; + } + } + else + { + buttonY = eToolTipQuickMove; + } + } + else + { + buttonY = eToolTipQuickMoveFuel; + } + } + else + { + buttonY = eToolTipQuickMove; + } + } + // 4J-PB - show tooltips for quick use of ingredients in brewing + else if((eSectionUnderPointer==eSectionBrewingUsing)||(eSectionUnderPointer==eSectionBrewingInventory)) + { + // Get the info on this item. + shared_ptr item = getSlotItem(eSectionUnderPointer, iNewSlotIndex); + int iId=item->id; + + // valid ingredient? + bool bValidIngredient=false; + //bool bValidIngredientBottom=false; + + if(Item::items[iId]->hasPotionBrewingFormula() || (iId == Item::netherStalkSeeds_Id)) + { + bValidIngredient=true; + } + + if(bValidIngredient) + { + // is there already something in the ingredient slot? + if(!isSlotEmpty(eSectionBrewingIngredient,0)) + { + // is it the same as this item + shared_ptr IngredientItem = getSlotItem(eSectionBrewingIngredient,0); + if(IngredientItem->id == item->id) + { + buttonY = eToolTipQuickMoveIngredient; + } + else + { + buttonY=eToolTipQuickMove; + } + } + else + { + // ingredient slot empty + buttonY = eToolTipQuickMoveIngredient; + } + } + else + { + // valid potion? Glass bottle with water in it is a 'potion' too. + if(iId==Item::potion_Id) + { + // space available? + if(isSlotEmpty(eSectionBrewingBottle1,0) || + isSlotEmpty(eSectionBrewingBottle2,0) || + isSlotEmpty(eSectionBrewingBottle3,0)) + { + buttonY = eToolTipQuickMoveIngredient; + } + else + { + buttonY=eToolTipNone; + } + } + else + { + buttonY=eToolTipQuickMove; + } + } + } + else if((eSectionUnderPointer==eSectionEnchantUsing)||(eSectionUnderPointer==eSectionEnchantInventory)) + { + // Get the info on this item. + shared_ptr item = getSlotItem(eSectionUnderPointer, iNewSlotIndex); + int iId=item->id; + + // valid enchantable tool? + if(Item::items[iId]->isEnchantable(item)) + { + // is there already something in the ingredient slot? + if(isSlotEmpty(eSectionEnchantSlot,0)) + { + // tool slot empty + switch(iId) + { + case Item::bow_Id: + case Item::sword_wood_Id: + case Item::sword_stone_Id: + case Item::sword_iron_Id: + case Item::sword_diamond_Id: + buttonY=eToolTipQuickMoveWeapon; + break; + + case Item::helmet_cloth_Id: + case Item::chestplate_cloth_Id: + case Item::leggings_cloth_Id: + case Item::boots_cloth_Id: + + case Item::helmet_chain_Id: + case Item::chestplate_chain_Id: + case Item::leggings_chain_Id: + case Item::boots_chain_Id: + + case Item::helmet_iron_Id: + case Item::chestplate_iron_Id: + case Item::leggings_iron_Id: + case Item::boots_iron_Id: + + case Item::helmet_diamond_Id: + case Item::chestplate_diamond_Id: + case Item::leggings_diamond_Id: + case Item::boots_diamond_Id: + + case Item::helmet_gold_Id: + case Item::chestplate_gold_Id: + case Item::leggings_gold_Id: + case Item::boots_gold_Id: + buttonY=eToolTipQuickMoveArmor; + + break; + case Item::book_Id: + buttonY = eToolTipQuickMove; + break; + default: + buttonY=eToolTipQuickMoveTool; + break; + } + } + else + { + buttonY = eToolTipQuickMove; + } + } + else + { + buttonY=eToolTipQuickMove; + } + } + else + { + buttonY = eToolTipQuickMove; + } + } + } + + if ( bPointerIsOutsidePanel ) + { + SetPointerOutsideMenu( true ); + // Outside window, we dropping items. + if ( bIsItemCarried ) + { + //int iCount = m_pointerControl->GetObjectCount( m_pointerControl->m_hObj ); + if ( iCarriedCount > 1 ) + { + buttonA = eToolTipDropAll; + buttonX = eToolTipDropOne; + } + else + { + buttonA = eToolTipDropGeneric; + } + } + } + else // pointer is just over dead space ... can't really do anything. + { + SetPointerOutsideMenu( false ); + } + + shared_ptr item = nullptr; + if(bPointerIsOverSlot && bSlotHasItem) item = getSlotItem(eSectionUnderPointer, iNewSlotIndex); + overrideTooltips(eSectionUnderPointer, item, bIsItemCarried, bSlotHasItem, bCarriedIsSameAsSlot, iSlotStackSizeRemaining, buttonA, buttonX, buttonY, buttonRT); + + SetToolTip( eToolTipButtonA, buttonA ); + SetToolTip( eToolTipButtonX, buttonX ); + SetToolTip( eToolTipButtonY, buttonY ); + SetToolTip( eToolTipButtonRT, buttonRT ); + + + // Offset back to image top left. + vPointerPos.x -= m_fPointerImageOffsetX; + vPointerPos.y -= m_fPointerImageOffsetY; + + // Update pointer position. + // 4J-PB - do not allow sub pixel positions or we get broken lines in box edges + + // problem here when sensitivity is low - we'll be moving a sub pixel size, so it'll clamp, and we'll never move. In that case, move 1 pixel + if(fInputDirX!=0.0f) + { + if(fInputDirX==1.0f) + { + vPointerPos.x+=0.999999f; + } + else + { + vPointerPos.x-=0.999999f; + } + } + + if(fInputDirY!=0.0f) + { + if(fInputDirY==1.0f) + { + vPointerPos.y+=0.999999f; + } + else + { + vPointerPos.y-=0.999999f; + } + } + + vPointerPos.x = floor(vPointerPos.x); + vPointerPos.x += ( (int)vPointerPos.x%2); + vPointerPos.y = floor(vPointerPos.y); + vPointerPos.y += ( (int)vPointerPos.y%2); + m_pointerPos = vPointerPos; + + adjustPointerForSafeZone(); +} + +bool IUIScene_AbstractContainerMenu::handleKeyDown(int iPad, int iAction, bool bRepeat) +{ + bool bHandled = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[getPad()] != NULL ) + { + Tutorial *tutorial = pMinecraft->localgameModes[getPad()]->getTutorial(); + if(tutorial != NULL) + { + tutorial->handleUIInput(iAction); + if(ui.IsTutorialVisible(getPad()) && !tutorial->isInputAllowed(iAction)) + { + return S_OK; + } + } + } + +#ifdef _XBOX + ui.AnimateKeyPress(iPad, iAction); +#else + ui.AnimateKeyPress(iPad, iAction, bRepeat, true, false); +#endif + + int buttonNum=0; // 0 = LeftMouse, 1 = RightMouse + BOOL quickKeyHeld=FALSE; // Represents shift key on PC + + BOOL validKeyPress = FALSE; + //BOOL itemEditorKeyPress = FALSE; + + // Ignore input from other players + //if(pMinecraft->player->GetXboxPad()!=pInputData->UserIndex) return S_OK; + + switch(iAction) + { +#ifdef _DEBUG_MENUS_ENABLED +#if TO_BE_IMPLEMENTED + case VK_PAD_RTHUMB_PRESS: + itemEditorKeyPress = TRUE; + break; +#endif +#endif + case ACTION_MENU_A: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(!bRepeat) + { + validKeyPress = TRUE; + + // Standard left click + buttonNum = 0; + quickKeyHeld = FALSE; + ui.PlayUISFX(eSFX_Press); + } + break; + case ACTION_MENU_X: + if(!bRepeat) + { + validKeyPress = TRUE; + + // Standard right click + buttonNum = 1; + quickKeyHeld = FALSE; + ui.PlayUISFX(eSFX_Press); + } + break; + case ACTION_MENU_Y: + if(!bRepeat) + { + //bool bIsItemCarried = !m_pointerControl->isEmpty( m_pointerControl->m_hObj ); + + // 4J Stu - TU8: Remove this fix, and fix the tooltip display instead as customers liked the feature + + // Fix for #58583 - TU6: Content: UI: The Quick Move button prompt disappears even though it still works + // No quick move tooltip is shown if something is carried, so disable the action as well + //if(!bIsItemCarried) + { + validKeyPress = TRUE; + + // Shift and left click + buttonNum = 0; + quickKeyHeld = TRUE; + ui.PlayUISFX(eSFX_Press); + } + } + break; + // 4J Stu - Also enable start to exit the scene. This key is also not constrained by the tutorials. + case ACTION_MENU_PAUSEMENU: + case ACTION_MENU_B: + { + + ui.SetTooltips(iPad, -1); + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + // Therefore I have moved this call to the OnDestroy() method to make sure that it always happens. + //Minecraft::GetInstance()->localplayers[pInputData->UserIndex]->closeContainer(); + + // Return to the game. We should really callback to the app here as well + // to let it know that we have closed the ui incase we need to do things when that happens + + if(m_bNavigateBack) + { + ui.NavigateBack(iPad); + } + else + { + ui.CloseUIScenes(iPad); + } + + bHandled = true; + return S_OK; + } + break; + case ACTION_MENU_LEFT: + { + //ui.PlayUISFX(eSFX_Focus); + m_eCurrTapState = eTapStateLeft; + } + break; + case ACTION_MENU_RIGHT: + { + //ui.PlayUISFX(eSFX_Focus); + m_eCurrTapState = eTapStateRight; + } + break; + case ACTION_MENU_UP: + { + //ui.PlayUISFX(eSFX_Focus); + m_eCurrTapState = eTapStateUp; + } + break; + case ACTION_MENU_DOWN: + { + //ui.PlayUISFX(eSFX_Focus); + m_eCurrTapState = eTapStateDown; + } + break; + case ACTION_MENU_PAGEUP: + { + // 4J Stu - Do nothing except stop this being passed anywhere else + bHandled = true; + } + break; +#ifdef __PSVITA__ + //CD - Vita uses select for What's this - key 40 + case MINECRAFT_ACTION_GAME_INFO: +#else + case ACTION_MENU_PAGEDOWN: +#endif + + { + if( IsSectionSlotList( m_eCurrSection ) ) + { + int currentIndex = getCurrentIndex( m_eCurrSection ) - getSectionStartOffset(m_eCurrSection); + + bool bSlotHasItem = !isSlotEmpty(m_eCurrSection, currentIndex); + if ( bSlotHasItem ) + { + shared_ptr item = getSlotItem(m_eCurrSection, currentIndex); + if( Minecraft::GetInstance()->localgameModes[iPad] != NULL ) + { + Tutorial::PopupMessageDetails *message = new Tutorial::PopupMessageDetails; + message->m_messageId = item->getUseDescriptionId(); + + if(Item::items[item->id] != NULL) message->m_titleString = Item::items[item->id]->getHoverName(item); + message->m_titleId = item->getDescriptionId(); + + message->m_icon = item->id; + message->m_iAuxVal = item->getAuxValue(); + message->m_forceDisplay = true; + + TutorialMode *gameMode = (TutorialMode *)Minecraft::GetInstance()->localgameModes[iPad]; + gameMode->getTutorial()->setMessage(NULL, message); + ui.PlayUISFX(eSFX_Press); + } + } + } + bHandled = TRUE; + } + break; + }; + + if( validKeyPress == TRUE ) + { + if(handleValidKeyPress(iPad,buttonNum,quickKeyHeld)) + { + // Used to allow overriding certain keypresses, so do nothing here + } + else + { + if( IsSectionSlotList( m_eCurrSection ) ) + { + handleSlotListClicked(m_eCurrSection,buttonNum,quickKeyHeld); + } + else + { + // TODO Clicked something else, like for example the craft result. Do something here + + // 4J WESTY : For pointer system we can legally drop items outside of the window panel here, or may press button while + // pointer is over empty panel space. + if ( m_bPointerOutsideMenu ) + { + handleOutsideClicked(iPad, buttonNum, quickKeyHeld); + } + else // + { + // over empty space or something else??? + handleOtherClicked(iPad,m_eCurrSection,buttonNum,quickKeyHeld?true:false); + //assert( FALSE ); + } + } + } + bHandled = true; + } +#ifdef _DEBUG_MENUS_ENABLED +#if TO_BE_IMPLEMENTED + else if(itemEditorKeyPress == TRUE) + { + HXUIOBJ hFocusObject = GetFocus(pInputData->UserIndex); + HXUIOBJ hFocusObjectParent; + XuiElementGetParent( hFocusObject, &hFocusObjectParent ); + + HXUICLASS hClassCXuiCtrlSlotList; + + // TODO Define values for these + hClassCXuiCtrlSlotList = XuiFindClass( L"CXuiCtrlSlotList" ); + + // If the press comes from a SlotList, cast it up then send a clicked call to it's menu + if( XuiIsInstanceOf( hFocusObjectParent, hClassCXuiCtrlSlotList ) ) + { + CXuiCtrlSlotList* slotList; + VOID *pObj; + XuiObjectFromHandle( hFocusObjectParent, &pObj ); + slotList = (CXuiCtrlSlotList *)pObj; + + int currentIndex = slotList->GetCurSel(); + + CXuiCtrlSlotItemListItem* pCXuiCtrlSlotItem; + slotList->GetCXuiCtrlSlotItem( currentIndex, &( pCXuiCtrlSlotItem ) ); + + //Minecraft *pMinecraft = Minecraft::GetInstance(); + + CScene_DebugItemEditor::ItemEditorInput *initData = new CScene_DebugItemEditor::ItemEditorInput(); + initData->iPad = m_iPad; + initData->slot = pCXuiCtrlSlotItem->getSlot( pCXuiCtrlSlotItem->m_hObj ); + initData->menu = m_menu; + + // Add timer to poll controller stick input at 60Hz + HRESULT timerResult = KillTimer( POINTER_INPUT_TIMER_ID ); + assert( timerResult == S_OK ); + + app.NavigateToScene(m_iPad,eUIScene_DebugItemEditor,(void *)initData,false,TRUE); + } + } +#endif +#endif + else + { + handleAdditionalKeyPress(iAction); + } + + UpdateTooltips(); + + return bHandled; +} + +bool IUIScene_AbstractContainerMenu::handleValidKeyPress(int iUserIndex, int buttonNum, BOOL quickKeyHeld) +{ + return false; +} + +void IUIScene_AbstractContainerMenu::handleOutsideClicked(int iPad, int buttonNum, BOOL quickKeyHeld) +{ + // Drop items. + + //pMinecraft->localgameModes[m_iPad]->handleInventoryMouseClick(menu->containerId, AbstractContainerMenu::CLICKED_OUTSIDE, buttonNum, quickKeyHeld?true:false, pMinecraft->localplayers[m_iPad] ); + slotClicked(AbstractContainerMenu::CLICKED_OUTSIDE, buttonNum, quickKeyHeld?true:false); +} + +void IUIScene_AbstractContainerMenu::handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey) +{ + // Do nothing +} + +void IUIScene_AbstractContainerMenu::handleAdditionalKeyPress(int iAction) +{ + // Do nothing +} + +void IUIScene_AbstractContainerMenu::handleSlotListClicked(ESceneSection eSection, int buttonNum, BOOL quickKeyHeld) +{ + int currentIndex = getCurrentIndex(eSection); + + //pMinecraft->localgameModes[m_iPad]->handleInventoryMouseClick(menu->containerId, currentIndex, buttonNum, quickKeyHeld?true:false, pMinecraft->localplayers[m_iPad] ); + slotClicked(currentIndex, buttonNum, quickKeyHeld?true:false); + + handleSectionClick(eSection); +} + +void IUIScene_AbstractContainerMenu::slotClicked(int slotId, int buttonNum, bool quickKey) +{ + // 4J Stu - Removed this line as unused + //if (slot != NULL) slotId = slot->index; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->localgameModes[getPad()]->handleInventoryMouseClick(m_menu->containerId, slotId, buttonNum, quickKey, pMinecraft->localplayers[getPad()] ); +} + +int IUIScene_AbstractContainerMenu::getCurrentIndex(ESceneSection eSection) +{ + int rows, columns; + GetSectionDimensions( eSection, &columns, &rows ); + int currentIndex = (m_iCurrSlotY * columns) + m_iCurrSlotX; + + return currentIndex + getSectionStartOffset(eSection); +} + +bool IUIScene_AbstractContainerMenu::IsSameItemAs(shared_ptr itemA, shared_ptr itemB) +{ + if(itemA == NULL || itemB == NULL) return false; + + bool bStackedByData = itemA->isStackedByData(); + return ( ( itemA->id == itemB->id ) && ( (bStackedByData && itemA->getAuxValue() == itemB->getAuxValue()) || !bStackedByData ) ); +} + +int IUIScene_AbstractContainerMenu::GetEmptyStackSpace(Slot *slot) +{ + int iResult = 0; + + if(slot != NULL && slot->hasItem()) + { + shared_ptr item = slot->getItem(); + if ( item->isStackable() ) + { + int iCount = item->GetCount(); + int iMaxStackSize = min(item->getMaxStackSize(), slot->getMaxStackSize() ); + + iResult = iMaxStackSize - iCount; + + if(iResult < 0 ) iResult = 0; + } + } + + return iResult; +} + +wstring IUIScene_AbstractContainerMenu::GetItemDescription(Slot *slot, vector &unformattedStrings) +{ + if(slot == NULL) return L""; + + wstring desc = L""; + vector *strings = slot->getItem()->getHoverText(nullptr, false, unformattedStrings); + bool firstLine = true; + for(AUTO_VAR(it, strings->begin()); it != strings->end(); ++it) + { + wstring thisString = *it; + if(!firstLine) + { + desc.append( L"
" ); + } + else + { + firstLine = false; + wchar_t formatted[256]; + eMinecraftColour rarityColour = slot->getItem()->getRarity()->color; + int colour = app.GetHTMLColour(rarityColour); + + if(slot->getItem()->hasCustomHoverName()) + { + colour = app.GetHTMLColour(eTextColor_RenamedItemTitle); + } + + swprintf(formatted, 256, L"%ls",colour,thisString.c_str()); + thisString = formatted; + } + desc.append( thisString ); + } + strings->clear(); + delete strings; + return desc; +} diff --git a/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h new file mode 100644 index 0000000..bdb8bb4 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_AbstractContainerMenu.h @@ -0,0 +1,223 @@ +#pragma once + +// Uncomment to enable tap input detection to jump 1 slot. Doesn't work particularly well yet, and I feel the system does not need it. +// Would probably be required if we decide to slow down the pointer movement. +// 4J Stu - There was a request to be able to navigate the scenes with the dpad, so I have used much of the TAP_DETECTION +// code as it worked well for that situation. This #define should still stop the same things happening when using the +// stick though when not defined +#define TAP_DETECTION + +// Uncomment to enable acceleration on pointer input. +//#define USE_POINTER_ACCEL + +#define POINTER_INPUT_TIMER_ID (0) // Arbitrary timer ID. +#define POINTER_SPEED_FACTOR (13.0f) // Speed of pointer. +//#define POINTER_PANEL_OVER_REACH (42.0f) // Amount beyond edge of panel which pointer can go over to drop items. - comes from the pointer size in the scene + +#define MAX_INPUT_TICKS_FOR_SCALING (7) +#define MAX_INPUT_TICKS_FOR_TAPPING (15) + +class AbstractContainerMenu; +class Slot; + +class IUIScene_AbstractContainerMenu +{ +protected: + // Sections of this scene containing items selectable by the pointer. + // 4J Stu - Always make the Using section the first one + enum ESceneSection + { + eSectionNone = -1, + eSectionContainerUsing = 0, + eSectionContainerInventory, + eSectionContainerChest, + eSectionContainerMax, + + eSectionFurnaceUsing, + eSectionFurnaceInventory, + eSectionFurnaceIngredient, + eSectionFurnaceFuel, + eSectionFurnaceResult, + eSectionFurnaceMax, + + eSectionInventoryUsing, + eSectionInventoryInventory, + eSectionInventoryArmor, + eSectionInventoryMax, + + eSectionTrapUsing, + eSectionTrapInventory, + eSectionTrapTrap, + eSectionTrapMax, + + eSectionInventoryCreativeUsing, + eSectionInventoryCreativeSelector, +#ifndef _XBOX + eSectionInventoryCreativeTab_0, + eSectionInventoryCreativeTab_1, + eSectionInventoryCreativeTab_2, + eSectionInventoryCreativeTab_3, + eSectionInventoryCreativeTab_4, + eSectionInventoryCreativeTab_5, + eSectionInventoryCreativeTab_6, + eSectionInventoryCreativeTab_7, + eSectionInventoryCreativeSlider, +#endif + eSectionInventoryCreativeMax, + + eSectionEnchantUsing, + eSectionEnchantInventory, + eSectionEnchantSlot, + eSectionEnchantButton1, + eSectionEnchantButton2, + eSectionEnchantButton3, + eSectionEnchantMax, + + eSectionBrewingUsing, + eSectionBrewingInventory, + eSectionBrewingBottle1, + eSectionBrewingBottle2, + eSectionBrewingBottle3, + eSectionBrewingIngredient, + eSectionBrewingMax, + + eSectionAnvilUsing, + eSectionAnvilInventory, + eSectionAnvilItem1, + eSectionAnvilItem2, + eSectionAnvilResult, + eSectionAnvilName, + eSectionAnvilMax, + }; + + AbstractContainerMenu* m_menu; + bool m_autoDeleteMenu; + + eTutorial_State m_previousTutorialState; + + UIVec2D m_pointerPos; + + // Offset from pointer image top left to centre (we use the centre as the actual pointer). + float m_fPointerImageOffsetX; + float m_fPointerImageOffsetY; + + // Min and max extents for the pointer. + float m_fPointerMinX; + float m_fPointerMaxX; + float m_fPointerMinY; + float m_fPointerMaxY; + + // Min and max extents of the panel. + float m_fPanelMinX; + float m_fPanelMaxX; + float m_fPanelMinY; + float m_fPanelMaxY; + + int m_iConsectiveInputTicks; + + // Used for detecting quick "taps" in a direction, should jump cursor to next slot. + enum ETapState + { + eTapStateNoInput = 0, + eTapStateUp, + eTapStateDown, + eTapStateLeft, + eTapStateRight, + eTapStateJump, + eTapNone + }; + + ETapState m_eCurrTapState; + ESceneSection m_eCurrSection; + int m_iCurrSlotX; + int m_iCurrSlotY; + +#ifdef __ORBIS__ + bool m_bFirstTouchStored[XUSER_MAX_COUNT]; // monitor the first position of a touch, so we can use relative distances of movement + UIVec2D m_oldvPointerPos; + UIVec2D m_oldvTouchPos; + // store the multipliers to map the UI window to the touchpad window + float m_fTouchPadMulX; + float m_fTouchPadMulY; + float m_fTouchPadDeadZoneX; // usese the multipliers + float m_fTouchPadDeadZoneY; + + +#endif + + // ENum indexes of the first section for this scene, and 1+the last section + ESceneSection m_eFirstSection, m_eMaxSection; + + // 4J - WESTY - Added for pointer prototype. + // Current tooltip settings. + EToolTipItem m_aeToolTipSettings[ eToolTipNumButtons ]; + + // 4J - WESTY - Added for pointer prototype. + // Indicates if pointer is outside UI window (used to drop items). + bool m_bPointerOutsideMenu; + Slot *m_lastPointerLabelSlot; + + bool m_bSplitscreen; + bool m_bNavigateBack; // should we exit the xuiscenes or just navigate back on exit? + + virtual bool IsSectionSlotList( ESceneSection eSection ) { return eSection != eSectionNone; } + virtual bool CanHaveFocus( ESceneSection eSection ) { return true; } + int GetSectionDimensions( ESceneSection eSection, int* piNumColumns, int* piNumRows ); + virtual int getSectionColumns(ESceneSection eSection) = 0; + virtual int getSectionRows(ESceneSection eSection) = 0; + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) = 0; + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) = 0; + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) = 0; + void updateSlotPosition( ESceneSection eSection, ESceneSection newSection, ETapState eTapDirection, int *piTargetX, int *piTargetY, int xOffset ); + + #ifdef TAP_DETECTION + ETapState GetTapInputType( float fInputX, float fInputY ); + #endif + + // Current tooltip settings. + void SetToolTip( EToolTipButton eButton, EToolTipItem eItem ); + void UpdateTooltips(); + + // 4J - WESTY - Added for pointer prototype. + void SetPointerOutsideMenu( bool bOutside ) { m_bPointerOutsideMenu = bOutside; } + + void Initialize(int m_iPad, AbstractContainerMenu* menu, bool autoDeleteMenu, int startIndex,ESceneSection firstSection,ESceneSection maxSection, bool bNavigateBack=FALSE); + virtual void PlatformInitialize(int iPad, int startIndex) = 0; + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0) = 0; + + void onMouseTick(); + bool handleKeyDown(int iPad, int iAction, bool bRepeat); + virtual bool handleValidKeyPress(int iUserIndex, int buttonNum, BOOL quickKeyHeld); + virtual void handleOutsideClicked(int iPad, int buttonNum, BOOL quickKeyHeld); + virtual void handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey); + virtual void handleAdditionalKeyPress(int iAction); + virtual void handleSlotListClicked(ESceneSection eSection, int buttonNum, BOOL quickKeyHeld); + virtual void handleSectionClick(ESceneSection eSection) = 0; + void slotClicked(int slotId, int buttonNum, bool quickKey); + int getCurrentIndex(ESceneSection eSection); + virtual int getSectionStartOffset(ESceneSection eSection) = 0; + virtual bool doesSectionTreeHaveFocus(ESceneSection eSection) = 0; + virtual void setSectionFocus(ESceneSection eSection, int iPad) = 0; + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y) = 0; + virtual void setFocusToPointer(int iPad) = 0; + virtual void SetPointerText(const wstring &description, vector &unformattedStrings, bool newSlot) = 0; + virtual shared_ptr getSlotItem(ESceneSection eSection, int iSlot) = 0; + virtual bool isSlotEmpty(ESceneSection eSection, int iSlot) = 0; + virtual void adjustPointerForSafeZone() = 0; + + virtual bool overrideTooltips(ESceneSection sectionUnderPointer, shared_ptr itemUnderPointer, bool bIsItemCarried, bool bSlotHasItem, bool bCarriedIsSameAsSlot, int iSlotStackSizeRemaining, + EToolTipItem &buttonA, EToolTipItem &buttonX, EToolTipItem &buttonY, EToolTipItem &buttonRT) { return false; } + +private: + bool IsSameItemAs(shared_ptr itemA, shared_ptr itemB); + int GetEmptyStackSpace(Slot *slot); + wstring GetItemDescription(Slot *slot, vector &unformattedStrings); + +protected: + + IUIScene_AbstractContainerMenu(); + virtual ~IUIScene_AbstractContainerMenu(); + +public: + virtual int getPad() = 0; +}; diff --git a/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.cpp new file mode 100644 index 0000000..81451c7 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.cpp @@ -0,0 +1,272 @@ +#include "stdafx.h" +#include "IUIScene_AnvilMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" + +IUIScene_AnvilMenu::IUIScene_AnvilMenu() +{ + m_inventory = nullptr; + m_repairMenu = NULL; + m_itemName = L""; +} + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_AnvilMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + int xOffset = 0; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionAnvilItem1: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionAnvilName; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionAnvilInventory; + xOffset = ANVIL_SCENE_ITEM1_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionAnvilResult; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionAnvilItem2; + } + break; + case eSectionAnvilItem2: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionAnvilName; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionAnvilInventory; + xOffset = ANVIL_SCENE_ITEM2_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionAnvilItem1; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionAnvilResult; + } + break; + case eSectionAnvilResult: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionAnvilName; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionAnvilInventory; + xOffset = ANVIL_SCENE_RESULT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionAnvilItem2; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionAnvilItem1; + } + break; + case eSectionAnvilName: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionAnvilUsing; + xOffset = ANVIL_SCENE_ITEM2_SLOT_UP_OFFSET; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionAnvilItem2; + } + break; + case eSectionAnvilInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionAnvilUsing; + } + else if(eTapDirection == eTapStateUp) + { + if( *piTargetX <= ANVIL_SCENE_ITEM1_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilItem1; + } + else if( *piTargetX <= ANVIL_SCENE_ITEM2_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilItem2; + } + else if( *piTargetX >= ANVIL_SCENE_RESULT_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilResult; + } + } + break; + case eSectionAnvilUsing: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionAnvilInventory; + } + else if(eTapDirection == eTapStateDown) + { + if( *piTargetX <= ANVIL_SCENE_ITEM1_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilItem1; + } + else if( *piTargetX <= ANVIL_SCENE_ITEM2_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilName; + } + else if( *piTargetX >= ANVIL_SCENE_RESULT_SLOT_UP_OFFSET) + { + newSection = eSectionAnvilName; + } + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, xOffset); + + return newSection; +} + +int IUIScene_AnvilMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionAnvilItem1: + offset = MerchantMenu::PAYMENT1_SLOT; + break; + case eSectionAnvilItem2: + offset = MerchantMenu::PAYMENT2_SLOT; + break; + case eSectionAnvilResult: + offset = MerchantMenu::RESULT_SLOT; + break; + case eSectionAnvilInventory: + offset = MerchantMenu::INV_SLOT_START; + break; + case eSectionAnvilUsing: + offset = MerchantMenu::USE_ROW_SLOT_START; + break; + default: + assert( false ); + break; + } + return offset; +} + +void IUIScene_AnvilMenu::handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey) +{ + switch(eSection) + { + case eSectionAnvilName: + handleEditNamePressed(); + break; + }; +} + +bool IUIScene_AnvilMenu::IsSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionAnvilUsing: + case eSectionAnvilInventory: + case eSectionAnvilItem1: + case eSectionAnvilItem2: + case eSectionAnvilResult: + return true; + } + return false; +} + +void IUIScene_AnvilMenu::handleTick() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + bool canAfford = true; + wstring m_costString = L""; + + if(m_repairMenu->cost > 0) + { + if(m_repairMenu->cost >= 40 && !pMinecraft->localplayers[getPad()]->abilities.instabuild) + { + m_costString = app.GetString(IDS_REPAIR_EXPENSIVE); + canAfford = false; + } + else if(!m_repairMenu->getSlot(RepairMenu::RESULT_SLOT)->hasItem()) + { + // Do nothing + } + else + { + LPCWSTR costString = app.GetString(IDS_REPAIR_COST); + wchar_t temp[256]; + swprintf(temp, 256, costString, m_repairMenu->cost); + m_costString = temp; + if(!m_repairMenu->getSlot(RepairMenu::RESULT_SLOT)->mayPickup(dynamic_pointer_cast(m_inventory->player->shared_from_this()))) + { + canAfford = false; + } + } + } + setCostLabel(m_costString, canAfford); + + bool crossVisible = (m_repairMenu->getSlot(RepairMenu::INPUT_SLOT)->hasItem() || m_repairMenu->getSlot(RepairMenu::ADDITIONAL_SLOT)->hasItem()) && !m_repairMenu->getSlot(RepairMenu::RESULT_SLOT)->hasItem(); + showCross(crossVisible); +} + +void IUIScene_AnvilMenu::updateItemName() +{ + Slot *slot = m_repairMenu->getSlot(RepairMenu::INPUT_SLOT); + if (slot != NULL && slot->hasItem()) + { + if (!slot->getItem()->hasCustomHoverName() && m_itemName.compare(slot->getItem()->getHoverName())==0) + { + m_itemName = L""; + } + } + + m_repairMenu->setItemName(m_itemName); + + // Convert to byteArray + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeUTF(m_itemName); + Minecraft::GetInstance()->localplayers[getPad()]->connection->send(shared_ptr(new CustomPayloadPacket(CustomPayloadPacket::SET_ITEM_NAME_PACKET, baos.toByteArray()))); +} + +void IUIScene_AnvilMenu::refreshContainer(AbstractContainerMenu *container, vector > *items) +{ + slotChanged(container, RepairMenu::INPUT_SLOT, container->getSlot(0)->getItem()); +} + +void IUIScene_AnvilMenu::slotChanged(AbstractContainerMenu *container, int slotIndex, shared_ptr item) +{ + if (slotIndex == RepairMenu::INPUT_SLOT) + { + m_itemName = item == NULL ? L"" : item->getHoverName(); + setEditNameValue(m_itemName); + setEditNameEditable(item != NULL); + if (item != NULL) + { + updateItemName(); + } + } +} + +void IUIScene_AnvilMenu::setContainerData(AbstractContainerMenu *container, int id, int value) +{ +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.h b/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.h new file mode 100644 index 0000000..6c4348f --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_AnvilMenu.h @@ -0,0 +1,45 @@ +#pragma once +#include "IUIScene_AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.ContainerListener.h" + +// The 0-indexed slot in the inventory list that lines up with the result slot +#define ANVIL_SCENE_RESULT_SLOT_UP_OFFSET 5 +#define ANVIL_SCENE_RESULT_SLOT_DOWN_OFFSET 5 +#define ANVIL_SCENE_ITEM1_SLOT_UP_OFFSET 3 +#define ANVIL_SCENE_ITEM1_SLOT_DOWN_OFFSET 3 +#define ANVIL_SCENE_ITEM2_SLOT_UP_OFFSET 4 +#define ANVIL_SCENE_ITEM2_SLOT_DOWN_OFFSET 4 + +class Inventory; +class RepairMenu; + +class IUIScene_AnvilMenu : public virtual IUIScene_AbstractContainerMenu, public net_minecraft_world_inventory::ContainerListener +{ +protected: + shared_ptr m_inventory; + RepairMenu *m_repairMenu; + wstring m_itemName; + +protected: + IUIScene_AnvilMenu(); + + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); + virtual void handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey); + bool IsSectionSlotList( ESceneSection eSection ); + + void handleTick(); + + // Anvil only + virtual void handleEditNamePressed() = 0; + virtual void setEditNameValue(const wstring &name) = 0; + virtual void setEditNameEditable(bool enabled) = 0; + virtual void setCostLabel(const wstring &label, bool canAfford) = 0; + virtual void showCross(bool show) = 0; + void updateItemName(); + + // ContainerListenr + void refreshContainer(AbstractContainerMenu *container, vector > *items); + void slotChanged(AbstractContainerMenu *container, int slotIndex, shared_ptr item); + void setContainerData(AbstractContainerMenu *container, int id, int value); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.cpp new file mode 100644 index 0000000..44bbdc4 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" + +#include "IUIScene_BrewingMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_BrewingMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + int xOffset = 0; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionBrewingBottle1: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionBrewingIngredient; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingInventory; + xOffset = BREWING_SCENE_BOTTLE1_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionBrewingBottle3; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionBrewingBottle2; + } + break; + case eSectionBrewingBottle2: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionBrewingIngredient; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingInventory; + xOffset = BREWING_SCENE_BOTTLE2_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionBrewingBottle1; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionBrewingBottle3; + } + break; + case eSectionBrewingBottle3: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionBrewingIngredient; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingInventory; + xOffset = BREWING_SCENE_BOTTLE3_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionBrewingBottle2; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionBrewingBottle1; + } + break; + case eSectionBrewingIngredient: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionBrewingUsing; + xOffset = BREWING_SCENE_INGREDIENT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingBottle2; + } + break; + case eSectionBrewingInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingUsing; + } + else if(eTapDirection == eTapStateUp) + { + if( *piTargetX <= BREWING_SCENE_BOTTLE1_SLOT_UP_OFFSET) + { + newSection = eSectionBrewingBottle1; + } + else if( *piTargetX <= BREWING_SCENE_BOTTLE2_SLOT_UP_OFFSET) + { + newSection = eSectionBrewingBottle2; + } + else if( *piTargetX >= BREWING_SCENE_BOTTLE3_SLOT_UP_OFFSET) + { + newSection = eSectionBrewingBottle3; + } + } + break; + case eSectionBrewingUsing: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionBrewingInventory; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionBrewingIngredient; + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, xOffset); + + return newSection; +} + +int IUIScene_BrewingMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionBrewingBottle1: + offset = BrewingStandMenu::BOTTLE_SLOT_START; + break; + case eSectionBrewingBottle2: + offset = BrewingStandMenu::BOTTLE_SLOT_START + 1; + break; + case eSectionBrewingBottle3: + offset = BrewingStandMenu::BOTTLE_SLOT_START + 2; + break; + case eSectionBrewingIngredient: + offset = BrewingStandMenu::INGREDIENT_SLOT; + break; + case eSectionBrewingInventory: + offset = BrewingStandMenu::INV_SLOT_START; + break; + case eSectionBrewingUsing: + offset = BrewingStandMenu::INV_SLOT_START + 27; + break; + default: + assert( false ); + break; + } + return offset; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.h b/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.h new file mode 100644 index 0000000..2d5150b --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_BrewingMenu.h @@ -0,0 +1,19 @@ +#pragma once +#include "IUIScene_AbstractContainerMenu.h" + +// The 0-indexed slot in the inventory list that lines up with the result slot +#define BREWING_SCENE_INGREDIENT_SLOT_UP_OFFSET 5 +#define BREWING_SCENE_INGREDIENT_SLOT_DOWN_OFFSET 5 +#define BREWING_SCENE_BOTTLE1_SLOT_UP_OFFSET 3 +#define BREWING_SCENE_BOTTLE1_SLOT_DOWN_OFFSET 3 +#define BREWING_SCENE_BOTTLE2_SLOT_UP_OFFSET 4 +#define BREWING_SCENE_BOTTLE2_SLOT_DOWN_OFFSET 4 +#define BREWING_SCENE_BOTTLE3_SLOT_UP_OFFSET 5 +#define BREWING_SCENE_BOTTLE3_SLOT_DOWN_OFFSET 5 + +class IUIScene_BrewingMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.cpp new file mode 100644 index 0000000..c6a4df0 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "IUIScene_ContainerMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_ContainerMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionContainerChest: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionContainerInventory; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionContainerUsing; + } + break; + case eSectionContainerInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionContainerUsing; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionContainerChest; + } + break; + case eSectionContainerUsing: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionContainerChest; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionContainerInventory; + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, 0); + + return newSection; +} + +int IUIScene_ContainerMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionContainerChest: + offset = 0; + break; + case eSectionContainerInventory: + offset = m_menu->getSize() - (27+9); + break; + case eSectionContainerUsing: + offset = m_menu->getSize() - 9; + break; + default: + assert( false ); + break; + } + return offset; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.h b/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.h new file mode 100644 index 0000000..e0408b3 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_ContainerMenu.h @@ -0,0 +1,10 @@ +#pragma once + +#include "IUIScene_AbstractContainerMenu.h" + +class IUIScene_ContainerMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.cpp new file mode 100644 index 0000000..f158b17 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.cpp @@ -0,0 +1,1400 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.item.crafting.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\LocalPlayer.h" +#include "IUIScene_CraftingMenu.h" + +Recipy::_eGroupType IUIScene_CraftingMenu::m_GroupTypeMapping4GridA[IUIScene_CraftingMenu::m_iMaxGroup2x2]= +{ + Recipy::eGroupType_Structure, + Recipy::eGroupType_Tool, + Recipy::eGroupType_Food, + Recipy::eGroupType_Mechanism, + Recipy::eGroupType_Transport, + Recipy::eGroupType_Decoration, +}; + +Recipy::_eGroupType IUIScene_CraftingMenu::m_GroupTypeMapping9GridA[IUIScene_CraftingMenu::m_iMaxGroup3x3]= +{ + Recipy::eGroupType_Structure, + Recipy::eGroupType_Tool, + Recipy::eGroupType_Food, + Recipy::eGroupType_Armour, + Recipy::eGroupType_Mechanism, + Recipy::eGroupType_Transport, + Recipy::eGroupType_Decoration, +}; + + +LPCWSTR IUIScene_CraftingMenu::m_GroupIconNameA[m_iMaxGroup3x3]= +{ + L"Structures",//Recipy::eGroupType_Structure, + L"Tools",//Recipy::eGroupType_Tool, + L"Food",//Recipy::eGroupType_Food, + L"Armour",//Recipy::eGroupType_Armour, + L"Mechanisms",//Recipy::eGroupType_Mechanism, + L"Transport",//Recipy::eGroupType_Transport, + L"Decoration",//Recipy::eGroupType_Decoration, +}; + +IUIScene_CraftingMenu::_eGroupTab IUIScene_CraftingMenu::m_GroupTabBkgMapping2x2A[m_iMaxGroup2x2]= +{ + eGroupTab_Left, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Right, +}; + +IUIScene_CraftingMenu::_eGroupTab IUIScene_CraftingMenu::m_GroupTabBkgMapping3x3A[m_iMaxGroup3x3]= +{ + eGroupTab_Left, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Middle, + eGroupTab_Right, +}; + + +// mapping array to map the base objects to their description string +// This should map the enums +// enum +// { +// eBaseItemType_undefined=0, +// eBaseItemType_sword, +// eBaseItemType_shovel, +// eBaseItemType_pickaxe, +// eBaseItemType_hatchet, +// eBaseItemType_hoe, +// eBaseItemType_door, +// eBaseItemType_helmet, +// eBaseItemType_chestplate, +// eBaseItemType_leggings, +// eBaseItemType_boots, +// eBaseItemType_ingot, +// eBaseItemType_rail, +// eBaseItemType_block, +// eBaseItemType_pressureplate, +// eBaseItemType_stairs, +// eBaseItemType_cloth, +// eBaseItemType_dyepowder, +// eBaseItemType_structplanks +// eBaseItemType_structblock, +// eBaseItemType_slab, +// eBaseItemType_halfslab, +// eBaseItemType_torch, +// eBaseItemType_bow, +// eBaseItemType_pockettool, +// eBaseItemType_utensil, +// +// } +// eBaseItemType; + +IUIScene_CraftingMenu::IUIScene_CraftingMenu() +{ + m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + + for(int i=0;ilocalgameModes[getPad()] != NULL ) + { + Tutorial *tutorial = pMinecraft->localgameModes[getPad()]->getTutorial(); + if(tutorial != NULL) + { + tutorial->handleUIInput(iAction); + if(ui.IsTutorialVisible(getPad()) && !tutorial->isInputAllowed(iAction)) + { + return S_OK; + } + } + } + + + switch(iAction) + { + case ACTION_MENU_X: + + // change the display + m_iDisplayDescription++; + if(m_iDisplayDescription==DISPLAY_MAX) m_iDisplayDescription=DISPLAY_INVENTORY; + ui.PlayUISFX(eSFX_Focus); + UpdateMultiPanel(); + UpdateTooltips(); + break; + case ACTION_MENU_PAUSEMENU: + case ACTION_MENU_B: + ui.ShowTooltip( iPad, eToolTipButtonX, false ); + ui.ShowTooltip( iPad, eToolTipButtonB, false ); + ui.ShowTooltip( iPad, eToolTipButtonA, false ); + ui.ShowTooltip( iPad, eToolTipButtonRB, false ); + // kill the crafting xui + //ui.PlayUISFX(eSFX_Back); + ui.CloseUIScenes(iPad); + + bHandled = true; + break; + case ACTION_MENU_A: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + // Do some crafting! + if(m_pPlayer && m_pPlayer->inventory) + { + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + // Force a make if the debug is on + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L< pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); + //int iIcon=pTempItemInst->getItem()->getIcon(pTempItemInst->getAuxValue()); + + if( pMinecraft->localgameModes[iPad] != NULL) + { + Tutorial *tutorial = pMinecraft->localgameModes[iPad]->getTutorial(); + if(tutorial != NULL) + { + tutorial->onCrafted(pTempItemInst); + } + } + + pMinecraft->localgameModes[iPad]->handleCraftItem(iRecipe,m_pPlayer); + + if(m_pPlayer->inventory->add(pTempItemInst)==false) + { + // no room in inventory, so throw it down + m_pPlayer->drop(pTempItemInst); + } + // play a sound + //pMinecraft->soundEngine->playUI( L"random.pop", 1.0f, 1.0f); + ui.PlayUISFX(eSFX_Craft); + } + } + else if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + int iSlot; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + } + else + { + iSlot=0; + } + int iRecipe= CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]; + shared_ptr pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); + //int iIcon=pTempItemInst->getItem()->getIcon(pTempItemInst->getAuxValue()); + + if( pMinecraft->localgameModes[iPad] != NULL ) + { + Tutorial *tutorial = pMinecraft->localgameModes[iPad]->getTutorial(); + if(tutorial != NULL) + { + tutorial->createItemSelected(pTempItemInst, pRecipeIngredientsRequired[iRecipe].bCanMake[iPad]); + } + } + + if(pRecipeIngredientsRequired[iRecipe].bCanMake[iPad]) + { + pTempItemInst->onCraftedBy(m_pPlayer->level, dynamic_pointer_cast( m_pPlayer->shared_from_this() ), pTempItemInst->count ); + // TODO 4J Stu - handleCraftItem should do a lot more than what it does, loads of the "can we craft" code should also probably be + // shifted to the GameMode + pMinecraft->localgameModes[iPad]->handleCraftItem(iRecipe,m_pPlayer); + + // play a sound + //pMinecraft->soundEngine->playUI( L"random.pop", 1.0f, 1.0f); + ui.PlayUISFX(eSFX_Craft); + + // and remove those resources from your inventory + for(int i=0;i ingItemInst = nullptr; + // do we need to remove a specific aux value? + if(pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]!=Recipes::ANY_AUX_VALUE) + { + ingItemInst = m_pPlayer->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i] ); + m_pPlayer->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i],pRecipeIngredientsRequired[iRecipe].iIngAuxValA[i]); + } + else + { + ingItemInst = m_pPlayer->inventory->getResourceItem( pRecipeIngredientsRequired[iRecipe].iIngIDA[i] ); + m_pPlayer->inventory->removeResource(pRecipeIngredientsRequired[iRecipe].iIngIDA[i]); + } + + // 4J Stu - Fix for #13097 - Bug: Milk Buckets are removed when crafting Cake + if (ingItemInst != NULL) + { + if (ingItemInst->getItem()->hasCraftingRemainingItem()) + { + // replace item with remaining result + m_pPlayer->inventory->add( shared_ptr( new ItemInstance(ingItemInst->getItem()->getCraftingRemainingItem()) ) ); + } + + } + } + } + + // 4J Stu - Fix for #13119 - We should add the item after we remove the ingredients + if(m_pPlayer->inventory->add(pTempItemInst)==false ) + { + // no room in inventory, so throw it down + m_pPlayer->drop(pTempItemInst); + } + + //4J Gordon: Achievements + switch(pTempItemInst->id ) + { + case Tile::workBench_Id: m_pPlayer->awardStat(GenericStats::buildWorkbench(), GenericStats::param_buildWorkbench()); break; + case Item::pickAxe_wood_Id: m_pPlayer->awardStat(GenericStats::buildPickaxe(), GenericStats::param_buildPickaxe()); break; + case Tile::furnace_Id: m_pPlayer->awardStat(GenericStats::buildFurnace(), GenericStats::param_buildFurnace()); break; + case Item::hoe_wood_Id: m_pPlayer->awardStat(GenericStats::buildHoe(), GenericStats::param_buildHoe()); break; + case Item::bread_Id: m_pPlayer->awardStat(GenericStats::makeBread(), GenericStats::param_makeBread()); break; + case Item::cake_Id: m_pPlayer->awardStat(GenericStats::bakeCake(), GenericStats::param_bakeCake()); break; + case Item::pickAxe_stone_Id: m_pPlayer->awardStat(GenericStats::buildBetterPickaxe(), GenericStats::param_buildBetterPickaxe()); break; + case Item::sword_wood_Id: m_pPlayer->awardStat(GenericStats::buildSword(), GenericStats::param_buildSword()); break; + case Tile::dispenser_Id: m_pPlayer->awardStat(GenericStats::dispenseWithThis(), GenericStats::param_dispenseWithThis()); break; + case Tile::enchantTable_Id: m_pPlayer->awardStat(GenericStats::enchantments(), GenericStats::param_enchantments()); break; + case Tile::bookshelf_Id: m_pPlayer->awardStat(GenericStats::bookcase(), GenericStats::param_bookcase()); break; + } + + // We've used some ingredients from our inventory, so update the recipes we can make + CheckRecipesAvailable(); + // don't reset the vertical slots - we want to stay where we are + UpdateVerticalSlots(); + UpdateHighlight(); + } + else + { + //pMinecraft->soundEngine->playUI( L"btn.back", 1.0f, 1.0f); + ui.PlayUISFX(eSFX_CraftFail); + } + } + } + break; + + case ACTION_MENU_LEFT_SCROLL: + // turn off the old group tab + showTabHighlight(m_iGroupIndex,false); + + if(m_iGroupIndex==0) + { + if(m_iContainerType==RECIPE_TYPE_3x3) + { + m_iGroupIndex=m_iMaxGroup3x3-1; + } + else + { + m_iGroupIndex=m_iMaxGroup2x2-1; + } + } + else + { + m_iGroupIndex--; + } + // turn on the new group + showTabHighlight(m_iGroupIndex,true); + + m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + + CheckRecipesAvailable(); + // reset the vertical slots + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + ui.PlayUISFX(eSFX_Focus); + UpdateVerticalSlots(); + UpdateHighlight(); + setGroupText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + + break; + case ACTION_MENU_RIGHT_SCROLL: + // turn off the old group tab + showTabHighlight(m_iGroupIndex,false); + + m_iGroupIndex++; + if(m_iContainerType==RECIPE_TYPE_3x3) + { + if(m_iGroupIndex==m_iMaxGroup3x3) m_iGroupIndex=0; + } + else + { + if(m_iGroupIndex==m_iMaxGroup2x2) m_iGroupIndex=0; + } + // turn on the new group + showTabHighlight(m_iGroupIndex,true); + + m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + CheckRecipesAvailable(); + // reset the vertical slots + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + ui.PlayUISFX(eSFX_Focus); + UpdateVerticalSlots(); + UpdateHighlight(); + setGroupText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + break; + } + + // 4J-Tomk - check if we've only got one vertical scroll slot (480, splits & Vita) + bool bNoScrollSlots = false; + if(m_bSplitscreen ||(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen())) + { + bNoScrollSlots = true; + } +#ifdef __PSVITA__ + bNoScrollSlots = true; +#endif + + // 4J Stu - We did used to swap the thumsticks based on Southpaw in this scene, but ONLY in this scene + switch(iAction) + { + case ACTION_MENU_OTHER_STICK_UP: + scrollDescriptionUp(); + break; + case ACTION_MENU_OTHER_STICK_DOWN: + scrollDescriptionDown(); + break; + case ACTION_MENU_RIGHT: + { + int iOldHSlot=m_iCurrentSlotHIndex; + + m_iCurrentSlotHIndex++; + if(m_iCurrentSlotHIndex>=m_iCraftablesMaxHSlotC) m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + // clear the indices + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + + UpdateVerticalSlots(); + UpdateHighlight(); + // re-enable the old hslot + if(CanBeMadeA[iOldHSlot].iCount>0) + { + setShowCraftHSlot(iOldHSlot,true); + } + ui.PlayUISFX(eSFX_Focus); + bHandled = true; + } + break; + case ACTION_MENU_LEFT: + { + if(m_iCraftablesMaxHSlotC!=0) + { + int iOldHSlot=m_iCurrentSlotHIndex; + if(m_iCurrentSlotHIndex==0) m_iCurrentSlotHIndex=m_iCraftablesMaxHSlotC-1; + else m_iCurrentSlotHIndex--; + m_iCurrentSlotVIndex=1; + // clear the indices + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + + UpdateVerticalSlots(); + UpdateHighlight(); + // re-enable the old hslot + if(CanBeMadeA[iOldHSlot].iCount>0) + { + setShowCraftHSlot(iOldHSlot,true); + } + ui.PlayUISFX(eSFX_Focus); + } + bHandled = true; + } + break; + case ACTION_MENU_UP: + { + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + if(bNoScrollSlots) + { + if(iVSlotIndexA[1]==0) + { + iVSlotIndexA[1]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + } + else + { + iVSlotIndexA[1]--; + } + ui.PlayUISFX(eSFX_Focus); + } + else + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>2) + { + { + if(m_iCurrentSlotVIndex!=0) + { + // just move the highlight + m_iCurrentSlotVIndex--; + ui.PlayUISFX(eSFX_Focus); + } + else + { + //move the slots + iVSlotIndexA[2]=iVSlotIndexA[1]; + iVSlotIndexA[1]=iVSlotIndexA[0]; + // on 0 and went up, so cycle the values + if(iVSlotIndexA[0]==0) + { + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + } + else + { + iVSlotIndexA[0]--; + } + ui.PlayUISFX(eSFX_Focus); + } + } + } + else + { + if(m_iCurrentSlotVIndex!=1) + { + // just move the highlight + m_iCurrentSlotVIndex--; + ui.PlayUISFX(eSFX_Focus); + } + } + UpdateVerticalSlots(); + UpdateHighlight(); + } + + } + break; + case ACTION_MENU_DOWN: + { + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + if(bNoScrollSlots) + { + if(iVSlotIndexA[1]==(CanBeMadeA[m_iCurrentSlotHIndex].iCount-1)) + { + iVSlotIndexA[1]=0; + } + else + { + iVSlotIndexA[1]++; + } + ui.PlayUISFX(eSFX_Focus); + + } + else + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>2) + { + if(m_iCurrentSlotVIndex!=2) + { + m_iCurrentSlotVIndex++; + ui.PlayUISFX(eSFX_Focus); + } + else + { + iVSlotIndexA[0]=iVSlotIndexA[1]; + iVSlotIndexA[1]=iVSlotIndexA[2]; + if(iVSlotIndexA[m_iCurrentSlotVIndex]==(CanBeMadeA[m_iCurrentSlotHIndex].iCount-1)) + { + iVSlotIndexA[2]=0; + } + else + { + iVSlotIndexA[2]++; + } + ui.PlayUISFX(eSFX_Focus); + } + } + else + { + if(m_iCurrentSlotVIndex!=(CanBeMadeA[m_iCurrentSlotHIndex].iCount)) + { + m_iCurrentSlotVIndex++; + ui.PlayUISFX(eSFX_Focus); + } + } + UpdateVerticalSlots(); + UpdateHighlight(); + } + } + break; + } + + return bHandled; +} + +////////////////////////////////////////////////////////////////////////// +// +// CheckRecipesAvailable +// +////////////////////////////////////////////////////////////////////////// +void IUIScene_CraftingMenu::CheckRecipesAvailable() +{ + int iHSlotBrushControl=0; + + // clear the current list + memset(CanBeMadeA,0,sizeof(CANBEMADE)*m_iCraftablesMaxHSlotC); + + hideAllHSlots(); + + if(m_pPlayer && m_pPlayer->inventory) + { + // dump out the inventory + /* for (unsigned int k = 0; k < m_pPlayer->inventory->items.length; k++) + { + if (m_pPlayer->inventory->items[k] != NULL) + { + wstring itemstring=m_pPlayer->inventory->items[k]->toString(); + + //printf("--- Player has "); + OutputDebugStringW(itemstring.c_str()); + //printf(" with Aux val = %d, base type = %d, Material = %d\n",m_pPlayer->inventory->items[k]->getAuxValue(),m_pPlayer->inventory->items[k]->getItem()->getBaseItemType(),m_pPlayer->inventory->items[k]->getItem()->getMaterial()); + } + } + */ + RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + int iRecipeC=(int)recipes->size(); + AUTO_VAR(itRecipe, recipes->begin()); + + // dump out the recipe products + + // for (int i = 0; i < iRecipeC; i++) + // { + // shared_ptr pTempItemInst=pRecipeIngredientsRequired[i].pRecipy->assemble(NULL); + // if (pTempItemInst != NULL) + // { + // wstring itemstring=pTempItemInst->toString(); + // + // printf("Recipe [%d] = ",i); + // OutputDebugStringW(itemstring.c_str()); + // if(pTempItemInst->id!=0) + // { + // if(pTempItemInst->id<256) + // { + // Tile *pTile=Tile::tiles[pTempItemInst->id]; + // printf("[TILE] ID\t%d\tAux val\t%d\tBase type\t%d\tMaterial\t%d\t Count=%d\n",pTempItemInst->id, pTempItemInst->getAuxValue(),pTile->getBaseItemType(),pTile->getMaterial(),pTempItemInst->GetCount()); + // } + // else + // { + // printf("ID\t%d\tAux val\t%d\tBase type\t%d\tMaterial\t%d Count=%d\n",pTempItemInst->id, pTempItemInst->getAuxValue(),pTempItemInst->getItem()->getBaseItemType(),pTempItemInst->getItem()->getMaterial(),pTempItemInst->GetCount()); + // } + // + // } + // } + // } + + for(int i=0;igetGroup()!=m_pGroupA[m_iGroupIndex]) + { + itRecipe++; + pRecipeIngredientsRequired[i].bCanMake[getPad()]=false; + continue; + } + // if we are in the inventory menu, then we have 2x2 crafting available only + if((m_iContainerType==RECIPE_TYPE_2x2) && (pRecipeIngredientsRequired[i].iType==RECIPE_TYPE_3x3)) + { + // need a crafting table for this recipe + itRecipe++; + pRecipeIngredientsRequired[i].bCanMake[getPad()]=false; + continue; + } + // clear the mask showing which ingredients are missing + pRecipeIngredientsRequired[i].usBitmaskMissingGridIngredients[getPad()]=0; + + //bool bCanMakeRecipe=true; + bool *bFoundA= new bool [pRecipeIngredientsRequired[i].iIngC]; + for(int j=0;jinventory->items.length; k++) + { + if (m_pPlayer->inventory->items[k] != NULL) + { + // do they have the ingredient, and the aux value matches, and enough off it? + if((m_pPlayer->inventory->items[k]->id == pRecipeIngredientsRequired[i].iIngIDA[j]) && + // check if the ingredient required doesn't care about the aux value, or if it does, does the inventory item aux match it + ((pRecipeIngredientsRequired[i].iIngAuxValA[j]==Recipes::ANY_AUX_VALUE) || (pRecipeIngredientsRequired[i].iIngAuxValA[j]==m_pPlayer->inventory->items[k]->getAuxValue())) + ) + { + // do they have enough? We need to check the whole inventory, since they may have enough in different slots (milk isn't milkx3, but milk,milk,milk) + if(m_pPlayer->inventory->items[k]->GetCount()>=pRecipeIngredientsRequired[i].iIngValA[j]) + { + // they have enough with one slot + bFoundA[j]=true; + } + else + { + // look at the combined value from the whole inventory + + for(unsigned int l=0;linventory->items.length;l++) + { + if (m_pPlayer->inventory->items[l] != NULL) + { + if( + (m_pPlayer->inventory->items[l]->id == pRecipeIngredientsRequired[i].iIngIDA[j]) && + ( (pRecipeIngredientsRequired[i].iIngAuxValA[j]==Recipes::ANY_AUX_VALUE) || (pRecipeIngredientsRequired[i].iIngAuxValA[j]==m_pPlayer->inventory->items[l]->getAuxValue() )) + ) + { + iTotalCount+=m_pPlayer->inventory->items[l]->GetCount(); + } + } + } + + if(iTotalCount>=pRecipeIngredientsRequired[i].iIngValA[j]) + { + bFoundA[j]=true; + } + } + + // 4J Stu - TU-1 hotfix + // Fix for #13143 - Players are able to craft items they do not have enough ingredients for if they store the ingredients in multiple, smaller stacks + break; + } + } + } + // if bFoundA[j] is false, then we didn't have enough of the ingredient required by the recipe, so mark the grid items we're short of + if(bFoundA[j]==false) + { + int iMissing = pRecipeIngredientsRequired[i].iIngValA[j]-iTotalCount; + int iGridIndex=0; + while(iMissing!=0) + { + // need to check if there is an aux val and match that + if(((pRecipeIngredientsRequired[i].uiGridA[iGridIndex]&0x00FFFFFF)==pRecipeIngredientsRequired[i].iIngIDA[j]) && + ((pRecipeIngredientsRequired[i].iIngAuxValA[j]==Recipes::ANY_AUX_VALUE) ||(pRecipeIngredientsRequired[i].iIngAuxValA[j]== ((pRecipeIngredientsRequired[i].uiGridA[iGridIndex]&0xFF000000)>>24))) ) + { + // this grid entry is the ingredient we don't have enough of + pRecipeIngredientsRequired[i].usBitmaskMissingGridIngredients[getPad()]|=1< pTempItemInst=pRecipeIngredientsRequired[i].pRecipy->assemble(nullptr); + //int iIcon=pTempItemInst->getItem()->getIcon(pTempItemInst->getAuxValue()); + int iID=pTempItemInst->getItem()->id; + int iBaseType; + + if(iID<256) // is it a tile? + { + iBaseType=Tile::tiles[iID]->getBaseItemType(); + } + else + { + iBaseType=pTempItemInst->getItem()->getBaseItemType(); + } + + // ignore for the misc base type - these have not been placed in a base type group + if(iBaseType!=Item::eBaseItemType_undefined) + { + for(int k=0;kgetDescriptionId())); +#endif + app.DebugPrintf("\n"); + + } + } + } + else + { + app.DebugPrintf("Need more HSlots\n"); + } + + delete [] bFoundA; + itRecipe++; + } + } + + // run through the canbemade list and update the icons displayed + int iIndex=0; + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + + while((iIndex pTempItemInst=pRecipeIngredientsRequired[CanBeMadeA[iIndex].iRecipeA[0]].pRecipy->assemble(nullptr); + assert(pTempItemInst->id!=0); + unsigned int uiAlpha; + + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<id == Item::clock_Id || pTempItemInst->id == Item::compass_Id ) + { + pTempItemInst->setAuxValue( 255 ); + } + setCraftHSlotItem(getPad(),iIndex,pTempItemInst,uiAlpha); + + iIndex++; + } + + // 4J-PB - Removed - UpdateTooltips will do this + // Update tooltips + /*if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + ui.ShowTooltip( getPad(), eToolTipButtonA, true ); + // 4J-PB - not implemented ! + //ui.EnableTooltip( getPad(), eToolTipButtonA, true ); + } + else + { + ui.ShowTooltip( getPad(), eToolTipButtonA, false ); + }*/ +} + +////////////////////////////////////////////////////////////////////////// +// +// UpdateHighlight +// +////////////////////////////////////////////////////////////////////////// +void IUIScene_CraftingMenu::UpdateHighlight() +{ + updateHighlightAndScrollPositions(); + + bool bCanBeMade=CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0; + if(bCanBeMade) + { + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + int iSlot; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + } + else + { + iSlot=0; + } + shared_ptr pTempItemInstAdditional=pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].pRecipy->assemble(nullptr); + + // special case for the torch coal/charcoal + int id=pTempItemInstAdditional->getDescriptionId(); + LPCWSTR itemstring; + + switch(id) + { + case IDS_TILE_TORCH: + { + if(pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].iIngAuxValA[0]==1) + { + itemstring=app.GetString( IDS_TILE_TORCHCHARCOAL ); + } + else + { + itemstring=app.GetString( IDS_TILE_TORCHCOAL ); + } + } + break; + case IDS_ITEM_FIREBALL: + { + if(pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].iIngAuxValA[2]==1) + { + itemstring=app.GetString( IDS_ITEM_FIREBALLCHARCOAL ); + } + else + { + itemstring=app.GetString( IDS_ITEM_FIREBALLCOAL ); + } + } + break; + default: + itemstring=app.GetString(id ); + break; + } + + setItemText(itemstring); + } + else + { + setItemText(L""); + } + UpdateDescriptionText(bCanBeMade); + DisplayIngredients(); + + UpdateMultiPanel(); + + UpdateTooltips(); +} + +////////////////////////////////////////////////////////////////////////// +// +// UpdateVerticalSlots +// +////////////////////////////////////////////////////////////////////////// +void IUIScene_CraftingMenu::UpdateVerticalSlots() +{ + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + + // update the vertical items for the current horizontal slot + hideAllVSlots(); + + // could have either 1 or 2 vertical slots, above and below the horizontal slot + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + // turn off the horizontal one since we could be cycling through others + setShowCraftHSlot(m_iCurrentSlotHIndex,false); + int iSlots=(CanBeMadeA[m_iCurrentSlotHIndex].iCount>2)?3:2; + + // 4J-Tomk - check if we've only got one vertical scroll slot (480, splits & Vita) + bool bNoScrollSlots = false; + if(m_bSplitscreen ||(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen())) + { + bNoScrollSlots = true; + } +#ifdef __PSVITA__ + bNoScrollSlots = true; +#endif + + for(int i=0;i pTempItemInstAdditional=pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iVSlotIndexA[i]]].pRecipy->assemble(nullptr); + + assert(pTempItemInstAdditional->id!=0); + unsigned int uiAlpha; + + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<id == Item::clock_Id || pTempItemInstAdditional->id == Item::compass_Id ) + { + pTempItemInstAdditional->setAuxValue( 255 ); + } + + setCraftVSlotItem(getPad(),i,pTempItemInstAdditional,uiAlpha); + + updateVSlotPositions(iSlots, i); + } + } +} + +////////////////////////////////////////////////////////////////////////// +// +// DisplayIngredients +// +////////////////////////////////////////////////////////////////////////// +void IUIScene_CraftingMenu::DisplayIngredients() +{ + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + + // hide the previous ingredients + hideAllIngredientsSlots(); + + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + int iSlot,iRecipy; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + iRecipy=CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]; + } + else + { + iSlot=0; + iRecipy=CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[0]; + } + + // show the 2x2 or 3x3 to make the current item + int iBoxWidth=(m_iContainerType==RECIPE_TYPE_2x2)?2:3; + int iRecipe=CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]; + bool bCanMakeRecipe = pRecipeIngredientsRequired[iRecipe].bCanMake[getPad()]; + shared_ptr pTempItemInst=pRecipeIngredientsRequired[iRecipe].pRecipy->assemble(nullptr); + + m_iIngredientsC=pRecipeIngredientsRequired[iRecipe].iIngC; + + // update the ingredients required - these will all be hidden until cycled by the user + for(int i=0;i itemInst= shared_ptr(new ItemInstance(item,pRecipeIngredientsRequired[iRecipe].iIngValA[i],iAuxVal)); + + setIngredientDescriptionItem(getPad(),i,itemInst); + setIngredientDescriptionRedBox(i,false); + + // 4J-PB - a very special case - the bed can use any kind of wool, so we can't use the item description + // and the same goes for the painting + int idescID; + + if( ((pTempItemInst->id==Item::bed_Id) &&(id==Tile::cloth_Id)) || + ((pTempItemInst->id==Item::painting_Id) &&(id==Tile::cloth_Id)) ) + { + idescID=IDS_ANY_WOOL; + } + else + { + idescID=itemInst->getDescriptionId(); + } + setIngredientDescriptionText(i,app.GetString(idescID)); + } + + // 4J Stu - For clocks and compasses we set the aux value to a special one that signals we should use a default texture + // rather than the dynamic one for the player + if( pTempItemInst->id == Item::clock_Id || pTempItemInst->id == Item::compass_Id ) + { + pTempItemInst->setAuxValue( 255 ); + } + + // don't grey out the output icon + setCraftingOutputSlotItem(getPad(), pTempItemInst); + + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<>24; + + // 4J Stu - For clocks and compasses we set the aux value to a special one that signals we should use a default texture + // rather than the dynamic one for the player + if( id == Item::clock_Id || id == Item::compass_Id ) + { + iAuxVal = 0xFF; + } + shared_ptr itemInst= shared_ptr(new ItemInstance(id,1,iAuxVal)); + setIngredientSlotItem(getPad(),index,itemInst); + // show the ingredients we don't have if we can't make the recipe + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + + if(bCanBeMade) + { + int iSlot;//,iRecipy; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + //iRecipy=CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]; + } + else + { + iSlot=0; + //iRecipy=CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[0]; + } + + shared_ptr pTempItemInst=pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].pRecipy->assemble(nullptr); + int iID=pTempItemInst->getItem()->id; + int iAuxVal=pTempItemInst->getAuxValue(); + int iBaseType; + + if(iID<256) // is it a tile? + { + iBaseType=Tile::tiles[iID]->getBaseItemType(); + + iIDSString = Tile::tiles[iID]->getUseDescriptionId(); + } + else + { + iBaseType=pTempItemInst->getItem()->getBaseItemType(); + + iIDSString = pTempItemInst->getUseDescriptionId(); + } + + // A few special cases where the description required is specific to crafting, rather than the normal description + if(iBaseType!=Item::eBaseItemType_undefined) + { + switch(iBaseType) + { + case Item::eBaseItemType_cloth: + switch(iAuxVal) + { + case 0: + iIDSString=IDS_DESC_WOOLSTRING; + break; + } + break; + } + } + + // set the string mapped to by the base object mapping array + + if(iIDSString>=0) + { + // this is an html control now, so set the font size and colour + //wstring wsText=app.GetString(iIDSString); + wstring wsText=app.FormatHTMLString(getPad(),app.GetString(iIDSString)); + + // 12 for splitscreen, 14 for normal + EHTMLFontSize size = eHTMLSize_Normal; + if(m_bSplitscreen ||(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen())) + { + size = eHTMLSize_Splitscreen; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"

",app.GetHTMLColour(eHTMLColor_Black)); + wsText= startTags + wsText + L"

"; + + setDescriptionText(wsText.c_str()); + } + else + { + /// Missing string! +#ifdef _DEBUG + setDescriptionText(L"This is some placeholder description text about the craftable item."); +#else + setDescriptionText(L""); +#endif + } + } + else + { + setDescriptionText(L""); + } +} + +////////////////////////////////////////////////////////////////////////// +// +// UpdateTooltips +// +////////////////////////////////////////////////////////////////////////// +void IUIScene_CraftingMenu::UpdateTooltips() +{ + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + // Update tooltips + + bool bDisplayCreate; + + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + int iSlot; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + } + else + { + iSlot=0; + } + + if(pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].bCanMake[getPad()]) + { + bDisplayCreate=true; + } + else + { + bDisplayCreate=false; + } + } + else + { + bDisplayCreate=false; + } + + + switch(m_iDisplayDescription) + { + case DISPLAY_INVENTORY: + ui.SetTooltips( getPad(), bDisplayCreate?IDS_TOOLTIPS_CREATE:-1,IDS_TOOLTIPS_EXIT, IDS_TOOLTIPS_SHOW_DESCRIPTION,-1,-1,-1,-2, IDS_TOOLTIPS_CHANGE_GROUP); + break; + case DISPLAY_DESCRIPTION: + ui.SetTooltips( getPad(), bDisplayCreate?IDS_TOOLTIPS_CREATE:-1,IDS_TOOLTIPS_EXIT, IDS_TOOLTIPS_SHOW_INGREDIENTS,-1,-1,-1,-2, IDS_TOOLTIPS_CHANGE_GROUP); + break; + case DISPLAY_INGREDIENTS: + ui.SetTooltips( getPad(), bDisplayCreate?IDS_TOOLTIPS_CREATE:-1,IDS_TOOLTIPS_EXIT, IDS_TOOLTIPS_SHOW_INVENTORY,-1,-1,-1,-2, IDS_TOOLTIPS_CHANGE_GROUP); + break; + } + + /*if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + int iSlot; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + } + else + { + iSlot=0; + } + + if(pRecipeIngredientsRequired[CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]].bCanMake[getPad()]) + { + ui.EnableTooltip( getPad(), eToolTipButtonA, true ); + } + else + { + ui.EnableTooltip( getPad(), eToolTipButtonA, false ); + } + } + else + { + ui.ShowTooltip( getPad(), eToolTipButtonA, false ); + }*/ +} + +bool IUIScene_CraftingMenu::isItemSelected(int itemId) +{ + bool isSelected = false; + if(m_pPlayer && m_pPlayer->inventory) + { + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + Recipy::INGREDIENTS_REQUIRED *pRecipeIngredientsRequired=Recipes::getInstance()->getRecipeIngredientsArray(); + + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount!=0) + { + int iSlot; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount>1) + { + iSlot=iVSlotIndexA[m_iCurrentSlotVIndex]; + } + else + { + iSlot=0; + } + int iRecipe= CanBeMadeA[m_iCurrentSlotHIndex].iRecipeA[iSlot]; + ItemInstance *pTempItemInst = (ItemInstance *)pRecipeIngredientsRequired[iRecipe].pRecipy->getResultItem(); + + if(pTempItemInst->id == itemId) + { + isSelected = true; + } + } + } + return isSelected; +} diff --git a/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.h b/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.h new file mode 100644 index 0000000..05227ff --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_CraftingMenu.h @@ -0,0 +1,119 @@ +#pragma once +#include "..\..\..\Minecraft.World\Recipy.h" +#include "..\..\..\Minecraft.World\Item.h" + +class LocalPlayer; + +// 4J Stu - Crafting menu code that's shared across Iggy and XUI +class IUIScene_CraftingMenu +{ +protected: +#define DISPLAY_INVENTORY 0 +#define DISPLAY_DESCRIPTION 1 +#define DISPLAY_INGREDIENTS 2 +#define DISPLAY_MAX 3 + + enum _eGroupTab + { + eGroupTab_Left, + eGroupTab_Middle, + eGroupTab_Right + }; + + static const int m_iMaxHSlotC = 12; + static const int m_iMaxHCraftingSlotC = 10; + static const int m_iMaxVSlotC = 17; + static const int m_iMaxDisplayedVSlotC = 3; + static const int m_iIngredients3x3SlotC = 9; + static const int m_iIngredients2x2SlotC = 4; + + static const int m_iMaxHSlot3x3C = 12; + static const int m_iMaxHSlot2x2C = 10; + + static const int m_iMaxGroup3x3 = 7; + static const int m_iMaxGroup2x2 = 6; + + static int m_iBaseTypeMapA[Item::eBaseItemType_MAXTYPES]; + + typedef struct + { + int iCount; + int iItemBaseType; + int iRecipeA[m_iMaxVSlotC]; // tiers of item that can be made + } + CANBEMADE; + + CANBEMADE CanBeMadeA[m_iMaxHSlotC]; + + int m_iCurrentSlotHIndex; + int m_iCurrentSlotVIndex; + int m_iRecipeC; + int m_iContainerType; // 2x2 or 3x3 + shared_ptr m_pPlayer; + int m_iGroupIndex; + + int iVSlotIndexA[3]; // index of the v slots currently displayed + + static LPCWSTR m_GroupIconNameA[m_iMaxGroup3x3]; + static Recipy::_eGroupType m_GroupTypeMapping4GridA[m_iMaxGroup2x2]; + static Recipy::_eGroupType m_GroupTypeMapping9GridA[m_iMaxGroup3x3]; + Recipy::_eGroupType *m_pGroupA; + + static LPCWSTR m_GroupTabNameA[3]; + static _eGroupTab m_GroupTabBkgMapping2x2A[m_iMaxGroup2x2]; + static _eGroupTab m_GroupTabBkgMapping3x3A[m_iMaxGroup3x3]; + _eGroupTab *m_pGroupTabA; + int m_iCraftablesMaxHSlotC; + int m_iIngredientsMaxSlotC; + int m_iDisplayDescription; + int m_iIngredientsC; + bool m_bIgnoreKeyPresses; + bool m_bSplitscreen; + + eTutorial_State m_previousTutorialState; + + bool handleKeyDown(int iPad, int iAction, bool bRepeat); + +public: + IUIScene_CraftingMenu(); + +protected: + LPCWSTR GetGroupNameText(int iGroupType); + + void CheckRecipesAvailable(); + void UpdateHighlight(); + void UpdateVerticalSlots(); + void DisplayIngredients(); + void UpdateTooltips(); + void UpdateDescriptionText(bool); + +public: + Recipy::_eGroupType getCurrentGroup() { return m_pGroupA[m_iGroupIndex]; } + bool isItemSelected(int itemId); + +protected: + virtual int getPad() = 0; + virtual void hideAllHSlots() = 0; + virtual void hideAllVSlots() = 0; + virtual void hideAllIngredientsSlots() = 0; + virtual void setCraftHSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha) = 0; + virtual void setCraftVSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha) = 0; + virtual void setCraftingOutputSlotItem(int iPad, shared_ptr item) = 0; + virtual void setCraftingOutputSlotRedBox(bool show) = 0; + virtual void setIngredientSlotItem(int iPad, int index, shared_ptr item) = 0; + virtual void setIngredientSlotRedBox(int index, bool show) = 0; + virtual void setIngredientDescriptionItem(int iPad, int index, shared_ptr item) = 0; + virtual void setIngredientDescriptionRedBox(int index, bool show) = 0; + virtual void setIngredientDescriptionText(int index, LPCWSTR text) = 0; + virtual void setShowCraftHSlot(int iIndex, bool show) = 0; + virtual void showTabHighlight(int iIndex, bool show) = 0; + virtual void setGroupText(LPCWSTR text) = 0; + virtual void setDescriptionText(LPCWSTR text) = 0; + virtual void setItemText(LPCWSTR text) = 0; + virtual void scrollDescriptionUp() = 0; + virtual void scrollDescriptionDown() = 0; + virtual void updateHighlightAndScrollPositions() = 0; + virtual void updateVSlotPositions(int iSlots, int i) = 0; + + virtual void UpdateMultiPanel() = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp new file mode 100644 index 0000000..7ce3323 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -0,0 +1,1053 @@ +#include "stdafx.h" +#include "IUIScene_CreativeMenu.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.enchantment.h" + +// 4J JEV - Images for each tab. +IUIScene_CreativeMenu::TabSpec **IUIScene_CreativeMenu::specs = NULL; + +vector< shared_ptr > IUIScene_CreativeMenu::categoryGroups[eCreativeInventoryGroupsCount]; + +#define ITEM(id) list->push_back( shared_ptr(new ItemInstance(id, 1, 0)) ); +#define ITEM_AUX(id, aux) list->push_back( shared_ptr(new ItemInstance(id, 1, aux)) ); +#define DEF(index) list = &categoryGroups[index]; + + +void IUIScene_CreativeMenu::staticCtor() +{ + vector< shared_ptr > *list; + + + // Building Blocks + DEF(eCreativeInventory_BuildingBlocks) + ITEM(Tile::rock_Id) + ITEM(Tile::grass_Id) + ITEM(Tile::dirt_Id) + ITEM(Tile::stoneBrick_Id) + ITEM(Tile::sand_Id) + ITEM(Tile::sandStone_Id) + ITEM_AUX(Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE) + ITEM_AUX(Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS) + ITEM(Tile::goldBlock_Id) + ITEM(Tile::ironBlock_Id) + ITEM(Tile::lapisBlock_Id) + ITEM(Tile::diamondBlock_Id) + ITEM(Tile::emeraldBlock_Id) + ITEM_AUX(Tile::quartzBlock_Id,QuartzBlockTile::TYPE_DEFAULT) + ITEM(Tile::coalOre_Id) + ITEM(Tile::lapisOre_Id) + ITEM(Tile::diamondOre_Id) + ITEM(Tile::redStoneOre_Id) + ITEM(Tile::ironOre_Id) + ITEM(Tile::goldOre_Id) + ITEM(Tile::emeraldOre_Id) + ITEM(Tile::netherQuartz_Id) + ITEM(Tile::unbreakable_Id) + ITEM_AUX(Tile::wood_Id,0) + ITEM_AUX(Tile::wood_Id,TreeTile::DARK_TRUNK) + ITEM_AUX(Tile::wood_Id,TreeTile::BIRCH_TRUNK) + ITEM_AUX(Tile::wood_Id,TreeTile::JUNGLE_TRUNK) + ITEM_AUX(Tile::treeTrunk_Id, 0) + ITEM_AUX(Tile::treeTrunk_Id, TreeTile::DARK_TRUNK) + ITEM_AUX(Tile::treeTrunk_Id, TreeTile::BIRCH_TRUNK) + ITEM_AUX(Tile::treeTrunk_Id, TreeTile::JUNGLE_TRUNK) + ITEM(Tile::gravel_Id) + ITEM(Tile::redBrick_Id) + ITEM(Tile::mossStone_Id) + ITEM(Tile::obsidian_Id) + ITEM(Tile::clay) + ITEM(Tile::ice_Id) + ITEM(Tile::snow_Id) + ITEM(Tile::hellRock_Id) + ITEM(Tile::hellSand_Id) + ITEM(Tile::lightGem_Id) + ITEM_AUX(Tile::stoneBrickSmooth_Id,SmoothStoneBrickTile::TYPE_DEFAULT) + ITEM_AUX(Tile::stoneBrickSmooth_Id,SmoothStoneBrickTile::TYPE_MOSSY) + ITEM_AUX(Tile::stoneBrickSmooth_Id,SmoothStoneBrickTile::TYPE_CRACKED) + ITEM_AUX(Tile::stoneBrickSmooth_Id,SmoothStoneBrickTile::TYPE_DETAIL) + ITEM_AUX(Tile::monsterStoneEgg_Id,StoneMonsterTile::HOST_ROCK) + ITEM_AUX(Tile::monsterStoneEgg_Id,StoneMonsterTile::HOST_COBBLE) + ITEM_AUX(Tile::monsterStoneEgg_Id,StoneMonsterTile::HOST_STONEBRICK) + ITEM(Tile::mycel_Id) + ITEM(Tile::netherBrick_Id) + ITEM(Tile::whiteStone_Id) + ITEM_AUX(Tile::quartzBlock_Id,QuartzBlockTile::TYPE_CHISELED) + ITEM_AUX(Tile::quartzBlock_Id,QuartzBlockTile::TYPE_LINES_Y) + ITEM(Tile::trapdoor_Id) + ITEM(Tile::fenceGate_Id) + ITEM(Item::door_wood_Id) + ITEM(Item::door_iron_Id) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::STONE_SLAB) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::SAND_SLAB) + // AP - changed oak slab to be wood because it wouldn't burn +// ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::WOOD_SLAB) + ITEM_AUX(Tile::woodSlabHalf_Id,0) + ITEM_AUX(Tile::woodSlabHalf_Id,TreeTile::DARK_TRUNK) + ITEM_AUX(Tile::woodSlabHalf_Id,TreeTile::BIRCH_TRUNK) + ITEM_AUX(Tile::woodSlabHalf_Id,TreeTile::JUNGLE_TRUNK) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::COBBLESTONE_SLAB) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::BRICK_SLAB) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::SMOOTHBRICK_SLAB) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::NETHERBRICK_SLAB) + ITEM_AUX(Tile::stoneSlabHalf_Id,StoneSlabTile::QUARTZ_SLAB) + ITEM(Tile::stairs_wood_Id) + ITEM(Tile::stairs_birchwood_Id) + ITEM(Tile::stairs_sprucewood_Id) + ITEM(Tile::stairs_junglewood_Id) + ITEM(Tile::stairs_stone_Id) + ITEM(Tile::stairs_bricks_Id) + ITEM(Tile::stairs_stoneBrickSmooth_Id) + ITEM(Tile::stairs_netherBricks_Id) + ITEM(Tile::stairs_sandstone_Id) + ITEM(Tile::stairs_quartz_Id) + + + // Decoration + DEF(eCreativeInventory_Decoration) + ITEM_AUX(Item::skull_Id,SkullTileEntity::TYPE_SKELETON) + ITEM_AUX(Item::skull_Id,SkullTileEntity::TYPE_WITHER) + ITEM_AUX(Item::skull_Id,SkullTileEntity::TYPE_ZOMBIE) + ITEM_AUX(Item::skull_Id,SkullTileEntity::TYPE_CHAR) + ITEM_AUX(Item::skull_Id,SkullTileEntity::TYPE_CREEPER) + ITEM(Tile::sponge_Id) + ITEM(Tile::melon_Id) + ITEM(Tile::pumpkin_Id) + ITEM(Tile::litPumpkin_Id) + ITEM_AUX(Tile::sapling_Id, Sapling::TYPE_DEFAULT) + ITEM_AUX(Tile::sapling_Id, Sapling::TYPE_EVERGREEN) + ITEM_AUX(Tile::sapling_Id, Sapling::TYPE_BIRCH) + ITEM_AUX(Tile::sapling_Id, Sapling::TYPE_JUNGLE) + ITEM_AUX(Tile::leaves_Id, LeafTile::NORMAL_LEAF) + ITEM_AUX(Tile::leaves_Id, LeafTile::EVERGREEN_LEAF) + ITEM_AUX(Tile::leaves_Id, LeafTile::BIRCH_LEAF) + ITEM_AUX(Tile::leaves_Id, LeafTile::JUNGLE_LEAF) + ITEM(Tile::vine) + ITEM(Tile::waterLily_Id) + ITEM(Tile::torch_Id) + ITEM_AUX(Tile::tallgrass_Id, TallGrass::DEAD_SHRUB) + ITEM_AUX(Tile::tallgrass_Id, TallGrass::TALL_GRASS) + ITEM_AUX(Tile::tallgrass_Id, TallGrass::FERN) + ITEM(Tile::deadBush_Id) + ITEM(Tile::flower_Id) + ITEM(Tile::rose_Id) + ITEM(Tile::mushroom1_Id) + ITEM(Tile::mushroom2_Id) + ITEM(Tile::cactus_Id) + ITEM(Tile::topSnow_Id) + // 4J-PB - Already got sugar cane in Materials ITEM_11(Tile::reeds_Id) + ITEM(Tile::web_Id) + ITEM(Tile::thinGlass_Id) + ITEM(Tile::glass_Id) + ITEM(Item::painting_Id) + ITEM(Item::itemFrame_Id) + ITEM(Item::sign_Id) + ITEM(Tile::bookshelf_Id) + ITEM(Item::flowerPot_Id) + ITEM_AUX(Tile::cloth_Id,14) // Red + ITEM_AUX(Tile::cloth_Id,1) // Orange + ITEM_AUX(Tile::cloth_Id,4) // Yellow + ITEM_AUX(Tile::cloth_Id,5) // Lime + ITEM_AUX(Tile::cloth_Id,3) // Light Blue + ITEM_AUX(Tile::cloth_Id,9) // Cyan + ITEM_AUX(Tile::cloth_Id,11) // Blue + ITEM_AUX(Tile::cloth_Id,10) // Purple + ITEM_AUX(Tile::cloth_Id,2) // Magenta + ITEM_AUX(Tile::cloth_Id,6) // Pink + ITEM_AUX(Tile::cloth_Id,0) // White + ITEM_AUX(Tile::cloth_Id,8) // Light Gray + ITEM_AUX(Tile::cloth_Id,7) // Gray + ITEM_AUX(Tile::cloth_Id,15) // Black + ITEM_AUX(Tile::cloth_Id,13) // Green + ITEM_AUX(Tile::cloth_Id,12) // Brown + + ITEM_AUX(Tile::woolCarpet_Id,14) // Red + ITEM_AUX(Tile::woolCarpet_Id,1) // Orange + ITEM_AUX(Tile::woolCarpet_Id,4) // Yellow + ITEM_AUX(Tile::woolCarpet_Id,5) // Lime + ITEM_AUX(Tile::woolCarpet_Id,3) // Light Blue + ITEM_AUX(Tile::woolCarpet_Id,9) // Cyan + ITEM_AUX(Tile::woolCarpet_Id,11) // Blue + ITEM_AUX(Tile::woolCarpet_Id,10) // Purple + ITEM_AUX(Tile::woolCarpet_Id,2) // Magenta + ITEM_AUX(Tile::woolCarpet_Id,6) // Pink + ITEM_AUX(Tile::woolCarpet_Id,0) // White + ITEM_AUX(Tile::woolCarpet_Id,8) // Light Gray + ITEM_AUX(Tile::woolCarpet_Id,7) // Gray + ITEM_AUX(Tile::woolCarpet_Id,15) // Black + ITEM_AUX(Tile::woolCarpet_Id,13) // Green + ITEM_AUX(Tile::woolCarpet_Id,12) // Brown + + + // Redstone + DEF(eCreativeInventory_Redstone) + ITEM(Tile::dispenser_Id) + ITEM(Tile::musicBlock_Id) + ITEM(Tile::pistonBase_Id) + ITEM(Tile::pistonStickyBase_Id) + ITEM(Tile::tnt_Id) + ITEM(Tile::lever_Id) + ITEM(Tile::button_stone_Id) + ITEM(Tile::button_wood_Id) + ITEM(Tile::pressurePlate_stone_Id) + ITEM(Tile::pressurePlate_wood_Id) + ITEM(Item::redStone_Id) + ITEM(Tile::notGate_on_Id) + ITEM(Item::diode_Id) + ITEM(Tile::redstoneLight_Id) + ITEM(Tile::tripWireSource_Id) + + // Transport + DEF(eCreativeInventory_Transport) + ITEM(Tile::rail_Id) + ITEM(Tile::goldenRail_Id) + ITEM(Tile::detectorRail_Id) + ITEM(Tile::ladder_Id) + ITEM(Item::minecart_Id) + ITEM(Item::minecart_chest_Id) + ITEM(Item::minecart_furnace_Id) + ITEM(Item::saddle_Id) + ITEM(Item::boat_Id) + + // Miscellaneous + DEF(eCreativeInventory_Misc) + ITEM(Tile::chest_Id) + ITEM(Tile::enderChest_Id) + ITEM(Tile::workBench_Id) + ITEM(Tile::furnace_Id) + ITEM(Item::brewingStand_Id) + ITEM(Tile::enchantTable_Id) + ITEM(Tile::endPortalFrameTile_Id) + ITEM(Tile::recordPlayer_Id) + ITEM(Tile::anvil_Id); + ITEM(Tile::fence_Id) + ITEM(Tile::netherFence_Id) + ITEM(Tile::ironFence_Id) + ITEM_AUX(Tile::cobbleWall_Id, WallTile::TYPE_NORMAL) + ITEM_AUX(Tile::cobbleWall_Id, WallTile::TYPE_MOSSY) + ITEM(Item::bed_Id) + ITEM(Item::bucket_empty_Id) + ITEM(Item::bucket_lava_Id) + ITEM(Item::bucket_water_Id) + ITEM(Item::milk_Id) + ITEM(Item::cauldron_Id) + ITEM(Item::snowBall_Id) + ITEM(Item::paper_Id) + ITEM(Item::book_Id) + ITEM(Item::enderPearl_Id) + ITEM(Item::eyeOfEnder_Id) + ITEM(Item::record_01_Id) + ITEM(Item::record_02_Id) + ITEM(Item::record_03_Id) + ITEM(Item::record_04_Id) + ITEM(Item::record_05_Id) + ITEM(Item::record_06_Id) + ITEM(Item::record_07_Id) + ITEM(Item::record_08_Id) + ITEM(Item::record_09_Id) + ITEM(Item::record_10_Id) + ITEM(Item::record_11_Id) + ITEM(Item::record_12_Id) + ITEM_AUX(Item::monsterPlacer_Id, 50); // Creeper + ITEM_AUX(Item::monsterPlacer_Id, 51); // Skeleton + ITEM_AUX(Item::monsterPlacer_Id, 52); // Spider + ITEM_AUX(Item::monsterPlacer_Id, 54); // Zombie + ITEM_AUX(Item::monsterPlacer_Id, 55); // Slime + ITEM_AUX(Item::monsterPlacer_Id, 56); // Ghast + ITEM_AUX(Item::monsterPlacer_Id, 57); // Zombie Pigman + ITEM_AUX(Item::monsterPlacer_Id, 58); // Enderman + ITEM_AUX(Item::monsterPlacer_Id, 59); // Cave Spider + ITEM_AUX(Item::monsterPlacer_Id, 60); // Silverfish + ITEM_AUX(Item::monsterPlacer_Id, 61); // Blaze + ITEM_AUX(Item::monsterPlacer_Id, 62); // Magma Cube + ITEM_AUX(Item::monsterPlacer_Id, 90); // Pig + ITEM_AUX(Item::monsterPlacer_Id, 91); // Sheep + ITEM_AUX(Item::monsterPlacer_Id, 92); // Cow + ITEM_AUX(Item::monsterPlacer_Id, 93); // Chicken + ITEM_AUX(Item::monsterPlacer_Id, 94); // Squid + ITEM_AUX(Item::monsterPlacer_Id, 95); // Wolf + ITEM_AUX(Item::monsterPlacer_Id, 96); // Mooshroom + ITEM_AUX(Item::monsterPlacer_Id, 98); // Ozelot + ITEM_AUX(Item::monsterPlacer_Id, 120); // Villager + + // Food + DEF(eCreativeInventory_Food) + ITEM(Item::apple_Id) + ITEM(Item::apple_gold_Id) + ITEM_AUX(Item::apple_gold_Id,1) // Enchanted + ITEM(Item::melon_Id) + ITEM(Item::mushroomStew_Id) + ITEM(Item::bread_Id) + ITEM(Item::cake_Id) + ITEM(Item::cookie_Id) + ITEM(Item::fish_cooked_Id) + ITEM(Item::fish_raw_Id) + ITEM(Item::porkChop_cooked_Id) + ITEM(Item::porkChop_raw_Id) + ITEM(Item::beef_cooked_Id) + ITEM(Item::beef_raw_Id) + ITEM(Item::chicken_raw_Id) + ITEM(Item::chicken_cooked_Id) + ITEM(Item::rotten_flesh_Id) + ITEM(Item::spiderEye_Id) + ITEM(Item::potato_Id) + ITEM(Item::potatoBaked_Id) + ITEM(Item::potatoPoisonous_Id) + ITEM(Item::carrots_Id) + ITEM(Item::carrotGolden_Id) + ITEM(Item::pumpkinPie_Id) + + // Tools, Armour and Weapons (Complete) + DEF(eCreativeInventory_ToolsArmourWeapons) + ITEM(Item::compass_Id) + ITEM(Item::helmet_cloth_Id) + ITEM(Item::chestplate_cloth_Id) + ITEM(Item::leggings_cloth_Id) + ITEM(Item::boots_cloth_Id) + ITEM(Item::sword_wood_Id) + ITEM(Item::shovel_wood_Id) + ITEM(Item::pickAxe_wood_Id) + ITEM(Item::hatchet_wood_Id) + ITEM(Item::hoe_wood_Id) + + ITEM(Item::map_Id) + ITEM(Item::helmet_chain_Id) + ITEM(Item::chestplate_chain_Id) + ITEM(Item::leggings_chain_Id) + ITEM(Item::boots_chain_Id) + ITEM(Item::sword_stone_Id) + ITEM(Item::shovel_stone_Id) + ITEM(Item::pickAxe_stone_Id) + ITEM(Item::hatchet_stone_Id) + ITEM(Item::hoe_stone_Id) + + ITEM(Item::bow_Id) + ITEM(Item::helmet_iron_Id) + ITEM(Item::chestplate_iron_Id) + ITEM(Item::leggings_iron_Id) + ITEM(Item::boots_iron_Id) + ITEM(Item::sword_iron_Id) + ITEM(Item::shovel_iron_Id) + ITEM(Item::pickAxe_iron_Id) + ITEM(Item::hatchet_iron_Id) + ITEM(Item::hoe_iron_Id) + + ITEM(Item::arrow_Id) + ITEM(Item::helmet_gold_Id) + ITEM(Item::chestplate_gold_Id) + ITEM(Item::leggings_gold_Id) + ITEM(Item::boots_gold_Id) + ITEM(Item::sword_gold_Id) + ITEM(Item::shovel_gold_Id) + ITEM(Item::pickAxe_gold_Id) + ITEM(Item::hatchet_gold_Id) + ITEM(Item::hoe_gold_Id) + + ITEM(Item::flintAndSteel_Id) + ITEM(Item::helmet_diamond_Id) + ITEM(Item::chestplate_diamond_Id) + ITEM(Item::leggings_diamond_Id) + ITEM(Item::boots_diamond_Id) + ITEM(Item::sword_diamond_Id) + ITEM(Item::shovel_diamond_Id) + ITEM(Item::pickAxe_diamond_Id) + ITEM(Item::hatchet_diamond_Id) + ITEM(Item::hoe_diamond_Id) + + ITEM(Item::fireball_Id) + ITEM(Item::clock_Id) + ITEM(Item::shears_Id) + ITEM(Item::fishingRod_Id) + ITEM(Item::carrotOnAStick_Id) + + for(unsigned int i = 0; i < Enchantment::enchantments.length; ++i) + { + Enchantment *enchantment = Enchantment::enchantments[i]; + if (enchantment == NULL || enchantment->category == NULL) continue; + list->push_back(Item::enchantedBook->createForEnchantment(new EnchantmentInstance(enchantment, enchantment->getMaxLevel()))); + } + + // Materials + DEF(eCreativeInventory_Materials) + ITEM(Item::coal_Id) + ITEM_AUX(Item::coal_Id,1) + ITEM(Item::diamond_Id) + ITEM(Item::emerald_Id) + ITEM(Item::ironIngot_Id) + ITEM(Item::goldIngot_Id) + ITEM(Item::netherQuartz_Id) + ITEM(Item::brick_Id) + ITEM(Item::netherbrick_Id) + ITEM(Item::stick_Id) + ITEM(Item::bowl_Id) + ITEM(Item::bone_Id) + ITEM(Item::string_Id) + ITEM(Item::feather_Id) + ITEM(Item::flint_Id) + ITEM(Item::leather_Id) + ITEM(Item::sulphur_Id) + ITEM(Item::clay_Id) + ITEM(Item::yellowDust_Id) + ITEM(Item::seeds_wheat_Id) + ITEM(Item::seeds_melon_Id) + ITEM(Item::seeds_pumpkin_Id) + ITEM(Item::wheat_Id) + ITEM(Item::reeds_Id) + ITEM(Item::egg_Id) + ITEM(Item::sugar_Id) + ITEM(Item::slimeBall_Id) + ITEM(Item::blazeRod_Id) + ITEM(Item::goldNugget_Id) + ITEM(Item::netherStalkSeeds_Id) + ITEM_AUX(Item::dye_powder_Id,1) // Red + ITEM_AUX(Item::dye_powder_Id,14) // Orange + ITEM_AUX(Item::dye_powder_Id,11) // Yellow + ITEM_AUX(Item::dye_powder_Id,10) // Lime + ITEM_AUX(Item::dye_powder_Id,12) // Light Blue + ITEM_AUX(Item::dye_powder_Id,6) // Cyan + ITEM_AUX(Item::dye_powder_Id,4) // Blue + ITEM_AUX(Item::dye_powder_Id,5) // Purple + ITEM_AUX(Item::dye_powder_Id,13) // Magenta + ITEM_AUX(Item::dye_powder_Id,9) // Pink + ITEM_AUX(Item::dye_powder_Id,15) // Bone Meal + ITEM_AUX(Item::dye_powder_Id,7) // Light gray + ITEM_AUX(Item::dye_powder_Id,8) // Gray + ITEM_AUX(Item::dye_powder_Id,0) // black (ink sac) + ITEM_AUX(Item::dye_powder_Id,2) // Green + ITEM_AUX(Item::dye_powder_Id,3) // Brown + + // Brewing (TODO) + DEF(eCreativeInventory_Brewing) + ITEM(Item::expBottle_Id) + + // 4J Stu - Anything else added here also needs to be added to the key handler below + ITEM(Item::ghastTear_Id) + ITEM(Item::fermentedSpiderEye_Id) + ITEM(Item::blazePowder_Id) + ITEM(Item::magmaCream_Id) + ITEM(Item::speckledMelon_Id) + ITEM(Item::glassBottle_Id) + ITEM_AUX(Item::potion_Id,0) // Water bottle + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_TYPE_AWKWARD)) // Awkward Potion + + + DEF(eCreativeInventory_Potions_Basic) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_SPEED)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_POISON)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_STRENGTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_SLOWNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_INSTANTDAMAGE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_SPEED)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_POISON)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_STRENGTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_SLOWNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_INSTANTDAMAGE)) + + DEF(eCreativeInventory_Potions_Level2) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_SPEED)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_POISON)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_NIGHTVISION)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INVISIBILITY)) + + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_STRENGTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_SLOWNESS)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_SPEED)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_POISON)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_NIGHTVISION)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INVISIBILITY)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_STRENGTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_SLOWNESS)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + + DEF(eCreativeInventory_Potions_Extended) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_SPEED)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_POISON)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTHEALTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_NIGHTVISION)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, 0, MASK_INVISIBILITY)) // 4J- Moved here as there isn't a weak variant of this potion. + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_STRENGTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_SLOWNESS)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_SPEED)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_POISON)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTHEALTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_NIGHTVISION)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, 0, MASK_INVISIBILITY)) // 4J- Moved here as there isn't a weak variant of this potion. + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_STRENGTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_SLOWNESS)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + + DEF(eCreativeInventory_Potions_Level2_Extended) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_SPEED)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_POISON)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_NIGHTVISION)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_INVISIBILITY)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_NIGHTVISION)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_INVISIBILITY)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2EXTENDED, MASK_STRENGTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_EXTENDED, MASK_SLOWNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(0, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_REGENERATION)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_SPEED)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_FIRE_RESISTANCE)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_POISON)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTHEALTH)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_NIGHTVISION)) + //ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_INVISIBILITY)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_NIGHTVISION)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_INVISIBILITY)) // 4J- Moved here as there isn't a weak variant of this potion. + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_WEAKNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2EXTENDED, MASK_STRENGTH)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_EXTENDED, MASK_SLOWNESS)) + ITEM_AUX(Item::potion_Id,MACRO_MAKEPOTION_AUXVAL(MASK_SPLASH, MASK_LEVEL2, MASK_INSTANTDAMAGE)) + + + specs = new TabSpec*[eCreativeInventoryTab_COUNT]; + + // Top Row + ECreative_Inventory_Groups blocksGroup[] = {eCreativeInventory_BuildingBlocks}; + specs[eCreativeInventoryTab_BuildingBlocks] = new TabSpec(L"Structures", IDS_GROUPNAME_BUILDING_BLOCKS, 1, blocksGroup, 0, NULL); + + ECreative_Inventory_Groups decorationsGroup[] = {eCreativeInventory_Decoration}; + specs[eCreativeInventoryTab_Decorations] = new TabSpec(L"Decoration", IDS_GROUPNAME_DECORATIONS, 1, decorationsGroup, 0, NULL); + + ECreative_Inventory_Groups redAndTranGroup[] = {eCreativeInventory_Transport, eCreativeInventory_Redstone}; + specs[eCreativeInventoryTab_RedstoneAndTransport] = new TabSpec(L"RedstoneAndTransport", IDS_GROUPNAME_REDSTONE_AND_TRANSPORT, 2, redAndTranGroup, 0, NULL); + + ECreative_Inventory_Groups materialsGroup[] = {eCreativeInventory_Materials}; + specs[eCreativeInventoryTab_Materials] = new TabSpec(L"Materials", IDS_GROUPNAME_MATERIALS, 1, materialsGroup, 0, NULL); + + ECreative_Inventory_Groups foodGroup[] = {eCreativeInventory_Food}; + specs[eCreativeInventoryTab_Food] = new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup, 0, NULL); + + ECreative_Inventory_Groups toolsGroup[] = {eCreativeInventory_ToolsArmourWeapons}; + specs[eCreativeInventoryTab_ToolsWeaponsArmor] = new TabSpec(L"Tools", IDS_GROUPNAME_TOOLS_WEAPONS_ARMOR, 1, toolsGroup, 0, NULL); + + ECreative_Inventory_Groups brewingGroup[] = {eCreativeInventory_Brewing, eCreativeInventory_Potions_Level2_Extended, eCreativeInventory_Potions_Extended, eCreativeInventory_Potions_Level2, eCreativeInventory_Potions_Basic}; + + // Just use the text LT - the graphic doesn't fit in splitscreen either + // In 480p there's not enough room for the LT button, so use text instead + //if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"Brewing", IDS_GROUPNAME_POTIONS_480, 5, brewingGroup, 0, NULL); + } + // else + // { + // specs[eCreativeInventoryTab_Brewing] = new TabSpec(L"icon_brewing.png", IDS_GROUPNAME_POTIONS, 1, brewingGroup, 4, potionsGroup); + // } + + ECreative_Inventory_Groups miscGroup[] = {eCreativeInventory_Misc}; + specs[eCreativeInventoryTab_Misc] = new TabSpec(L"Misc", IDS_GROUPNAME_MISCELLANEOUS, 1, miscGroup, 0, NULL); + +} + +IUIScene_CreativeMenu::IUIScene_CreativeMenu() +{ + m_bCarryingCreativeItem = false; + m_creativeSlotX = m_creativeSlotY = m_inventorySlotX = m_inventorySlotY = 0; + + // 4J JEV - Settup Tabs + for (int i = 0; i < eCreativeInventoryTab_COUNT; i++) + { + m_tabDynamicPos[i] = 0; + m_tabPage[i] = 0; + } +} + +/* 4J JEV - Switches between tabs. +*/ +void IUIScene_CreativeMenu::switchTab(ECreativeInventoryTabs tab) +{ + // Could just be changing page on the current tab + if(tab != m_curTab) updateTabHighlightAndText(tab); + + m_curTab = tab; + + updateScrollCurrentPage(m_tabPage[m_curTab] + 1, specs[m_curTab]->getPageCount()); + + specs[tab]->populateMenu(itemPickerMenu,m_tabDynamicPos[m_curTab], m_tabPage[m_curTab]); +} + +// 4J JEV - Tab Spec Struct + +IUIScene_CreativeMenu::TabSpec::TabSpec(LPCWSTR icon, int descriptionId, int staticGroupsCount, ECreative_Inventory_Groups *staticGroups, int dynamicGroupsCount, ECreative_Inventory_Groups *dynamicGroups) + : m_icon(icon), m_descriptionId(descriptionId), m_staticGroupsCount(staticGroupsCount), m_dynamicGroupsCount(dynamicGroupsCount) +{ + + m_pages = 0; + m_staticGroupsA = NULL; + + unsigned int dynamicItems = 0; + m_staticItems = 0; + + if(staticGroupsCount > 0) + { + m_staticGroupsA = new ECreative_Inventory_Groups[staticGroupsCount]; + for(int i = 0; i < staticGroupsCount; ++i) + { + m_staticGroupsA[i] = staticGroups[i]; + m_staticItems += categoryGroups[m_staticGroupsA[i]].size(); + } + } + + m_dynamicGroupsA = NULL; + if(dynamicGroupsCount > 0) + { + m_dynamicGroupsA = new ECreative_Inventory_Groups[dynamicGroupsCount]; + for(int i = 0; i < dynamicGroupsCount; ++i) + { + m_dynamicGroupsA[i] = dynamicGroups[i]; + dynamicItems += categoryGroups[m_dynamicGroupsA[i]].size(); + } + } + + m_staticPerPage = MAX_SIZE - dynamicItems; + m_pages = (int)ceil((float)m_staticItems / m_staticPerPage); +} + +IUIScene_CreativeMenu::TabSpec::~TabSpec() +{ + if(m_staticGroupsA != NULL) delete [] m_staticGroupsA; + if(m_dynamicGroupsA != NULL) delete [] m_dynamicGroupsA; +} + +void IUIScene_CreativeMenu::TabSpec::populateMenu(AbstractContainerMenu *menu, int dynamicIndex, unsigned int page) +{ + int lastSlotIndex = 0; + + // Fill the dynamic group + if(m_dynamicGroupsCount > 0 && m_dynamicGroupsA != NULL) + { + for(AUTO_VAR(it, categoryGroups[m_dynamicGroupsA[dynamicIndex]].rbegin()); it != categoryGroups[m_dynamicGroupsA[dynamicIndex]].rend() && lastSlotIndex < MAX_SIZE; ++it) + { + Slot *slot = menu->getSlot(++lastSlotIndex); + slot->set( *it ); + } + } + + // Fill from the static groups + unsigned int startIndex = page * m_staticPerPage; + int remainingItems = m_staticItems - startIndex; + + // Work out the first group with an item the want to display, and which item in that group + unsigned int currentIndex = 0; + unsigned int currentGroup = 0; + unsigned int currentItem = 0; + for(; currentGroup < m_staticGroupsCount; ++currentGroup) + { + int size = categoryGroups[m_staticGroupsA[currentGroup]].size(); + if( currentIndex + size < startIndex) + { + currentIndex += size; + continue; + } + currentItem = size - ((currentIndex + size) - startIndex); + break; + } + + for(; lastSlotIndex < MAX_SIZE;) + { + Slot *slot = menu->getSlot(lastSlotIndex++); + slot->set(categoryGroups[m_staticGroupsA[currentGroup]][currentItem]); + + ++currentItem; + if(currentItem >= categoryGroups[m_staticGroupsA[currentGroup]].size()) + { + currentItem = 0; + ++currentGroup; + if(currentGroup >= m_staticGroupsCount) + { + break; + } + } + } + + for(; lastSlotIndex < MAX_SIZE; ++lastSlotIndex) + { + Slot *slot = menu->getSlot(lastSlotIndex); + slot->remove(1); + } +} + +unsigned int IUIScene_CreativeMenu::TabSpec::getPageCount() +{ + return m_pages; +} + + +// 4J JEV - Item Picker Menu +IUIScene_CreativeMenu::ItemPickerMenu::ItemPickerMenu( shared_ptr smp, shared_ptr inv ) : AbstractContainerMenu() +{ + inventory = inv; + creativeContainer = smp; + + //int startLength = slots->size(); + + Slot *slot = NULL; + for (int i = 0; i < TabSpec::MAX_SIZE; i++) + { + // 4J JEV - These values get set by addSlot anyway. + slot = new Slot( creativeContainer, i, -1, -1); + + ItemPickerMenu::addSlot( slot ); + } + + for (int i = 0; i < 9; i++) + { + slot = new Slot( inventory, i, -1, -1 ); + ItemPickerMenu::addSlot( slot ); + } + + // 4J Stu - Give the creative menu a unique container id + containerId = CONTAINER_ID_CREATIVE; +} + +bool IUIScene_CreativeMenu::ItemPickerMenu::stillValid(shared_ptr player) +{ + return true; +} + +bool IUIScene_CreativeMenu::ItemPickerMenu::isOverrideResultClick(int slotNum, int buttonNum) +{ + return slotNum >= 0 && slotNum < 9 && buttonNum == 0; +} + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_CreativeMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + if (eTapDirection == eTapStateDown || eTapDirection == eTapStateUp) + { + newSection = eSectionInventoryCreativeUsing; + } + break; + case eSectionInventoryCreativeUsing: + if (eTapDirection == eTapStateDown || eTapDirection == eTapStateUp) + { + newSection = eSectionInventoryCreativeSelector; + } + break; +#ifndef _XBOX + case eSectionInventoryCreativeTab_0: + case eSectionInventoryCreativeTab_1: + case eSectionInventoryCreativeTab_2: + case eSectionInventoryCreativeTab_3: + case eSectionInventoryCreativeTab_4: + case eSectionInventoryCreativeTab_5: + case eSectionInventoryCreativeTab_6: + case eSectionInventoryCreativeTab_7: + case eSectionInventoryCreativeSlider: + /* do nothing */ + break; +#endif + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, 0); + + return newSection; +} + +bool IUIScene_CreativeMenu::handleValidKeyPress(int iPad, int buttonNum, BOOL quickKeyHeld) +{ + // 4J Added - Make pressing the X button clear the hotbar + if(buttonNum == 1) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + for(unsigned int i = TabSpec::MAX_SIZE; i < TabSpec::MAX_SIZE + 9; ++i) + { + shared_ptr newItem = m_menu->getSlot(i)->getItem(); + + if(newItem != NULL) + { + m_menu->getSlot(i)->set(nullptr); + // call this function to synchronize multiplayer item bar + pMinecraft->localgameModes[iPad]->handleCreativeModeItemAdd(nullptr, i - (int)m_menu->slots->size() + 9 + InventoryMenu::USE_ROW_SLOT_START); + } + } + return true; + } + return false; +} + +void IUIScene_CreativeMenu::handleOutsideClicked(int iPad, int buttonNum, BOOL quickKeyHeld) +{ + // Drop items. + Minecraft *pMinecraft = Minecraft::GetInstance(); + + shared_ptr playerInventory = pMinecraft->localplayers[iPad]->inventory; + if (playerInventory->getCarried() != NULL) + { + if (buttonNum == 0) + { + pMinecraft->localgameModes[iPad]->handleCreativeModeItemDrop(playerInventory->getCarried()); + playerInventory->setCarried(nullptr); + } + if (buttonNum == 1) + { + shared_ptr removedItem = playerInventory->getCarried()->remove(1); + pMinecraft->localgameModes[iPad]->handleCreativeModeItemDrop(removedItem); + if (playerInventory->getCarried()->count == 0) playerInventory->setCarried(nullptr); + } + } + + //pMinecraft->localgameModes[m_iPad]->handleInventoryMouseClick(menu->containerId, AbstractContainerMenu::CLICKED_OUTSIDE, buttonNum, quickKeyHeld?true:false, pMinecraft->localplayers[m_iPad] ); +} + +void IUIScene_CreativeMenu::handleAdditionalKeyPress(int iAction) +{ + int dir = 1; + switch(iAction) + { + case ACTION_MENU_LEFT_SCROLL: + dir = -1; + // Fall through intentional + case ACTION_MENU_RIGHT_SCROLL: + { + ECreativeInventoryTabs tab = (ECreativeInventoryTabs)(m_curTab + dir); + if (tab < 0) tab = (ECreativeInventoryTabs)(eCreativeInventoryTab_COUNT - 1); + if (tab >= eCreativeInventoryTab_COUNT) tab = eCreativeInventoryTab_BuildingBlocks; + switchTab(tab); + ui.PlayUISFX(eSFX_Focus); + } + break; + case ACTION_MENU_PAGEUP: + // change the potion strength + { + ++m_tabDynamicPos[m_curTab]; + if(m_tabDynamicPos[m_curTab] >= specs[m_curTab]->m_dynamicGroupsCount) m_tabDynamicPos[m_curTab] = 0; + switchTab(m_curTab); + } + break; + case ACTION_MENU_OTHER_STICK_DOWN: + ++m_tabPage[m_curTab]; + if(m_tabPage[m_curTab] >= specs[m_curTab]->getPageCount()) + { + m_tabPage[m_curTab] = specs[m_curTab]->getPageCount() - 1; + } + else + { + switchTab(m_curTab); + } + break; + case ACTION_MENU_OTHER_STICK_UP: + --m_tabPage[m_curTab]; + if(m_tabPage[m_curTab] < 0) + { + m_tabPage[m_curTab] = 0; + } + else + { + switchTab(m_curTab); + } + break; + } +} + +void IUIScene_CreativeMenu::handleSlotListClicked(ESceneSection eSection, int buttonNum, BOOL quickKeyHeld) +{ + int currentIndex = getCurrentIndex(eSection); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + bool instantPlace = false; + if (eSection == eSectionInventoryCreativeSelector) + { + if (buttonNum == 0) + { + + shared_ptr playerInventory = pMinecraft->localplayers[getPad()]->inventory; + shared_ptr carried = playerInventory->getCarried(); + shared_ptr clicked = m_menu->getSlot(currentIndex)->getItem(); + if (clicked != NULL) + { + playerInventory->setCarried(ItemInstance::clone(clicked)); + carried = playerInventory->getCarried(); + if (quickKeyHeld == TRUE) + { + carried->count = carried->getMaxStackSize(); + } + m_creativeSlotX = m_iCurrSlotX; + m_creativeSlotY = m_iCurrSlotY; + m_eCurrSection = eSectionInventoryCreativeUsing; + m_eCurrTapState = eTapStateJump; + + instantPlace = getEmptyInventorySlot(carried, m_inventorySlotX); + m_iCurrSlotX = m_inventorySlotX; + m_iCurrSlotY = m_inventorySlotY; + + m_bCarryingCreativeItem = true; + } + } + } + if(instantPlace || eSection == eSectionInventoryCreativeUsing) + { + if(instantPlace) + { + setSectionSelectedSlot(eSectionInventoryCreativeUsing,m_iCurrSlotX,m_iCurrSlotY); + currentIndex = getCurrentIndex(eSectionInventoryCreativeUsing); + buttonNum = 0; + quickKeyHeld = FALSE; + } + m_menu->clicked(currentIndex, buttonNum, quickKeyHeld?AbstractContainerMenu::CLICK_QUICK_MOVE:AbstractContainerMenu::CLICK_PICKUP, pMinecraft->localplayers[getPad()]); + shared_ptr newItem = m_menu->getSlot(currentIndex)->getItem(); + // call this function to synchronize multiplayer item bar + pMinecraft->localgameModes[getPad()]->handleCreativeModeItemAdd(newItem, currentIndex - (int)m_menu->slots->size() + 9 + InventoryMenu::USE_ROW_SLOT_START); + + if(m_bCarryingCreativeItem) + { + m_inventorySlotX = m_iCurrSlotX; + m_inventorySlotY = m_iCurrSlotY; + m_eCurrSection = eSectionInventoryCreativeSelector; + m_eCurrTapState = eTapStateJump; + m_iCurrSlotX = m_creativeSlotX; + m_iCurrSlotY = m_creativeSlotY; + + shared_ptr playerInventory = pMinecraft->localplayers[getPad()]->inventory; + playerInventory->setCarried(nullptr); + m_bCarryingCreativeItem = false; + } + } +} + +bool IUIScene_CreativeMenu::IsSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryCreativeUsing: + case eSectionInventoryCreativeSelector: + return true; + } + return false; +} + +bool IUIScene_CreativeMenu::CanHaveFocus( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryCreativeUsing: + case eSectionInventoryCreativeSelector: + return true; + } + return false; +} + +bool IUIScene_CreativeMenu::getEmptyInventorySlot(shared_ptr item, int &slotX) +{ + bool sameItemFound = false; + bool emptySlotFound = false; + // Jump to the slot with this item already on it, if we can stack more + for(unsigned int i = TabSpec::MAX_SIZE; i < TabSpec::MAX_SIZE + 9; ++i) + { + shared_ptr slotItem = m_menu->getSlot(i)->getItem(); + if( slotItem != NULL && slotItem->sameItem(item) && (slotItem->GetCount() + item->GetCount() <= item->getMaxStackSize() )) + { + sameItemFound = true; + slotX = i - TabSpec::MAX_SIZE; + break; + } + } + + if(!sameItemFound) + { + // Find an empty slot + for(unsigned int i = TabSpec::MAX_SIZE; i < TabSpec::MAX_SIZE + 9; ++i) + { + if( m_menu->getSlot(i)->getItem() == NULL ) + { + slotX = i - TabSpec::MAX_SIZE; + emptySlotFound = true; + break; + } + } + } + return sameItemFound || emptySlotFound; +} + +int IUIScene_CreativeMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + offset = 0; + break; + case eSectionInventoryCreativeUsing: + offset = TabSpec::MAX_SIZE; + break; + default: + assert( false ); + break; + } + return offset; +} + +bool IUIScene_CreativeMenu::overrideTooltips(ESceneSection sectionUnderPointer, shared_ptr itemUnderPointer, bool bIsItemCarried, bool bSlotHasItem, bool bCarriedIsSameAsSlot, int iSlotStackSizeRemaining, + EToolTipItem &buttonA, EToolTipItem &buttonX, EToolTipItem &buttonY, EToolTipItem &buttonRT) +{ + bool _override = false; + + if(sectionUnderPointer == eSectionInventoryCreativeSelector) + { + if(bSlotHasItem) + { + buttonA = eToolTipPickUpGeneric; + buttonRT = eToolTipWhatIsThis; + + if(itemUnderPointer->isStackable()) + { + buttonY = eToolTipPickUpAll; + } + else + { + buttonY = eToolTipNone; //eToolTipPickUpGeneric; + } + } + } + else if(sectionUnderPointer == eSectionInventoryCreativeUsing) + { + buttonY = eToolTipNone; + } + buttonX = eToolTipClearQuickSelect; + _override = true; + + return _override; +} diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h new file mode 100644 index 0000000..7ab3ff7 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.h @@ -0,0 +1,122 @@ +#pragma once +#include "IUIScene_AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" +// 4J Stu - This class is for code that is common between XUI and Iggy + +class SimpleContainer; + +class IUIScene_CreativeMenu : public virtual IUIScene_AbstractContainerMenu +{ +public: + // 4J Stu - These map directly to the tabs seenon the screen + enum ECreativeInventoryTabs + { + eCreativeInventoryTab_BuildingBlocks = 0, + eCreativeInventoryTab_Decorations, + eCreativeInventoryTab_RedstoneAndTransport, + eCreativeInventoryTab_Materials, + eCreativeInventoryTab_Food, + eCreativeInventoryTab_ToolsWeaponsArmor, + eCreativeInventoryTab_Brewing, + eCreativeInventoryTab_Misc, + eCreativeInventoryTab_COUNT, + }; + + // 4J Stu - These are logical groupings of items, and be be combined for tabs on-screen + enum ECreative_Inventory_Groups + { + eCreativeInventory_BuildingBlocks, + eCreativeInventory_Decoration, + eCreativeInventory_Redstone, + eCreativeInventory_Transport, + eCreativeInventory_Materials, + eCreativeInventory_Food, + eCreativeInventory_ToolsArmourWeapons, + eCreativeInventory_Brewing, + eCreativeInventory_Potions_Basic, + eCreativeInventory_Potions_Level2, + eCreativeInventory_Potions_Extended, + eCreativeInventory_Potions_Level2_Extended, + eCreativeInventory_Misc, + eCreativeInventoryGroupsCount + }; + + // 4J JEV - Keeping all the tab specifications in one place. + struct TabSpec + { + public: + // 4J JEV - Layout + static const int rows = 5; + static const int columns = 10; + static const int MAX_SIZE = rows * columns; + + // 4J JEV - Images + const LPCWSTR m_icon; + const int m_descriptionId; + const int m_staticGroupsCount; + ECreative_Inventory_Groups *m_staticGroupsA; + const int m_dynamicGroupsCount; + ECreative_Inventory_Groups *m_dynamicGroupsA; + + private: + unsigned int m_pages; + unsigned int m_staticPerPage; + unsigned int m_staticItems; + + public: + TabSpec( LPCWSTR icon, int descriptionId, int staticGroupsCount, ECreative_Inventory_Groups *staticGroups, int dynamicGroupsCount, ECreative_Inventory_Groups *dynamicGroups ); + ~TabSpec(); + + void populateMenu(AbstractContainerMenu *menu, int dynamicIndex, unsigned int page); + unsigned int getPageCount(); + }; + + class ItemPickerMenu : public AbstractContainerMenu + { + protected: + shared_ptr creativeContainer; + shared_ptr inventory; + + public: + ItemPickerMenu( shared_ptr creativeContainer, shared_ptr inventory ); + + virtual bool stillValid(shared_ptr player); + bool isOverrideResultClick(int slotNum, int buttonNum); + protected: + // 4J Stu - Brought forward from 1.2 to fix infinite recursion bug in creative + virtual void loopClick(int slotIndex, int buttonNum, bool quickKeyHeld, shared_ptr player) { } // do nothing + } *itemPickerMenu; + +protected: + static vector< shared_ptr > categoryGroups[eCreativeInventoryGroupsCount]; + // 4J JEV - Tabs + static TabSpec **specs; + + bool m_bCarryingCreativeItem; + int m_creativeSlotX, m_creativeSlotY, m_inventorySlotX, m_inventorySlotY; + +public: + static void staticCtor(); + IUIScene_CreativeMenu(); + +protected: + ECreativeInventoryTabs m_curTab; + int m_tabDynamicPos[eCreativeInventoryTab_COUNT]; + int m_tabPage[eCreativeInventoryTab_COUNT]; + + void switchTab(ECreativeInventoryTabs tab); + virtual void updateTabHighlightAndText(ECreativeInventoryTabs tab) = 0; + virtual void updateScrollCurrentPage(int currentPage, int pageCount) = 0; + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + virtual bool handleValidKeyPress(int iUserIndex, int buttonNum, BOOL quickKeyHeld); + virtual void handleOutsideClicked(int iPad, int buttonNum, BOOL quickKeyHeld); + virtual void handleAdditionalKeyPress(int iAction); + virtual void handleSlotListClicked(ESceneSection eSection, int buttonNum, BOOL quickKeyHeld); + bool getEmptyInventorySlot(shared_ptr item, int &slotX); + int getSectionStartOffset(ESceneSection eSection); + virtual bool IsSectionSlotList( ESceneSection eSection ); + virtual bool CanHaveFocus( ESceneSection eSection ); + + virtual bool overrideTooltips(ESceneSection sectionUnderPointer, shared_ptr itemUnderPointer, bool bIsItemCarried, bool bSlotHasItem, bool bCarriedIsSameAsSlot, int iSlotStackSizeRemaining, + EToolTipItem &buttonA, EToolTipItem &buttonX, EToolTipItem &buttonY, EToolTipItem &buttonRT); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.cpp new file mode 100644 index 0000000..ec8a73c --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" + +#include "IUIScene_DispenserMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_DispenserMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + int xOffset = 0; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionTrapTrap: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionTrapInventory; + xOffset = -TRAP_SCENE_TRAP_SLOT_OFFSET; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionTrapUsing; + xOffset = -TRAP_SCENE_TRAP_SLOT_OFFSET; + } + break; + case eSectionTrapInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionTrapUsing; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionTrapTrap; + xOffset = TRAP_SCENE_TRAP_SLOT_OFFSET; + } + break; + case eSectionTrapUsing: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionTrapTrap; + xOffset = TRAP_SCENE_TRAP_SLOT_OFFSET; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionTrapInventory; + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, xOffset); + + return newSection; +} + +int IUIScene_DispenserMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionTrapTrap: + offset = 0; + break; + case eSectionTrapInventory: + offset = 9; + break; + case eSectionTrapUsing: + offset = 9 + 27; + break; + default: + assert( false ); + break; + } + return offset; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.h b/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.h new file mode 100644 index 0000000..e1826f9 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_DispenserMenu.h @@ -0,0 +1,12 @@ +#pragma once +#include "IUIScene_AbstractContainerMenu.h" + +// The 0-indexed slot in the inventory list that lines up with the result slot +#define TRAP_SCENE_TRAP_SLOT_OFFSET 3 + +class IUIScene_DispenserMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.cpp new file mode 100644 index 0000000..c73f7dc --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "IUIScene_EnchantingMenu.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_EnchantingMenu::GetSectionAndSlotInDirection( IUIScene_AbstractContainerMenu::ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + IUIScene_AbstractContainerMenu::ESceneSection newSection = eSection; + int xOffset = 0; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionEnchantInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionEnchantUsing; + } + else if(eTapDirection == eTapStateUp) + { + if( *piTargetX >= ENCHANT_SCENE_ENCHANT_BUTTONS_UP_OFFSET) + { + newSection = eSectionEnchantButton3; + } + else + { + newSection = eSectionEnchantSlot; + } + } + break; + case eSectionEnchantUsing: + if(eTapDirection == eTapStateDown) + { + if( *piTargetX >= ENCHANT_SCENE_ENCHANT_BUTTONS_UP_OFFSET) + { + newSection = eSectionEnchantButton1; + } + else + { + newSection = eSectionEnchantSlot; + } + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionEnchantInventory; + } + break; + case eSectionEnchantSlot: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionEnchantInventory; + xOffset = ENCHANT_SCENE_INGREDIENT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionEnchantUsing; + xOffset = ENCHANT_SCENE_INGREDIENT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft || eTapDirection == eTapStateRight) + { + newSection = eSectionEnchantButton1; + } + break; + case eSectionEnchantButton1: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionEnchantButton2; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionEnchantUsing; + xOffset = ENCHANT_SCENE_ENCHANT_BUTTONS_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft || eTapDirection == eTapStateRight) + { + newSection = eSectionEnchantSlot; + } + break; + case eSectionEnchantButton2: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionEnchantButton3; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionEnchantButton1; + } + else if(eTapDirection == eTapStateLeft || eTapDirection == eTapStateRight) + { + newSection = eSectionEnchantSlot; + } + break; + case eSectionEnchantButton3: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionEnchantInventory; + xOffset = ENCHANT_SCENE_ENCHANT_BUTTONS_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionEnchantButton2; + } + else if(eTapDirection == eTapStateLeft || eTapDirection == eTapStateRight) + { + newSection = eSectionEnchantSlot; + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, xOffset); + + return newSection; +} + +void IUIScene_EnchantingMenu::handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey) +{ + int index = -1; + // Old xui code +#if 0 + HXUIOBJ hFocusObject = GetFocus(iPad); + if(hFocusObject == m_enchant1->m_hObj) index = 0; + else if(hFocusObject == m_enchant2->m_hObj) index = 1; + else if(hFocusObject == m_enchant3->m_hObj) index = 2; +#endif + + switch(eSection) + { + case eSectionEnchantButton1: + index = 0; + break; + case eSectionEnchantButton2: + index = 1; + break; + case eSectionEnchantButton3: + index = 2; + break; + }; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (index >= 0 && m_menu->clickMenuButton(dynamic_pointer_cast(pMinecraft->localplayers[iPad]), index)) + { + pMinecraft->localgameModes[iPad]->handleInventoryButtonClick(m_menu->containerId, index); + } +} + +int IUIScene_EnchantingMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionEnchantSlot: + offset = 0; + break; + case eSectionEnchantInventory: + offset = 1; + break; + case eSectionEnchantUsing: + offset = 1 + 27; + break; + default: + assert( false ); + break; + }; + return offset; +} + +bool IUIScene_EnchantingMenu::IsSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionEnchantInventory: + case eSectionEnchantUsing: + case eSectionEnchantSlot: + return true; + } + return false; +} + +EnchantmentMenu *IUIScene_EnchantingMenu::getMenu() +{ + return (EnchantmentMenu *)m_menu; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.h b/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.h new file mode 100644 index 0000000..7867265 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_EnchantingMenu.h @@ -0,0 +1,23 @@ +#pragma once + +#include "IUIScene_AbstractContainerMenu.h" + +// The 0-indexed slot in the inventory list that lines up with the result slot +#define ENCHANT_SCENE_ENCHANT_BUTTONS_UP_OFFSET 3 +#define ENCHANT_SCENE_ENCHANT_BUTTONS_DOWN_OFFSET -7 +#define ENCHANT_SCENE_INGREDIENT_SLOT_UP_OFFSET 0 +#define ENCHANT_SCENE_INGREDIENT_SLOT_DOWN_OFFSET 0 + +class EnchantmentMenu; + +class IUIScene_EnchantingMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + virtual void handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey); + int getSectionStartOffset(ESceneSection eSection); + virtual bool IsSectionSlotList( ESceneSection eSection ); + +public: + EnchantmentMenu *getMenu(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.cpp new file mode 100644 index 0000000..4a6c676 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.cpp @@ -0,0 +1,141 @@ +#include "stdafx.h" + +#include "IUIScene_FurnaceMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_FurnaceMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + int xOffset = 0; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionFurnaceResult: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionFurnaceUsing; + xOffset = FURNACE_SCENE_RESULT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionFurnaceInventory; + xOffset = FURNACE_SCENE_RESULT_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionFurnaceIngredient; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionFurnaceIngredient; + } + break; + case eSectionFurnaceIngredient: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionFurnaceUsing; + xOffset = FURNACE_SCENE_FUEL_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateDown) + { + newSection = eSectionFurnaceFuel; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionFurnaceResult; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionFurnaceResult; + } + break; + case eSectionFurnaceFuel: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionFurnaceInventory; + xOffset = FURNACE_SCENE_FUEL_SLOT_DOWN_OFFSET; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionFurnaceIngredient; + } + else if(eTapDirection == eTapStateLeft) + { + newSection = eSectionFurnaceResult; + } + else if(eTapDirection == eTapStateRight) + { + newSection = eSectionFurnaceResult; + } + break; + case eSectionFurnaceInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionFurnaceUsing; + } + else if(eTapDirection == eTapStateUp) + { + if( *piTargetX >= FURNACE_SCENE_RESULT_SLOT_UP_OFFSET) + { + newSection = eSectionFurnaceResult; + } + else + { + newSection = eSectionFurnaceFuel; + } + } + break; + case eSectionFurnaceUsing: + if(eTapDirection == eTapStateUp) + { + newSection = eSectionFurnaceInventory; + } + else if(eTapDirection == eTapStateDown) + { + if( *piTargetX >= FURNACE_SCENE_RESULT_SLOT_UP_OFFSET) + { + newSection = eSectionFurnaceResult; + } + else + { + newSection = eSectionFurnaceIngredient; + } + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, xOffset); + + return newSection; +} + +int IUIScene_FurnaceMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionFurnaceResult: + offset = FurnaceMenu::RESULT_SLOT; + break; + case eSectionFurnaceFuel: + offset = FurnaceMenu::FUEL_SLOT; + break; + case eSectionFurnaceIngredient: + offset = FurnaceMenu::INGREDIENT_SLOT; + break; + case eSectionFurnaceInventory: + offset = FurnaceMenu::INV_SLOT_START; + break; + case eSectionFurnaceUsing: + offset = FurnaceMenu::INV_SLOT_START + 27; + break; + default: + assert( false ); + break; + } + return offset; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.h b/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.h new file mode 100644 index 0000000..1e3b3ba --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_FurnaceMenu.h @@ -0,0 +1,15 @@ +#pragma once +#include "IUIScene_AbstractContainerMenu.h" + +// The 0-indexed slot in the inventory list that lines up with the result slot +#define FURNACE_SCENE_RESULT_SLOT_UP_OFFSET 6 +#define FURNACE_SCENE_RESULT_SLOT_DOWN_OFFSET -7 +#define FURNACE_SCENE_FUEL_SLOT_UP_OFFSET 0 +#define FURNACE_SCENE_FUEL_SLOT_DOWN_OFFSET -3 + +class IUIScene_FurnaceMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.cpp new file mode 100644 index 0000000..7bed406 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" + +#include "IUIScene_InventoryMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +IUIScene_AbstractContainerMenu::ESceneSection IUIScene_InventoryMenu::GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ) +{ + ESceneSection newSection = eSection; + + // Find the new section if there is one + switch( eSection ) + { + case eSectionInventoryArmor: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionInventoryInventory; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionInventoryUsing; + } + break; + case eSectionInventoryInventory: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionInventoryUsing; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionInventoryArmor; + } + break; + case eSectionInventoryUsing: + if(eTapDirection == eTapStateDown) + { + newSection = eSectionInventoryArmor; + } + else if(eTapDirection == eTapStateUp) + { + newSection = eSectionInventoryInventory; + } + break; + default: + assert( false ); + break; + } + + updateSlotPosition(eSection, newSection, eTapDirection, piTargetX, piTargetY, 0); + + return newSection; +} + +int IUIScene_InventoryMenu::getSectionStartOffset(ESceneSection eSection) +{ + int offset = 0; + switch( eSection ) + { + case eSectionInventoryArmor: + offset = InventoryMenu::ARMOR_SLOT_START; + break; + case eSectionInventoryInventory: + offset = InventoryMenu::INV_SLOT_START; + break; + case eSectionInventoryUsing: + offset = InventoryMenu::INV_SLOT_START + 27; + break; + default: + assert( false ); + break; + } + return offset; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.h b/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.h new file mode 100644 index 0000000..30887f8 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_InventoryMenu.h @@ -0,0 +1,10 @@ +#pragma once + +#include "IUIScene_AbstractContainerMenu.h" + +class IUIScene_InventoryMenu : public virtual IUIScene_AbstractContainerMenu +{ +protected: + virtual ESceneSection GetSectionAndSlotInDirection( ESceneSection eSection, ETapState eTapDirection, int *piTargetX, int *piTargetY ); + int getSectionStartOffset(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp new file mode 100644 index 0000000..79203e7 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.cpp @@ -0,0 +1,691 @@ +#include "stdafx.h" +#include "IUIScene_PauseMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MinecraftServer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\DLCTexturePack.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + + +int IUIScene_PauseMenu::ExitGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_PauseMenu *scene = (IUIScene_PauseMenu *)pParam; + + // Results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + scene->SetIgnoreInput(true); + app.SetAction(iPad,eAppAction_ExitWorld); + } + return 0; +} + + +int IUIScene_PauseMenu::ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_PauseMenu *scene = (IUIScene_PauseMenu *)pParam; + + // Exit with or without saving + // Decline means save in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) + { + if( result==C4JStorage::EMessage_ResultDecline ) // Save + { + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { +#ifdef _XBOX + // upsell + ULONGLONG ullOfferID_Full; + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad() , &IUIScene_PauseMenu::WarningTrialTexturePackReturned, scene,app.GetStringTable(), NULL, 0, false); + + return S_OK; + } + } + + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(), &IUIScene_PauseMenu::ExitGameAndSaveReturned, scene, app.GetStringTable(), NULL, 0, false); + return 0; + } + else + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( true ); + } + } + else + { + // been a few requests for a confirm on exit without saving + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_DECLINE_SAVE_GAME, IDS_CONFIRM_DECLINE_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(), &IUIScene_PauseMenu::ExitGameDeclineSaveReturned, scene, app.GetStringTable(), NULL, 0, false); + return 0; + } + + scene->SetIgnoreInput(true); + + app.SetAction(iPad,eAppAction_ExitWorld); + } + return 0; +} + + +int IUIScene_PauseMenu::ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // 4J-PB - we won't come in here if we have a trial texture pack + IUIScene_PauseMenu *scene = (IUIScene_PauseMenu *)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + //INT saveOrCheckpointId = 0; + //bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + //SentientManager.RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId); +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + scene->SetIgnoreInput(true); + MinecraftServer::getInstance()->setSaveOnExit( true ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + else + { + // has someone disconnected the ethernet here, causing the pause menu to shut? + if(ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + UINT uiIDA[3]; + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, ProfileManager.GetPrimaryPad(), &IUIScene_PauseMenu::ExitGameSaveDialogReturned, scene, app.GetStringTable(), NULL, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, ProfileManager.GetPrimaryPad(), &IUIScene_PauseMenu::ExitGameSaveDialogReturned, scene, app.GetStringTable(), NULL, 0, false); + } + } + } + return 0; +} + + + +int IUIScene_PauseMenu::ExitGameDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_PauseMenu *scene = (IUIScene_PauseMenu *)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Don't do this here, as it will still try and save some things even though it shouldn't! + //StorageManager.SetSaveDisabled(false); +#endif + scene->SetIgnoreInput(true); + MinecraftServer::getInstance()->setSaveOnExit( false ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + else + { + // has someone disconnected the ethernet here, causing the pause menu to shut? + if(ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + UINT uiIDA[3]; + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, ProfileManager.GetPrimaryPad(),&IUIScene_PauseMenu::ExitGameSaveDialogReturned, scene, app.GetStringTable(), NULL, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, ProfileManager.GetPrimaryPad(),&IUIScene_PauseMenu::ExitGameSaveDialogReturned, scene, app.GetStringTable(), NULL, 0, false); + } + } + + } + return 0; +} + + + +int IUIScene_PauseMenu::WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(result==C4JStorage::EMessage_ResultAccept) + { + if(!ProfileManager.IsSignedInLive(iPad)) + { + // you're not signed in to PSN! + + } + else + { + // 4J-PB - need to check this user can access the store + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(iPad,true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad,NULL,&app, app.GetStringTable(), NULL, 0, false); + } + else + { + // need to get info on the pack to see if the user has already downloaded it + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + // retrieve the store name for the skin pack + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + const char *pchPackName=wstringtofilename(pDLCPack->getName()); + app.DebugPrintf("Texture Pack - %s\n",pchPackName); + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo((char *)pchPackName); + + if(pSONYDLCInfo!=NULL) + { + char chName[42]; + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + // find the info on the skin pack + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + // So we assume the first sku for the product is the one we want +#ifdef __ORBIS__ + sprintf(chName,"%s",pSONYDLCInfo->chDLCKeyname); +#else + sprintf(chName,"%s-%s",app.GetCommerceCategory(),pSONYDLCInfo->chDLCKeyname); +#endif + app.GetDLCSkuIDFromProductList(chName,chSkuID); + // 4J-PB - need to check for an empty store +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if(app.CheckForEmptyStore(iPad)==false) +#endif + { + if(app.DLCAlreadyPurchased(chSkuID)) + { + app.DownloadAlreadyPurchased(chSkuID); + } + else + { + app.Checkout(chSkuID); + } + } + } + } + } + } +#endif // + +#ifdef _XBOX_ONE + IUIScene_PauseMenu* pScene = (IUIScene_PauseMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + if (ProfileManager.IsSignedInLive(iPad)) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack(); + + DLC_INFO *pDLCInfo=app.GetDLCInfoForProductName((WCHAR *)pDLCPack->getName().c_str()); + + StorageManager.InstallOffer(1,(WCHAR *)pDLCInfo->wsProductId.c_str(),NULL,NULL); + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + else + { + // 4J-JEV: Fix for XB1: #165863 - XR-074: Compliance: With no active network connection user is unable to convert from Trial to Full texture pack and is not messaged why. + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + } + } + +#endif + +#ifdef _XBOX + IUIScene_PauseMenu* pScene = (IUIScene_PauseMenu*)pParam; + + //pScene->m_bIgnoreInput = false; + pScene->ShowScene( true ); + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + ULONGLONG ullIndexA[1]; + + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + // Need to get the parent packs id, since this may be one of many child packs with their own ids + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullIndexA[0]); + + // need to allow downloads here, or the player would need to quit the game to let the download of a texture pack happen. This might affect the network traffic, since the download could take all the bandwidth... + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( pScene->m_pDLCPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } +#endif + + + return 0; +} + + +int IUIScene_PauseMenu::SaveWorldThreadProc( LPVOID lpParameter ) +{ + bool bAutosave=(bool)lpParameter; + if(bAutosave) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_AutoSaveGame); + } + else + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_SaveGame); + } + + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + //wprintf(L"Loading world on thread\n"); + + if(ProfileManager.IsFullVersion()) + { + app.SetGameStarted(false); + + while( app.GetXuiServerAction(ProfileManager.GetPrimaryPad() ) != eXuiServerAction_Idle && !MinecraftServer::serverHalted() ) + { + Sleep(10); + } + + if(!MinecraftServer::serverHalted() && !app.GetChangingSessionType() ) app.SetGameStarted(true); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + if(app.GetGameHostOption(eGameHostOption_DisableSaving)) StorageManager.SetSaveDisabled(true); +#endif + } + + HRESULT hr = S_OK; + if(app.GetChangingSessionType()) + { + // 4J Stu - This causes the fullscreenprogress scene to ignore the action it was given + hr = ERROR_CANCELLED; + } + return hr; +} + +int IUIScene_PauseMenu::ExitWorldThreadProc( void* lpParameter ) +{ + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + //app.SetGameStarted(false); + + _ExitWorld(lpParameter); + + return S_OK; +} + +// This function performs the meat of exiting from a level. It should be called from a thread other than the main thread. +void IUIScene_PauseMenu::_ExitWorld(LPVOID lpParameter) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + int exitReasonStringId = pMinecraft->progressRenderer->getCurrentTitle(); + int exitReasonTitleId = IDS_CONNECTION_LOST; + + bool saveStats = true; + if (pMinecraft->isClientSide() || g_NetworkManager.IsInSession()) + { + if(lpParameter != NULL ) + { + // 4J-PB - check if we have lost connection to Live + if(ProfileManager.GetLiveConnectionStatus()!=XONLINE_S_LOGON_CONNECTION_ESTABLISHED ) + { + exitReasonStringId = IDS_CONNECTION_LOST_LIVE; + } + else + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#if defined(__PS3__) || defined(__ORBIS__) + case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: + exitReasonStringId = IDS_CONTENT_RESTRICTION_MULTIPLAYER; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: + exitReasonStringId = IDS_CONTENT_RESTRICTION; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + case DisconnectPacket::eDisconnect_NoFlying: + exitReasonStringId = IDS_DISCONNECTED_FLYING; + break; + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; +#ifdef __ORBIS__ + case DisconnectPacket::eDisconnect_NetworkError: + exitReasonStringId = IDS_ERROR_NETWORK_EXIT; + exitReasonTitleId = IDS_ERROR_NETWORK_TITLE; + break; +#endif + case DisconnectPacket::eDisconnect_NoFriendsInGame: + exitReasonStringId = IDS_DISCONNECTED_NO_FRIENDS_IN_GAME; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_Banned: + exitReasonStringId = IDS_DISCONNECTED_BANNED; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_NotFriendsWithHost: + exitReasonStringId = IDS_NOTALLOWED_FRIENDSOFFRIENDS; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; +#ifdef _XBOX_ONE + case DisconnectPacket::eDisconnect_ExitedGame: + exitReasonTitleId = IDS_EXIT_GAME; + exitReasonStringId = IDS_DISCONNECTED_EXITED_GAME; + break; +#endif + +#if defined __ORBIS__ || defined __PS3__ || defined __PSVITA__ + case DisconnectPacket::eDisconnect_NATMismatch: + exitReasonStringId = IDS_DISCONNECTED_NAT_TYPE_MISMATCH; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + default: + exitReasonStringId = IDS_CONNECTION_LOST_SERVER; + } + } + //pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + // 4J Stu - Fix for #48669 - TU5: Code: Compliance: TCR #15: Incorrect/misleading messages after signing out a profile during online game session. + // If the primary player is signed out, then that is most likely the cause of the disconnection so don't display a message box. This will allow the message box requested by the libraries to be brought up + if( ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) ui.RequestMessageBox( exitReasonTitleId, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + + // 4J - Force a disconnection, this handles the situation that the server has already disconnected + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(false); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(false); + if( pMinecraft->levels[2] != NULL ) pMinecraft->levels[2]->disconnect(false); + } + else + { + exitReasonStringId = IDS_EXITING_GAME; + pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME ); + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(); + if( pMinecraft->levels[2] != NULL ) pMinecraft->levels[2]->disconnect(); + } + + // 4J Stu - This only does something if we actually have a server, so don't need to do any other checks + MinecraftServer::HaltServer(); + + // We need to call the stats & leaderboards save before we exit the session + // 4J We need to do this in a QNet callback where it is safe + //pMinecraft->forceStatsSave(); + saveStats = false; + + // 4J Stu - Leave the session once the disconnect packet has been sent + g_NetworkManager.LeaveGame(FALSE); + } + else + { + if(lpParameter != NULL && ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad()) ) + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#if defined(__PS3__) || defined(__ORBIS__) + case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: + exitReasonStringId = IDS_CONTENT_RESTRICTION_MULTIPLAYER; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: + exitReasonStringId = IDS_CONTENT_RESTRICTION; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; +#ifdef __ORBIS__ + case DisconnectPacket::eDisconnect_NetworkError: + exitReasonStringId = IDS_ERROR_NETWORK_EXIT; + exitReasonTitleId = IDS_ERROR_NETWORK_TITLE; + break; +#endif + case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin: + exitReasonStringId = IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; +#if defined __ORBIS__ || defined __PS3__ || defined __PSVITA__ + case DisconnectPacket::eDisconnect_NATMismatch: + exitReasonStringId = IDS_DISCONNECTED_NAT_TYPE_MISMATCH; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + default: + exitReasonStringId = IDS_DISCONNECTED; + } + //pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( exitReasonTitleId, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + } + } + // Fix for #93148 - TCR 001: BAS Game Stability: Title will crash for the multiplayer client if host of the game will exit during the clients loading to created world. + while( g_NetworkManager.IsNetworkThreadRunning() ) + { + Sleep(1); + } + pMinecraft->setLevel(NULL,exitReasonStringId,nullptr,saveStats); + + TelemetryManager->Flush(); + + app.m_gameRules.unloadCurrentGameRules(); + //app.m_Audio.unloadCurrentAudioDetails(); + + MinecraftServer::resetFlags(); + + // Fix for #48385 - BLACK OPS :TU5: Functional: Client becomes pseudo soft-locked when returned to the main menu after a remote disconnect + // Make sure there is text explaining why the player is waiting + pMinecraft->progressRenderer->progressStart(IDS_EXITING_GAME); + + // Fix for #13259 - CRASH: Gameplay: loading process is halted when player loads saved data + // We can't start/join a new game until the session is destroyed, so wait for it to be idle again + while( g_NetworkManager.IsInSession() ) + { + Sleep(1); + } + + app.SetChangingSessionType(false); + app.SetReallyChangingSessionType(false); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Make sure we don't think saving is disabled in the menus + StorageManager.SetSaveDisabled(false); +#endif +} + + +int IUIScene_PauseMenu::SaveGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_ENABLE_AUTOSAVE, IDS_CONFIRM_ENABLE_AUTOSAVE, uiIDA, 2, iPad,&IUIScene_PauseMenu::EnableAutosaveDialogReturned,pParam, app.GetStringTable(), NULL, 0, false); +#else + // flag a app action of save game + app.SetAction(iPad,eAppAction_SaveGame); +#endif + } + return 0; +} + +int IUIScene_PauseMenu::EnableAutosaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // Set the global flag, so that we don't disable saving again once the save is complete + app.SetGameHostOption(eGameHostOption_DisableSaving, 0); + } + else + { + // Set the global flag, so that we do disable saving again once the save is complete + // We need to set this on as we may have only disabled it due to having a trial texture pack + app.SetGameHostOption(eGameHostOption_DisableSaving, 1); + } + // Re-enable saving temporarily + StorageManager.SetSaveDisabled(false); + + // flag a app action of save game + app.SetAction(iPad,eAppAction_SaveGame); + return 0; +} + +int IUIScene_PauseMenu::DisableAutosaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // Set the global flag, so that we disable saving again once the save is complete + app.SetGameHostOption(eGameHostOption_DisableSaving, 1); + StorageManager.SetSaveDisabled(false); + + // flag a app action of save game + app.SetAction(iPad,eAppAction_SaveGame); + } + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_PauseMenu.h b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.h new file mode 100644 index 0000000..7233df3 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_PauseMenu.h @@ -0,0 +1,25 @@ +#pragma once + +class IUIScene_PauseMenu +{ +protected: + DLCPack *m_pDLCPack; + +public: + static int ExitGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int EnableAutosaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DisableAutosaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + static int SaveWorldThreadProc( void* lpParameter ); + static int ExitWorldThreadProc( void* lpParameter ); + static void _ExitWorld(LPVOID lpParameter); // Call only from a thread + +protected: + virtual void ShowScene(bool show) = 0; + virtual void SetIgnoreInput(bool ignoreInput) = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp b/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp new file mode 100644 index 0000000..be61177 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp @@ -0,0 +1,379 @@ +#include "stdafx.h" +#include "UI.h" +#include "TexturePack.h" +#include "TexturePackRepository.h" +#include "Minecraft.h" +#include "IUIScene_StartGame.h" + +IUIScene_StartGame::IUIScene_StartGame(int iPad, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_bIgnoreInput = false; + m_iTexturePacksNotInstalled=0; + m_texturePackDescDisplayed = false; + m_bShowTexturePackDescription = false; + m_iSetTexturePackDescription = -1; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(0); +} + +void IUIScene_StartGame::HandleDLCMountingComplete() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + // clear out the current texture pack list + m_texturePackList.clearSlots(); + + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + wchar_t imageName[64]; + swprintf(imageName,64,L"tpack%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + m_texturePackList.addPack(i,imageName); + } + } + + m_iTexturePacksNotInstalled=0; + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + // REMOVE UNTIL WORKING + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + char *pchName=app.GetDLCInfoTextures(i); + pDLCInfo=app.GetDLCInfo(pchName); +#elif defined _XBOX_ONE + pDLCInfo=app.GetDLCInfoForFullOfferID((WCHAR *)app.GetDLCInfoTexturesFullOffer(i).c_str()); +#else + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); +#endif + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + +#if TO_BE_IMPLEMENTED + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + if(m_iConfigA!=NULL) + { + delete m_iConfigA; + } + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } +#endif + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(0); + UpdateTexturePackDescription(m_currentTexturePackIndex); + + m_texturePackList.selectSlot(m_currentTexturePackIndex); + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); +} + +void IUIScene_StartGame::handleSelectionChanged(F64 selectedId) +{ + m_iSetTexturePackDescription = (int)selectedId; + + if(!m_texturePackDescDisplayed) + { + m_bShowTexturePackDescription = true; + } +} + +void IUIScene_StartGame::UpdateTexturePackDescription(int index) +{ + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackByIndex(index); + + if(tp==NULL) + { +#if TO_BE_IMPLEMENTED + // this is probably a texture pack icon added from TMS + + DWORD dwBytes=0,dwFileBytes=0; + PBYTE pbData=NULL,pbFileData=NULL; + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(index); + + app.GetTPD(ListItem.iData,&pbData,&dwBytes); + + app.GetFileFromTPD(eTPDFileType_Loc,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes > 0 && pbFileData) + { + StringTable *pStringTable = new StringTable(pbFileData, dwFileBytes); + m_texturePackTitle.SetText(pStringTable->getString(L"IDS_DISPLAY_NAME")); + m_texturePackDescription.SetText(pStringTable->getString(L"IDS_TP_DESCRIPTION")); + } + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackIconBrush); + m_texturePackIcon->UseBrush(m_hTexturePackIconBrush); + } + app.GetFileFromTPD(eTPDFileType_Comparison,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackComparisonBrush); + m_texturePackComparison->UseBrush(m_hTexturePackComparisonBrush); + } + else + { + m_texturePackComparison->UseBrush(NULL); + } +#endif + } + else + { + m_labelTexturePackName.setLabel(tp->getName()); + m_labelTexturePackDescription.setLabel(tp->getDesc1()); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + //if(dwImageBytes > 0 && pbImageData) + //{ + // registerSubstitutionTexture(L"texturePackIcon", pbImageData, dwImageBytes); + // m_bitmapTexturePackIcon.setTextureName(L"texturePackIcon"); + //} + + wchar_t imageName[64]; + swprintf(imageName,64,L"tpack%08x",tp->getId()); + m_bitmapTexturePackIcon.setTextureName(imageName); + + pbImageData = tp->getPackComparison(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + swprintf(imageName,64,L"texturePackComparison%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + m_bitmapComparison.setTextureName(imageName); + } + else + { + m_bitmapComparison.setTextureName(L""); + } + } +} + +void IUIScene_StartGame::UpdateCurrentTexturePack(int iSlot) +{ + m_currentTexturePackIndex = iSlot; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackByIndex(m_currentTexturePackIndex); + + // if the texture pack is null, you don't have it yet + if(tp==NULL) + { +#if TO_BE_IMPLEMENTED + // Upsell + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&:TexturePackDialogReturned,this,app.GetStringTable()); + + // do set the texture pack id, and on the user pressing create world, check they have it + m_MoreOptionsParams.dwTexturePack = ListItem.iData; + return ; +#endif + } + else + { + m_MoreOptionsParams.dwTexturePack = tp->getId(); + } +} + +int IUIScene_StartGame::TrialTexturePackWarningReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_StartGame* pScene = (IUIScene_StartGame*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + pScene->checkStateAndStartGame(); + } + else + { + pScene->m_bIgnoreInput=false; + } + return 0; +} + +int IUIScene_StartGame::UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_StartGame* pScene = (IUIScene_StartGame*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { +#if defined _XBOX //|| defined _XBOX_ONE + ULONGLONG ullIndexA[1]; + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(pScene->m_pDLCPack->getPurchaseOfferId()); + + if(pDLCInfo!=NULL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Full; + } + else + { + ullIndexA[0]=pScene->m_pDLCPack->getPurchaseOfferId(); + } + + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); +#elif defined _XBOX_ONE + //StorageManager.InstallOffer(1,StorageManager.GetOffer(iIndex).wszProductID,NULL,NULL); +#endif + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + } + else + { +#if defined _XBOX + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( pScene->m_pDLCPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); +#endif + } + + pScene->m_bIgnoreInput = false; + + return 0; +} + +int IUIScene_StartGame::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + IUIScene_StartGame *pClass = (IUIScene_StartGame *)pParam; + + +#ifdef _XBOX + // Exit with or without saving + // Decline means install full version of the texture pack in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultAccept) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=pClass->m_pTexturePacksList->GetData(pClass->m_currentTexturePackIndex); + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + } + else // trial version + { + // if there is no trial version, this is a Cancel + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + } +#elif defined _XBOX_ONE + // Get the product id from the texture pack id + if(result==C4JStorage::EMessage_ResultAccept) + { + + if(ProfileManager.IsSignedIn(iPad)) + { + if (ProfileManager.IsSignedInLive(iPad)) + { + wstring ProductId; + app.GetDLCFullOfferIDForPackID(pClass->m_MoreOptionsParams.dwTexturePack,ProductId); + + + StorageManager.InstallOffer(1,(WCHAR *)ProductId.c_str(),NULL,NULL); + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + else + { + // 4J-JEV: Fix for XB1: #165863 - XR-074: Compliance: With no active network connection user is unable to convert from Trial to Full texture pack and is not messaged why. + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + } + } + +#endif + pClass->m_bIgnoreInput=false; + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_StartGame.h b/Minecraft.Client/Common/UI/IUIScene_StartGame.h new file mode 100644 index 0000000..a336101 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_StartGame.h @@ -0,0 +1,48 @@ +#pragma once + +#include "UIScene.h" + +// Shared functions between CreteWorld, Load and Join +class IUIScene_StartGame : public UIScene +{ +protected: + UIControl_TexturePackList m_texturePackList; + + UIControl m_controlTexturePackPanel; + UIControl_Label m_labelTexturePackName, m_labelTexturePackDescription; + UIControl_BitmapIcon m_bitmapTexturePackIcon, m_bitmapComparison; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_controlTexturePackPanel, "TexturePackPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlTexturePackPanel ) + UI_MAP_ELEMENT( m_labelTexturePackName, "TexturePackName") + UI_MAP_ELEMENT( m_labelTexturePackDescription, "TexturePackDescription") + UI_MAP_ELEMENT( m_bitmapTexturePackIcon, "Icon") + UI_MAP_ELEMENT( m_bitmapComparison, "ComparisonPic") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + LaunchMoreOptionsMenuInitData m_MoreOptionsParams; + bool m_bIgnoreInput; + + int m_iTexturePacksNotInstalled; + unsigned int m_currentTexturePackIndex; + bool m_bShowTexturePackDescription; + bool m_texturePackDescDisplayed; + int m_iSetTexturePackDescription; + + IUIScene_StartGame(int iPad, UILayer *parentLayer); + + virtual void checkStateAndStartGame() = 0; + + virtual void handleSelectionChanged(F64 selectedId); + + virtual void HandleDLCMountingComplete(); + + void UpdateTexturePackDescription(int index); + void UpdateCurrentTexturePack(int iSlot); + + static int TrialTexturePackWarningReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp new file mode 100644 index 0000000..658bcdf --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.cpp @@ -0,0 +1,387 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.trading.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "IUIScene_TradingMenu.h" + +IUIScene_TradingMenu::IUIScene_TradingMenu() +{ + m_validOffersCount = 0; + m_selectedSlot = 0; + m_offersStartIndex = 0; + m_menu = NULL; + m_bHasUpdatedOnce = false; +} + +shared_ptr IUIScene_TradingMenu::getMerchant() +{ + return m_merchant; +} + +bool IUIScene_TradingMenu::handleKeyDown(int iPad, int iAction, bool bRepeat) +{ + bool handled = false; + //MerchantRecipeList *offers = m_merchant->getOffers(Minecraft::GetInstance()->localplayers[getPad()]); + + bool changed = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[getPad()] != NULL ) + { + Tutorial *tutorial = pMinecraft->localgameModes[getPad()]->getTutorial(); + if(tutorial != NULL) + { + tutorial->handleUIInput(iAction); + if(ui.IsTutorialVisible(getPad()) && !tutorial->isInputAllowed(iAction)) + { + return S_OK; + } + } + } + + + switch(iAction) + { + case ACTION_MENU_B: + ui.ShowTooltip( iPad, eToolTipButtonX, false ); + ui.ShowTooltip( iPad, eToolTipButtonB, false ); + ui.ShowTooltip( iPad, eToolTipButtonA, false ); + ui.ShowTooltip( iPad, eToolTipButtonRB, false ); + // kill the crafting xui + //ui.PlayUISFX(eSFX_Back); + ui.CloseUIScenes(iPad); + + handled = true; + break; + case ACTION_MENU_A: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(!m_activeOffers.empty()) + { + int selectedShopItem = (m_selectedSlot + m_offersStartIndex); + if( selectedShopItem < m_activeOffers.size() ) + { + MerchantRecipe *activeRecipe = m_activeOffers.at(selectedShopItem).first; + if(!activeRecipe->isDeprecated()) + { + // Do we have the ingredients? + shared_ptr buyAItem = activeRecipe->getBuyAItem(); + shared_ptr buyBItem = activeRecipe->getBuyBItem(); + shared_ptr player = Minecraft::GetInstance()->localplayers[getPad()]; + int buyAMatches = player->inventory->countMatches(buyAItem); + int buyBMatches = player->inventory->countMatches(buyBItem); + if( (buyAItem != NULL && buyAMatches >= buyAItem->count) && (buyBItem == NULL || buyBMatches >= buyBItem->count) ) + { + m_merchant->notifyTrade(activeRecipe); + + // Remove the items we are purchasing with + player->inventory->removeResources(buyAItem); + player->inventory->removeResources(buyBItem); + + // Add the item we have purchased + shared_ptr result = activeRecipe->getSellItem()->copy(); + if(!player->inventory->add( result ) ) + { + player->drop(result); + } + + // Send a packet to the server + int actualShopItem = m_activeOffers.at(selectedShopItem).second; + player->connection->send( shared_ptr( new TradeItemPacket(m_menu->containerId, actualShopItem) ) ); + + updateDisplay(); + } + } + } + } + handled = true; + break; + case ACTION_MENU_LEFT: + handled = true; + if(m_selectedSlot == 0) + { + if(m_offersStartIndex > 0) + { + --m_offersStartIndex; + changed = true; + } + } + else + { + --m_selectedSlot; + changed = true; + moveSelector(false); + } + break; + case ACTION_MENU_RIGHT: + handled = true; + if(m_selectedSlot == (DISPLAY_TRADES_COUNT - 1)) + { + if((m_offersStartIndex + DISPLAY_TRADES_COUNT) < m_activeOffers.size()) + { + ++m_offersStartIndex; + changed = true; + } + } + else + { + ++m_selectedSlot; + changed = true; + moveSelector(true); + } + break; + } + if (changed) + { + updateDisplay(); + + int selectedShopItem = (m_selectedSlot + m_offersStartIndex); + if( selectedShopItem < m_activeOffers.size() ) + { + int actualShopItem = m_activeOffers.at(selectedShopItem).second; + m_menu->setSelectionHint(actualShopItem); + + ByteArrayOutputStream rawOutput; + DataOutputStream output(&rawOutput); + output.writeInt(actualShopItem); + Minecraft::GetInstance()->getConnection(getPad())->send(shared_ptr( new CustomPayloadPacket(CustomPayloadPacket::TRADER_SELECTION_PACKET, rawOutput.toByteArray()))); + } + } + return handled; +} + +void IUIScene_TradingMenu::handleTick() +{ + int offerCount = 0; + MerchantRecipeList *offers = m_merchant->getOffers(Minecraft::GetInstance()->localplayers[getPad()]); + if (offers != NULL) + { + offerCount = offers->size(); + + if(!m_bHasUpdatedOnce) + { + updateDisplay(); + } + } + + showScrollRightArrow( (m_offersStartIndex + DISPLAY_TRADES_COUNT) < m_activeOffers.size()); + showScrollLeftArrow(m_offersStartIndex > 0); +} + +void IUIScene_TradingMenu::updateDisplay() +{ + int iA = -1; + + MerchantRecipeList *unfilteredOffers = m_merchant->getOffers(Minecraft::GetInstance()->localplayers[getPad()]); + if (unfilteredOffers != NULL) + { + m_activeOffers.clear(); + int unfilteredIndex = 0; + int firstValidTrade = INT_MAX; + for(AUTO_VAR(it, unfilteredOffers->begin()); it != unfilteredOffers->end(); ++it) + { + MerchantRecipe *recipe = *it; + if(!recipe->isDeprecated()) + { + m_activeOffers.push_back( pair(recipe,unfilteredIndex)); + firstValidTrade = min(firstValidTrade,unfilteredIndex); + } + ++unfilteredIndex; + } + + if(!m_bHasUpdatedOnce) + { + if(firstValidTrade != 0 && firstValidTrade < unfilteredOffers->size()) + { + m_menu->setSelectionHint(firstValidTrade); + + ByteArrayOutputStream rawOutput; + DataOutputStream output(&rawOutput); + output.writeInt(firstValidTrade); + Minecraft::GetInstance()->getConnection(getPad())->send(shared_ptr( new CustomPayloadPacket(CustomPayloadPacket::TRADER_SELECTION_PACKET, rawOutput.toByteArray()))); + } + } + + if( (m_offersStartIndex + DISPLAY_TRADES_COUNT) > m_activeOffers.size()) + { + m_offersStartIndex = m_activeOffers.size() - DISPLAY_TRADES_COUNT; + if(m_offersStartIndex < 0) m_offersStartIndex = 0; + } + + for(unsigned int i = 0; i < DISPLAY_TRADES_COUNT; ++i) + { + int offerIndex = i + m_offersStartIndex; + bool showRedBox = false; + if(offerIndex < m_activeOffers.size()) + { + showRedBox = !canMake(m_activeOffers.at(offerIndex).first); + setTradeItem(i, m_activeOffers.at(offerIndex).first->getSellItem() ); + } + else + { + setTradeItem(i, nullptr); + } + setTradeRedBox( i, showRedBox); + } + + int selectedShopItem = (m_selectedSlot + m_offersStartIndex); + if( selectedShopItem < m_activeOffers.size() ) + { + MerchantRecipe *activeRecipe = m_activeOffers.at(selectedShopItem).first; + + wstring wsTemp; + + // 4J-PB - need to get the villager type here + wsTemp = app.GetString(IDS_VILLAGER_OFFERS_ITEM); + wsTemp = replaceAll(wsTemp,L"{*VILLAGER_TYPE*}",app.GetString(m_merchant->getDisplayName())); + int iPos=wsTemp.find(L"%s"); + wsTemp.replace(iPos,2,activeRecipe->getSellItem()->getHoverName()); + + setTitle(wsTemp.c_str()); + + vector unformattedStrings; + wstring offerDescription = GetItemDescription(activeRecipe->getSellItem(), unformattedStrings); + setOfferDescription(offerDescription, unformattedStrings); + + shared_ptr buyAItem = activeRecipe->getBuyAItem(); + shared_ptr buyBItem = activeRecipe->getBuyBItem(); + + setRequest1Item(buyAItem); + setRequest2Item(buyBItem); + + if(buyAItem != NULL) setRequest1Name(buyAItem->getHoverName()); + else setRequest1Name(L""); + + if(buyBItem != NULL) setRequest2Name(buyBItem->getHoverName()); + else setRequest2Name(L""); + + bool canMake = true; + + shared_ptr player = Minecraft::GetInstance()->localplayers[getPad()]; + int buyAMatches = player->inventory->countMatches(buyAItem); + if(buyAMatches > 0) + { + setRequest1RedBox(buyAMatches < buyAItem->count); + canMake = buyAMatches > buyAItem->count; + } + else + { + setRequest1RedBox(true); + canMake = false; + } + + int buyBMatches = player->inventory->countMatches(buyBItem); + if(buyBMatches > 0) + { + setRequest2RedBox(buyBMatches < buyBItem->count); + canMake = canMake && buyBMatches > buyBItem->count; + } + else + { + if(buyBItem!=NULL) + { + setRequest2RedBox(true); + canMake = false; + } + else + { + setRequest2RedBox(buyBItem != NULL); + canMake = canMake && buyBItem == NULL; + } + } + + if(canMake) iA = IDS_TOOLTIPS_TRADE; + } + else + { + setTitle(app.GetString(m_merchant->getDisplayName())); + setRequest1Name(L""); + setRequest2Name(L""); + setRequest1RedBox(false); + setRequest2RedBox(false); + setRequest1Item(nullptr); + setRequest2Item(nullptr); + } + + m_bHasUpdatedOnce = true; + } + + ui.SetTooltips(getPad(), iA, IDS_TOOLTIPS_EXIT); +} + +bool IUIScene_TradingMenu::canMake(MerchantRecipe *recipe) +{ + bool canMake = false; + if (recipe != NULL) + { + if(recipe->isDeprecated()) return false; + + shared_ptr buyAItem = recipe->getBuyAItem(); + shared_ptr buyBItem = recipe->getBuyBItem(); + + shared_ptr player = Minecraft::GetInstance()->localplayers[getPad()]; + int buyAMatches = player->inventory->countMatches(buyAItem); + if(buyAMatches > 0) + { + canMake = buyAMatches >= buyAItem->count; + } + else + { + canMake = buyAItem == NULL; + } + + int buyBMatches = player->inventory->countMatches(buyBItem); + if(buyBMatches > 0) + { + canMake = canMake && buyBMatches >= buyBItem->count; + } + else + { + canMake = canMake && buyBItem == NULL; + } + } + return canMake; +} + + +void IUIScene_TradingMenu::setRequest1Item(shared_ptr item) +{ +} + +void IUIScene_TradingMenu::setRequest2Item(shared_ptr item) +{ +} + +void IUIScene_TradingMenu::setTradeItem(int index, shared_ptr item) +{ +} + +wstring IUIScene_TradingMenu::GetItemDescription(shared_ptr item, vector &unformattedStrings) +{ + if(item == NULL) return L""; + + wstring desc = L""; + vector *strings = item->getHoverTextOnly(nullptr, false, unformattedStrings); + bool firstLine = true; + for(AUTO_VAR(it, strings->begin()); it != strings->end(); ++it) + { + wstring thisString = *it; + if(!firstLine) + { + desc.append( L"
" ); + } + else + { + firstLine = false; + } + desc.append( thisString ); + } + strings->clear(); + delete strings; + return desc; +} diff --git a/Minecraft.Client/Common/UI/IUIScene_TradingMenu.h b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.h new file mode 100644 index 0000000..c8edda6 --- /dev/null +++ b/Minecraft.Client/Common/UI/IUIScene_TradingMenu.h @@ -0,0 +1,58 @@ +#pragma once +#include "..\Minecraft.World\MerchantMenu.h" + +class MerchantRecipe; + +class IUIScene_TradingMenu +{ +protected: + MerchantMenu *m_menu; + shared_ptr m_merchant; + vector< pair > m_activeOffers; + + int m_validOffersCount; + int m_selectedSlot; + int m_offersStartIndex; + bool m_bHasUpdatedOnce; + + eTutorial_State m_previousTutorialState; + + static const int DISPLAY_TRADES_COUNT = 7; + + static const int BUY_A = MerchantMenu::USE_ROW_SLOT_END; + static const int BUY_B = BUY_A + 1; + static const int TRADES_START = BUY_B + 1; + +protected: + IUIScene_TradingMenu(); + + bool handleKeyDown(int iPad, int iAction, bool bRepeat); + void handleTick(); + + virtual void showScrollRightArrow(bool show) = 0; + virtual void showScrollLeftArrow(bool show) = 0; + virtual void moveSelector(bool right) = 0; + virtual void setRequest1Name(const wstring &name) = 0; + virtual void setRequest2Name(const wstring &name) = 0; + virtual void setTitle(const wstring &name) = 0; + + virtual void setRequest1RedBox(bool show) = 0; + virtual void setRequest2RedBox(bool show) = 0; + virtual void setTradeRedBox(int index, bool show) = 0; + + virtual void setOfferDescription(const wstring &name, vector &unformattedStrings) = 0; + + virtual void setRequest1Item(shared_ptr item); + virtual void setRequest2Item(shared_ptr item); + virtual void setTradeItem(int index, shared_ptr item); + +private: + void updateDisplay(); + bool canMake(MerchantRecipe *recipe); + wstring GetItemDescription(shared_ptr item, vector &unformattedStrings); + +public: + shared_ptr getMerchant(); + + virtual int getPad() = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UI.h b/Minecraft.Client/Common/UI/UI.h new file mode 100644 index 0000000..622ccf8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UI.h @@ -0,0 +1,118 @@ +#pragma once + +#include "UIEnums.h" +#include "UIStructs.h" + +#include "UIBitmapFont.h" +#include "UITTFFont.h" + +#include "UIScene.h" +#include "UILayer.h" +#include "UIGroup.h" +#include "UIController.h" + +#include "UIControl.h" +#include "UIControl_Base.h" +#include "UIControl_Button.h" +#include "UIControl_CheckBox.h" +#include "UIControl_Slider.h" +#include "UIControl_Label.h" +#include "UIControl_TextInput.h" +#include "UIControl_SlotList.h" +#include "UIControl_Cursor.h" +#include "UIControl_ButtonList.h" +#include "UIControl_Progress.h" +#include "UIControl_TexturePackList.h" +#include "UIControl_LeaderboardList.h" +#include "UIControl_SaveList.h" +#include "UIControl_PlayerList.h" +#include "UIControl_BitmapIcon.h" +#include "UIControl_DLCList.h" +#include "UIControl_HTMLLabel.h" +#include "UIControl_DynamicLabel.h" +#include "UIControl_MinecraftPlayer.h" +#include "UIControl_PlayerSkinPreview.h" +#include "UIControl_EnchantmentButton.h" +#include "UIControl_EnchantmentBook.h" +#include "UIControl_SpaceIndicatorBar.h" + +#ifdef __PSVITA__ +#include "UIControl_Touch.h" +#endif + +#include "UIScene_HUD.h" +#include "UIComponent_Panorama.h" +#include "UIComponent_Logo.h" +#include "UIComponent_Tooltips.h" +#include "UIComponent_TutorialPopup.h" +#include "UIComponent_Chat.h" +#include "UIComponent_PressStartToPlay.h" +#include "UIComponent_MenuBackground.h" + +#include "UIScene_QuadrantSignin.h" +#include "UIScene_MessageBox.h" +#include "UIScene_Timer.h" +#include "UIScene_Keyboard.h" + +#include "UIScene_DebugOverlay.h" +#include "UIScene_DebugOptions.h" +#include "UIComponent_DebugUIConsole.h" +#include "UIComponent_DebugUIMarketingGuide.h" +#include "UIScene_DebugSetCamera.h" +#include "UIScene_DebugCreateSchematic.h" + +#include "UIScene_TrialExitUpsell.h" +#include "UIScene_Intro.h" +#include "UIScene_SaveMessage.h" +#include "UIScene_MainMenu.h" +#include "UIScene_LoadMenu.h" +#include "UIScene_JoinMenu.h" +#include "UIScene_LoadOrJoinMenu.h" +#include "UIScene_CreateWorldMenu.h" +#include "UIScene_LaunchMoreOptionsMenu.h" +#include "UIScene_FullscreenProgress.h" +#include "UIScene_LeaderboardsMenu.h" +#include "UIScene_DLCMainMenu.h" +#include "UIScene_DLCOffersMenu.h" +#include "UIScene_ReinstallMenu.h" + +#include "UIScene_HelpAndOptionsMenu.h" +#include "UIScene_SettingsMenu.h" +#include "UIScene_SettingsOptionsMenu.h" +#include "UIScene_SettingsAudioMenu.h" +#include "UIScene_SettingsControlMenu.h" +#include "UIScene_SettingsGraphicsMenu.h" +#include "UIScene_SettingsUIMenu.h" +#include "UIScene_SkinSelectMenu.h" +#include "UIScene_HowToPlayMenu.h" +#include "UIScene_HowToPlay.h" +#include "UIScene_ControlsMenu.h" +#include "UIScene_Credits.h" + +#include "UIScene_PauseMenu.h" + +#include "UIScene_AbstractContainerMenu.h" +#include "UIScene_BrewingStandMenu.h" +#include "UIScene_ContainerMenu.h" +#include "UIScene_DispenserMenu.h" +#include "UIScene_EnchantingMenu.h" +#include "UIScene_InventoryMenu.h" +#include "UIScene_FurnaceMenu.h" +#include "UIScene_CreativeMenu.h" +#include "UIScene_TradingMenu.h" +#include "UIScene_AnvilMenu.h" + +#include "UIScene_CraftingMenu.h" +#include "UIScene_SignEntryMenu.h" + +#include "UIScene_ConnectingProgress.h" +#include "UIScene_DeathMenu.h" +#include "UIScene_InGameInfoMenu.h" +#include "UIScene_InGameHostOptionsMenu.h" +#include "UIScene_InGamePlayerOptionsMenu.h" +#if defined(_XBOX_ONE) || defined(__ORBIS__) +#include "UIScene_InGameSaveManagementMenu.h" +#endif +#include "UIScene_TeleportMenu.h" +#include "UIScene_EndPoem.h" +#include "UIScene_EULA.h" diff --git a/Minecraft.Client/Common/UI/UIBitmapFont.cpp b/Minecraft.Client/Common/UI/UIBitmapFont.cpp new file mode 100644 index 0000000..ec49eea --- /dev/null +++ b/Minecraft.Client/Common/UI/UIBitmapFont.cpp @@ -0,0 +1,364 @@ +#include "stdafx.h" + +#include "BufferedImage.h" +#include "UIFontData.h" + +#include + +#include "UIBitmapFont.h" + + +///////////////////////////// +// UI Abstract Bitmap Font // +///////////////////////////// + +UIAbstractBitmapFont::~UIAbstractBitmapFont() +{ + if (m_registered) IggyFontRemoveUTF8( m_fontname.c_str(),-1,IGGY_FONTFLAG_none ); + delete m_bitmapFontProvider; +} + + +UIAbstractBitmapFont::UIAbstractBitmapFont(const string &fontname) +{ + m_fontname = fontname; + + m_registered = false; + + m_bitmapFontProvider = new IggyBitmapFontProvider(); + m_bitmapFontProvider->get_font_metrics = &UIAbstractBitmapFont::GetFontMetrics_Callback; + m_bitmapFontProvider->get_glyph_for_codepoint = &UIAbstractBitmapFont::GetCodepointGlyph_Callback; + m_bitmapFontProvider->get_glyph_metrics = &UIAbstractBitmapFont::GetGlyphMetrics_Callback; + m_bitmapFontProvider->is_empty = &UIAbstractBitmapFont::IsGlyphEmpty_Callback; + m_bitmapFontProvider->get_kerning = &UIAbstractBitmapFont::GetKerningForGlyphPair_Callback; + m_bitmapFontProvider->can_bitmap = &UIAbstractBitmapFont::CanProvideBitmap_Callback; + m_bitmapFontProvider->get_bitmap = &UIAbstractBitmapFont::GetGlyphBitmap_Callback; + m_bitmapFontProvider->free_bitmap = &UIAbstractBitmapFont::FreeGlyphBitmap_Callback; + m_bitmapFontProvider->userdata = this; +} + +void UIAbstractBitmapFont::registerFont() +{ + if(m_registered) + { + return; + } + + m_bitmapFontProvider->num_glyphs = m_numGlyphs; + + IggyFontInstallBitmapUTF8( m_bitmapFontProvider,m_fontname.c_str(),-1,IGGY_FONTFLAG_none ); + IggyFontSetIndirectUTF8( m_fontname.c_str(),-1 ,IGGY_FONTFLAG_all ,m_fontname.c_str() ,-1 ,IGGY_FONTFLAG_none ); + m_registered = true; +} + +IggyFontMetrics * RADLINK UIAbstractBitmapFont::GetFontMetrics_Callback(void *user_context,IggyFontMetrics *metrics) +{ + return ((UIAbstractBitmapFont *) user_context)->GetFontMetrics(metrics); +} + +S32 RADLINK UIAbstractBitmapFont::GetCodepointGlyph_Callback(void *user_context,U32 codepoint) +{ + return ((UIAbstractBitmapFont *) user_context)->GetCodepointGlyph(codepoint); +} + +IggyGlyphMetrics * RADLINK UIAbstractBitmapFont::GetGlyphMetrics_Callback(void *user_context,S32 glyph,IggyGlyphMetrics *metrics) +{ + return ((UIAbstractBitmapFont *) user_context)->GetGlyphMetrics(glyph,metrics); +} + +rrbool RADLINK UIAbstractBitmapFont::IsGlyphEmpty_Callback(void *user_context,S32 glyph) +{ + return ((UIAbstractBitmapFont *) user_context)->IsGlyphEmpty(glyph); +} + +F32 RADLINK UIAbstractBitmapFont::GetKerningForGlyphPair_Callback(void *user_context,S32 first_glyph,S32 second_glyph) +{ + return ((UIAbstractBitmapFont *) user_context)->GetKerningForGlyphPair(first_glyph,second_glyph); +} + +rrbool RADLINK UIAbstractBitmapFont::CanProvideBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale) +{ + return ((UIAbstractBitmapFont *) user_context)->CanProvideBitmap(glyph,pixel_scale); +} + +rrbool RADLINK UIAbstractBitmapFont::GetGlyphBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap) +{ + return ((UIAbstractBitmapFont *) user_context)->GetGlyphBitmap(glyph,pixel_scale,bitmap); +} + +void RADLINK UIAbstractBitmapFont::FreeGlyphBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap) +{ + return ((UIAbstractBitmapFont *) user_context)->FreeGlyphBitmap(glyph,pixel_scale,bitmap); +} + +UIBitmapFont::UIBitmapFont( SFontData &sfontdata ) + : UIAbstractBitmapFont( sfontdata.m_strFontName ) +{ + m_numGlyphs = sfontdata.m_uiGlyphCount; + + BufferedImage bimg(sfontdata.m_wstrFilename); + int *bimgData = bimg.getData(); + + m_cFontData = new CFontData(sfontdata, bimgData); + + //delete [] bimgData; +} + +UIBitmapFont::~UIBitmapFont() +{ + m_cFontData->release(); +} + +//Callback function type for returning vertical font metrics +IggyFontMetrics *UIBitmapFont::GetFontMetrics(IggyFontMetrics *metrics) +{ + //Description + // Vertical metrics for a font + //Members + // ascent - extent of characters above baseline (positive) + // descent - extent of characters below baseline (positive) + // line_gap - spacing between one row's descent and the next line's ascent + // average_glyph_width_for_tab_stops - spacing of "average" character for computing default tab stops + // largest_glyph_bbox_y1 - lowest point below baseline of any character in the font + + metrics->ascent = m_cFontData->getFontData()->m_fAscent; + metrics->descent = m_cFontData->getFontData()->m_fDescent; + + metrics->average_glyph_width_for_tab_stops = 8.0f; + + // This is my best guess, there's no reference to a specific glyph here + // so aren't these just exactly the same. + metrics->largest_glyph_bbox_y1 = metrics->descent; + + // metrics->line_gap; // 4J-JEV: Sean said this does nothing. + + return metrics; +} + +//Callback function type for mapping 32-bit unicode code point to internal font glyph number; use IGGY_GLYPH_INVALID to mean "invalid character" +S32 UIBitmapFont::GetCodepointGlyph(U32 codepoint) +{ + // 4J-JEV: Change "right single quotation marks" to apostrophies. + if (codepoint == 0x2019) codepoint = 0x27; + + return m_cFontData->getGlyphId(codepoint); +} + +//Callback function type for returning horizontal metrics for each glyph +IggyGlyphMetrics * UIBitmapFont::GetGlyphMetrics(S32 glyph,IggyGlyphMetrics *metrics) +{ + // 4J-JEV: Information about 'Glyph Metrics'. + // http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-3.html - Overview. + // http://en.wikipedia.org/wiki/Kerning#Kerning_values - 'Font Units' + + //Description + // Horizontal metrics for a glyph + //Members + // x0 y0 x1 y1 - bounding box + // advance - horizontal distance to move character origin after drawing this glyph + + + /* 4J-JEV: *IMPORTANT* + * + * I believe these are measured wrt the scale mentioned in GetGlyphBitmap + * i.e. 1.0f == pixel_scale, + * + * However we do not have that information here, then all these values need to be + * the same for every scale in this font. + * + * We have 2 scales of bitmap glyph, and we can only scale these up by powers of 2 + * otherwise the fonts will become blurry. The appropriate glyph is chosen in + * 'GetGlyphBitmap' however we need to set the horizontal sizes here. + */ + + float glyphAdvance = m_cFontData->getAdvance(glyph); + + // 4J-JEV: Anything outside this measurement will be + // cut off if it's at the start or end of the row. + metrics->x0 = 0.0f; + + if ( m_cFontData->glyphIsWhitespace(glyph) ) + metrics->x1 = 0.0f; + else + metrics->x1 = glyphAdvance; + + // The next Glyph just starts right after this one. + metrics->advance = glyphAdvance; + + //app.DebugPrintf("[UIBitmapFont] GetGlyphMetrics:\n\tmetrics->advance == %f,\n", metrics->advance); + + // These don't do anything either. + metrics->y0 = 0.0f; metrics->y1 = 1.0f; + + return metrics; +} + +//Callback function type that should return true iff the glyph has no visible elements +rrbool UIBitmapFont::IsGlyphEmpty (S32 glyph) +{ + if (m_cFontData->glyphIsWhitespace(glyph)) return true; + return false;//app.DebugPrintf("Is glyph %d empty? %s\n",glyph,isEmpty?"TRUE":"FALSE"); +} + +//Callback function type for returning the kerning amount for a given pair of glyphs +F32 UIBitmapFont::GetKerningForGlyphPair(S32 first_glyph,S32 second_glyph) +{ + //UIBitmapFont *uiFont = (UIBitmapFont *) user_context; + //app.DebugPrintf("Get kerning for glyph pair %d,%d\n",first_glyph,second_glyph); + + // 4J-JEV: Yet another field that doesn't do anything. + // Only set out of paranoia. + return 0.0f; +} + +//Callback function type used for reporting whether a bitmap supports a given glyph at the given scale +rrbool UIBitmapFont::CanProvideBitmap(S32 glyph,F32 pixel_scale) +{ + //app.DebugPrintf("Can provide bitmap for glyph %d at scale %f? %s\n",glyph,pixel_scale,canProvideBitmap?"TRUE":"FALSE"); + return true; +} + +// Description +// Callback function type used for getting the bitmap for a given glyph +// Parameters +// glyph The glyph to compute/get the bitmap for +// pixel_scale The scale factor (pseudo point size) requested by the textfield,adjusted for display resolution +// bitmap The structure to store the bitmap into +rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap) +{ + //Description + // Data structure used to return to Iggy the bitmap to use for a glyph + //Members + // pixels_one_per_byte - pixels startin with the top-left-most; 0 is transparent and 255 is opaque + // width_in_pixels - this is the width of the bitmap data + // height_in_pixels - this is the height of the bitmap data + // stride_in_bytes - the distance from one row to the next + // oversample - this is the amount of oversampling (0 or 1 = not oversample,2 = 2x oversampled,4 = 4x oversampled) + // point_sample - if true,the bitmap will be drawn with point sampling; if false,it will be drawn with bilinear + // top_left_x - the offset of the top left corner from the character origin + // top_left_y - the offset of the top left corner from the character origin + // pixel_scale_correct - the pixel_scale at which this character should be displayed at displayed_width_in_pixels + // pixel_scale_min - the smallest pixel_scale to allow using this character (scaled down) + // pixel_scale_max - the largest pixels cale to allow using this character (scaled up) + // user_context_for_free - you can use this to store data to access on the corresponding free call + + int row = 0,col = 0; + m_cFontData->getPos(glyph,row,col); + + // Skip to glyph start. + bitmap->pixels_one_per_byte = m_cFontData->topLeftPixel(row,col); + + // Choose a reasonable glyph scale. + float glyphScale = 1.0f, truePixelScale = 1.0f / m_cFontData->getFontData()->m_fAdvPerPixel; + F32 targetPixelScale = pixel_scale; + //if(!RenderManager.IsWidescreen()) + //{ + // // Fix for different scales in 480 + // targetPixelScale = pixel_scale*2/3; + //} + while ( (0.5f + glyphScale) * truePixelScale < targetPixelScale) + glyphScale++; + + // 4J-JEV: Debug code to check which font sizes are being used. +#if (!defined _CONTENT_PACKAGE) && (VERBOSE_FONT_OUTPUT > 0) + + struct DebugData + { + string name; + long scale; + long mul; + + bool operator==(const DebugData& dd) const + { + if ( name.compare(dd.name) != 0 ) return false; + else if (scale != dd.scale) return false; + else if (mul != dd.mul) return false; + else return true; + } + }; + + static long long lastPrint = System::currentTimeMillis(); + static unordered_set debug_fontSizesRequested; + + { + DebugData dData = { m_cFontData->getFontName(), (long) pixel_scale, (long) glyphScale }; + debug_fontSizesRequested.insert(dData); + + if ( (lastPrint - System::currentTimeMillis()) > VERBOSE_FONT_OUTPUT ) + { + app.DebugPrintf(" Requested font/sizes:\n"); + + unordered_set::iterator itr; + for ( itr = debug_fontSizesRequested.begin(); + itr != debug_fontSizesRequested.end(); + itr++ + ) + { + app.DebugPrintf("\t- %s:%i\t(x%i)\n", itr->name.c_str(), itr->scale, itr->mul); + } + + lastPrint = System::currentTimeMillis(); + debug_fontSizesRequested.clear(); + } + } +#endif + + //app.DebugPrintf("Request glyph_%d (U+%.4X) at %f, converted to %f (%f)\n", + // glyph, GetUnicode(glyph), pixel_scale, targetPixelScale, glyphScale); + + // It is not necessary to shrink the glyph width here + // as its already been done in 'GetGlyphMetrics' by: + // > metrics->x1 = m_kerningTable[glyph] * ratio; + bitmap->width_in_pixels = m_cFontData->getFontData()->m_uiGlyphWidth; + bitmap->height_in_pixels = m_cFontData->getFontData()->m_uiGlyphHeight; + + /* 4J-JEV: This is to do with glyph placement, + * and not the position in the archive. + * I don't know why the 0.65 is needed, or what it represents, + * although it doesn't look like its the baseline. + */ + bitmap->top_left_x = 0; + + // 4J-PB - this was chopping off the top of the characters, so accented ones were losing a couple of pixels at the top + // DaveK has reduced the height of the accented capitalised characters, and we've dropped this from 0.65 to 0.64 + bitmap->top_left_y = -((S32) m_cFontData->getFontData()->m_uiGlyphHeight) * m_cFontData->getFontData()->m_fAscent; + + bitmap->oversample = 0; + bitmap->point_sample = true; + + // 4J-JEV: + // pixel_scale == font size chosen in flash. + // bitmap->pixel_scale_correct = (float) m_glyphHeight; // Scales the glyph to desired size. + // bitmap->pixel_scale_correct = pixel_scale; // Always the same size (not desired size). + // bitmap->pixel_scale_correct = pixel_scale * 0.5; // Doubles original size. + // bitmap->pixel_scale_correct = pixel_scale * 2; // Halves original size. + + // Actual scale, and possible range of scales. + bitmap->pixel_scale_correct = pixel_scale / glyphScale; + bitmap->pixel_scale_max = 99.0f; + bitmap->pixel_scale_min = 0.0f; + + /* 4J-JEV: Some of Sean's code. + int glyphScaleMin = 1; + int glyphScaleMax = 3; + float actualScale = pixel_scale / glyphScale; + bitmap->pixel_scale_correct = actualScale; + bitmap->pixel_scale_min = actualScale * glyphScaleMin * 0.999f; + bitmap->pixel_scale_max = actualScale * glyphScaleMax * 1.001f; */ + + // 4J-JEV: Nothing to do with glyph placement, + // entirely to do with cropping your glyph out of an archive. + bitmap->stride_in_bytes = m_cFontData->getFontData()->m_uiGlyphMapX; + + // 4J-JEV: Additional information needed to release memory afterwards. + bitmap->user_context_for_free = NULL; + + return true; +} + +//Callback function type for freeing a bitmap shape returned by GetGlyphBitmap +void RADLINK UIBitmapFont::FreeGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap) +{ + // We don't need to free anything,it just comes from the archive. + //app.DebugPrintf("Free bitmap for glyph %d at scale %f\n",glyph,pixel_scale); +} diff --git a/Minecraft.Client/Common/UI/UIBitmapFont.h b/Minecraft.Client/Common/UI/UIBitmapFont.h new file mode 100644 index 0000000..62b708f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIBitmapFont.h @@ -0,0 +1,75 @@ +#pragma once + +struct SFontData; +class CFontData; + +#define VERBOSE_FONT_OUTPUT 0 + +// const int BITMAP_FONT_LANGUAGES = XC_LANGUAGE_ENGLISH +// | XC_LANGUAGE_GERMAN +// | XC_LANGUAGE_FRENCH +// | XC_LANGUAGE_SPANISH +// | XC_LANGUAGE_ITALIAN +// | XC_LANGUAGE_PORTUGUESE +// | XC_LANGUAGE_BRAZILIAN; + +using namespace std; + +class UIAbstractBitmapFont +{ +protected: + string m_fontname; + + IggyBitmapFontProvider *m_bitmapFontProvider; + + bool m_registered; + + unsigned int m_numGlyphs; + +public: + UIAbstractBitmapFont(const string &fontname); + ~UIAbstractBitmapFont(); + + void registerFont(); + + // Virtual Functions. + virtual IggyFontMetrics *GetFontMetrics(IggyFontMetrics *metrics) = 0; + virtual S32 GetCodepointGlyph(U32 codepoint) = 0; + virtual IggyGlyphMetrics *GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics) = 0; + virtual rrbool IsGlyphEmpty(S32 glyph) = 0; + virtual F32 GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph) = 0; + virtual rrbool CanProvideBitmap(S32 glyph, F32 pixel_scale) = 0; + virtual rrbool GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap) = 0; + virtual void FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap) = 0; + + // Static Callbacks + // Just wrappers for the virtual functions. + static IggyFontMetrics * RADLINK GetFontMetrics_Callback(void *user_context, IggyFontMetrics *metrics); + static S32 RADLINK GetCodepointGlyph_Callback(void *user_context, U32 codepoint); + static IggyGlyphMetrics * RADLINK GetGlyphMetrics_Callback(void *user_context, S32 glyph, IggyGlyphMetrics *metrics); + static rrbool RADLINK IsGlyphEmpty_Callback(void *user_context, S32 glyph); + static F32 RADLINK GetKerningForGlyphPair_Callback(void *user_context, S32 first_glyph, S32 second_glyph); + static rrbool RADLINK CanProvideBitmap_Callback(void *user_context, S32 glyph, F32 pixel_scale); + static rrbool RADLINK GetGlyphBitmap_Callback(void *user_context, S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); + static void RADLINK FreeGlyphBitmap_Callback(void *user_context, S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); +}; + +class UIBitmapFont : public UIAbstractBitmapFont +{ +protected: + CFontData *m_cFontData; + +public: + UIBitmapFont(SFontData &sfontdata); + + ~UIBitmapFont(); + + virtual IggyFontMetrics * GetFontMetrics(IggyFontMetrics *metrics); + virtual S32 GetCodepointGlyph(U32 codepoint); + virtual IggyGlyphMetrics * GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics); + virtual rrbool IsGlyphEmpty(S32 glyph); + virtual F32 GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph); + virtual rrbool CanProvideBitmap(S32 glyph, F32 pixel_scale); + virtual rrbool GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); + virtual void FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Chat.cpp b/Minecraft.Client/Common/UI/UIComponent_Chat.cpp new file mode 100644 index 0000000..98b4f16 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Chat.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_Chat.h" +#include "..\..\Minecraft.h" +#include "..\..\Gui.h" + +UIComponent_Chat::UIComponent_Chat(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i) + { + m_labelChatText[i].init(L""); + } + m_labelJukebox.init(L""); + + addTimer(0, 100); +} + +wstring UIComponent_Chat::getMoviePath() +{ + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + m_bSplitscreen = true; + return L"ComponentChatSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + m_bSplitscreen = false; + return L"ComponentChat"; + break; + } +} + +void UIComponent_Chat::handleTimerComplete(int id) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + bool anyVisible = false; + if(pMinecraft->localplayers[m_iPad]!= NULL) + { + Gui *pGui = pMinecraft->gui; + //DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) ); + for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) + { + float opacity = pGui->getOpacity(m_iPad, i); + if( opacity > 0 ) + { + m_controlLabelBackground[i].setOpacity(opacity); + m_labelChatText[i].setOpacity(opacity); + m_labelChatText[i].setLabel( pGui->getMessage(m_iPad,i) ); + + anyVisible = true; + } + else + { + m_controlLabelBackground[i].setOpacity(0); + m_labelChatText[i].setOpacity(0); + m_labelChatText[i].setLabel(L""); + } + } + if(pGui->getJukeboxOpacity(m_iPad) > 0) anyVisible = true; + m_labelJukebox.setOpacity( pGui->getJukeboxOpacity(m_iPad) ); + m_labelJukebox.setLabel( pGui->getJukeboxMessage(m_iPad) ); + } + else + { + for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) + { + m_controlLabelBackground[i].setOpacity(0); + m_labelChatText[i].setOpacity(0); + m_labelChatText[i].setLabel(L""); + } + m_labelJukebox.setOpacity( 0 ); + } + + setVisible(anyVisible); +} + +void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(m_bSplitscreen) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + } + ui.setupRenderPosition(xPos, yPos); + + S32 tileXStart = 0; + S32 tileYStart = 0; + S32 tileWidth = width; + S32 tileHeight = height; + + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + tileHeight = (S32)(ui.getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + tileYStart = (S32)(m_movieHeight / 2); + break; + } + + IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight ); + + IggyPlayerDrawTilesStart ( getMovie() ); + + m_renderWidth = tileWidth; + m_renderHeight = tileHeight; + IggyPlayerDrawTile ( getMovie() , + tileXStart , + tileYStart , + tileXStart + tileWidth , + tileYStart + tileHeight , + 0 ); + IggyPlayerDrawTilesEnd ( getMovie() ); + } + else + { + UIScene::render(width, height, viewport); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Chat.h b/Minecraft.Client/Common/UI/UIComponent_Chat.h new file mode 100644 index 0000000..d18352c --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Chat.h @@ -0,0 +1,66 @@ +#pragma once + +#include "UIScene.h" + +#define CHAT_LINES_COUNT 10 + +class UIComponent_Chat : public UIScene +{ +private: + bool m_bSplitscreen; + +protected: + UIControl_Label m_labelChatText[CHAT_LINES_COUNT]; + UIControl_Label m_labelJukebox; + UIControl m_controlLabelBackground[CHAT_LINES_COUNT]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_labelChatText[0],"Label1") + UI_MAP_ELEMENT(m_labelChatText[1],"Label2") + UI_MAP_ELEMENT(m_labelChatText[2],"Label3") + UI_MAP_ELEMENT(m_labelChatText[3],"Label4") + UI_MAP_ELEMENT(m_labelChatText[4],"Label5") + UI_MAP_ELEMENT(m_labelChatText[5],"Label6") + UI_MAP_ELEMENT(m_labelChatText[6],"Label7") + UI_MAP_ELEMENT(m_labelChatText[7],"Label8") + UI_MAP_ELEMENT(m_labelChatText[8],"Label9") + UI_MAP_ELEMENT(m_labelChatText[9],"Label10") + + UI_MAP_ELEMENT(m_controlLabelBackground[0],"Label1Background") + UI_MAP_ELEMENT(m_controlLabelBackground[1],"Label2Background") + UI_MAP_ELEMENT(m_controlLabelBackground[2],"Label3Background") + UI_MAP_ELEMENT(m_controlLabelBackground[3],"Label4Background") + UI_MAP_ELEMENT(m_controlLabelBackground[4],"Label5Background") + UI_MAP_ELEMENT(m_controlLabelBackground[5],"Label6Background") + UI_MAP_ELEMENT(m_controlLabelBackground[6],"Label7Background") + UI_MAP_ELEMENT(m_controlLabelBackground[7],"Label8Background") + UI_MAP_ELEMENT(m_controlLabelBackground[8],"Label9Background") + UI_MAP_ELEMENT(m_controlLabelBackground[9],"Label10Background") + + UI_MAP_ELEMENT(m_labelJukebox,"Jukebox") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIComponent_Chat(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_Chat;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); + +protected: + void handleTimerComplete(int id); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.cpp b/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.cpp new file mode 100644 index 0000000..7436d79 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_DebugUIConsole.h" + +UIComponent_DebugUIConsole::UIComponent_DebugUIConsole(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bTextChanged = false; +} + +wstring UIComponent_DebugUIConsole::getMoviePath() +{ + return L"DebugUIConsoleComponent"; +} + +void UIComponent_DebugUIConsole::tick() +{ + UIScene::tick(); + if(m_bTextChanged) + { + m_bTextChanged = false; + for(unsigned int i = 0; i < 10 && i < m_textList.size(); ++i) + { + m_labels[i].setLabel(m_textList[i]); + } + } +} + +void UIComponent_DebugUIConsole::addText(const string &text) +{ + if(!text.empty() && text.compare("\n") != 0) + { + if(m_textList.size() >= 10) m_textList.pop_front(); + m_textList.push_back(text); + m_bTextChanged = true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.h b/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.h new file mode 100644 index 0000000..177754f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_DebugUIConsole.h @@ -0,0 +1,49 @@ +#pragma once + +#include "UIScene.h" +#include "UIControl_Label.h" + +class UIComponent_DebugUIConsole : public UIScene +{ +private: + UIControl_Label m_labels[10]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_labels[0], "consoleLine1") + UI_MAP_ELEMENT( m_labels[1], "consoleLine2") + UI_MAP_ELEMENT( m_labels[2], "consoleLine3") + UI_MAP_ELEMENT( m_labels[3], "consoleLine4") + UI_MAP_ELEMENT( m_labels[4], "consoleLine5") + UI_MAP_ELEMENT( m_labels[5], "consoleLine6") + UI_MAP_ELEMENT( m_labels[6], "consoleLine7") + UI_MAP_ELEMENT( m_labels[7], "consoleLine8") + UI_MAP_ELEMENT( m_labels[8], "consoleLine9") + UI_MAP_ELEMENT( m_labels[9], "consoleLine10") + UI_END_MAP_ELEMENTS_AND_NAMES() + + deque m_textList; + + bool m_bTextChanged; + +public: + UIComponent_DebugUIConsole(int iPad, void *initData, UILayer *parentLayer); + + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_DebugUIConsole;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + void addText(const string &text); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.cpp b/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.cpp new file mode 100644 index 0000000..240429b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_DebugUIMarketingGuide.h" + +UIComponent_DebugUIMarketingGuide::UIComponent_DebugUIMarketingGuide(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = (F64)0; // WIN64 +#if defined _XBOX + value[0].number = (F64)1; +#elif defined _DURANGO + value[0].number = (F64)2; +#elif defined __PS3__ + value[0].number = (F64)3; +#elif defined __ORBIS__ + value[0].number = (F64)4; +#elif defined __PSVITA__ + value[0].number = (F64)5; +#elif defined _WINDOWS64 + value[0].number = (F64)0; +#endif + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetPlatform , 1 , value ); +} + +wstring UIComponent_DebugUIMarketingGuide::getMoviePath() +{ + return L"DebugUIMarketingGuide"; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.h b/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.h new file mode 100644 index 0000000..2c65117 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_DebugUIMarketingGuide.h @@ -0,0 +1,34 @@ +#pragma once + +#include "UIScene.h" +#include "UIControl_Label.h" + +class UIComponent_DebugUIMarketingGuide : public UIScene +{ +private: + IggyName m_funcSetPlatform; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_NAME( m_funcSetPlatform, L"SetPlatform") + UI_END_MAP_ELEMENTS_AND_NAMES() + + +public: + UIComponent_DebugUIMarketingGuide(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_DebugUIMarketingGuide;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Logo.cpp b/Minecraft.Client/Common/UI/UIComponent_Logo.cpp new file mode 100644 index 0000000..2f5c82b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Logo.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_Logo.h" + +UIComponent_Logo::UIComponent_Logo(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); +} + +wstring UIComponent_Logo::getMoviePath() +{ + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + return L"ComponentLogoSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + return L"ComponentLogo"; + break; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Logo.h b/Minecraft.Client/Common/UI/UIComponent_Logo.h new file mode 100644 index 0000000..1a8cf81 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Logo.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIScene.h" + +class UIComponent_Logo : public UIScene +{ +public: + UIComponent_Logo(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_Logo;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_MenuBackground.cpp b/Minecraft.Client/Common/UI/UIComponent_MenuBackground.cpp new file mode 100644 index 0000000..d3a4c4c --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_MenuBackground.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_MenuBackground.h" + +UIComponent_MenuBackground::UIComponent_MenuBackground(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_bSplitscreen = false; + // Setup all the Iggy references we need for this scene + initialiseMovie(); +} + +wstring UIComponent_MenuBackground::getMoviePath() +{ switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + m_bSplitscreen = true; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + m_bSplitscreen = false; + break; + } + + // We use the fullscreen one even in splitscreen, just draw different parts of it + return L"MenuBackground"; +} + +void UIComponent_MenuBackground::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(m_bSplitscreen) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + } + ui.setupRenderPosition(xPos, yPos); + + S32 tileXStart = 0; + S32 tileYStart = 0; + S32 tileWidth = width; + S32 tileHeight = height; + + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + tileHeight = (S32)(ui.getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + tileYStart = (S32)(m_movieHeight / 2); + break; + } + + IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight ); + + IggyPlayerDrawTilesStart ( getMovie() ); + + m_renderWidth = tileWidth; + m_renderHeight = tileHeight; + IggyPlayerDrawTile ( getMovie() , + tileXStart , + tileYStart , + tileXStart + tileWidth , + tileYStart + tileHeight , + 0 ); + IggyPlayerDrawTilesEnd ( getMovie() ); + } + else + { + UIScene::render(width, height, viewport); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_MenuBackground.h b/Minecraft.Client/Common/UI/UIComponent_MenuBackground.h new file mode 100644 index 0000000..ac0b9d2 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_MenuBackground.h @@ -0,0 +1,30 @@ +#pragma once + +#include "UIScene.h" + +class UIComponent_MenuBackground : public UIScene +{ +private: + bool m_bSplitscreen; +public: + UIComponent_MenuBackground(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_MenuBackground;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp b/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp new file mode 100644 index 0000000..a418fcd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Panorama.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_Panorama.h" +#include "Minecraft.h" +#include "MultiPlayerLevel.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.storage.h" + +UIComponent_Panorama::UIComponent_Panorama(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bShowingDay = true; + + while(!m_hasTickedOnce) tick(); +} + +wstring UIComponent_Panorama::getMoviePath() +{ + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + m_bSplitscreen = true; + return L"PanoramaSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + m_bSplitscreen = false; + return L"Panorama"; + break; + } +} + +void UIComponent_Panorama::tick() +{ + if(!hasMovie()) return; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + EnterCriticalSection(&pMinecraft->m_setLevelCS); + if(pMinecraft->level!=NULL) + { + __int64 i64TimeOfDay =0; + // are we in the Nether? - Leave the time as 0 if we are, so we show daylight + if(pMinecraft->level->dimension->id==0) + { + i64TimeOfDay = pMinecraft->level->getLevelData()->getTime() % 24000; + } + + if(i64TimeOfDay>14000) + { + setPanorama(false); + } + else + { + setPanorama(true); + } + } + else + { + setPanorama(true); + } + LeaveCriticalSection(&pMinecraft->m_setLevelCS); + + UIScene::tick(); +} + +void UIComponent_Panorama::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + bool specialViewport = (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_TOP) || + (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM) || + (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || + (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT); + if(m_bSplitscreen && specialViewport) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + } + ui.setupRenderPosition(xPos, yPos); + + if((viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT)) + { + // Need to render at full height, but only the left side of the scene + S32 tileXStart = 0; + S32 tileYStart = 0; + S32 tileWidth = width; + S32 tileHeight = (S32)(ui.getScreenHeight()); + + IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight ); + + IggyPlayerDrawTilesStart ( getMovie() ); + + m_renderWidth = tileWidth; + m_renderHeight = tileHeight; + IggyPlayerDrawTile ( getMovie() , + tileXStart , + tileYStart , + tileXStart + tileWidth , + tileYStart + tileHeight , + 0 ); + IggyPlayerDrawTilesEnd ( getMovie() ); + } + else + { + // Need to render at full height, and full width. But compressed into the viewport + IggyPlayerSetDisplaySize( getMovie(), ui.getScreenWidth(), ui.getScreenHeight()/2 ); + IggyPlayerDraw( getMovie() ); + } + } + else + { + UIScene::render(width, height, viewport); + } +} + +void UIComponent_Panorama::setPanorama(bool isDay) +{ + if(isDay != m_bShowingDay) + { + m_bShowingDay = isDay; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = isDay; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowPanoramaDay , 1 , value ); + } +} diff --git a/Minecraft.Client/Common/UI/UIComponent_Panorama.h b/Minecraft.Client/Common/UI/UIComponent_Panorama.h new file mode 100644 index 0000000..99dc115 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Panorama.h @@ -0,0 +1,40 @@ +#pragma once + +#include "UIScene.h" + +class UIComponent_Panorama : public UIScene +{ +private: + bool m_bSplitscreen; + bool m_bShowingDay; + +protected: + IggyName m_funcShowPanoramaDay; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_NAME(m_funcShowPanoramaDay, L"ShowPanoramaDay"); + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIComponent_Panorama(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_Panorama;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + virtual void tick(); + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); + +private: + void setPanorama(bool isDay); +}; diff --git a/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.cpp b/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.cpp new file mode 100644 index 0000000..2feb94c --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.cpp @@ -0,0 +1,167 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_PressStartToPlay.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIComponent_PressStartToPlay::UIComponent_PressStartToPlay(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_showingSaveIcon = false; + m_showingAutosaveTimer = false; + m_showingTrialTimer = false; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_showingPressStart[i] = false; + } + m_trialTimer = L""; + m_autosaveTimer = L""; + + m_labelTrialTimer.init(L""); + m_labelTrialTimer.setVisible(false); + +#ifdef __ORBIS__ + wstring text = app.GetString(IDS_PRESS_X_TO_JOIN); + text = replaceAll(text, L"{*CONTROLLER_VK_A*}", app.GetVKReplacement(VK_PAD_A) ); + + m_labelPressStart.init(text.c_str()); +#elif defined _XBOX_ONE + wstring text = app.GetString(IDS_PRESS_START_TO_JOIN); + text = replaceAll(text, L"{*CONTROLLER_VK_START*}", app.GetVKReplacement(VK_PAD_START) ); + m_labelPressStart.init(text.c_str()); +#else + m_labelPressStart.init(app.GetString(IDS_PRESS_START_TO_JOIN)); +#endif + m_controlSaveIcon.setVisible(false); + m_controlPressStartPanel.setVisible(false); + m_playerDisplayName.setVisible(false); +} + +wstring UIComponent_PressStartToPlay::getMoviePath() +{ + return L"PressStartToPlay"; +} + +void UIComponent_PressStartToPlay::handleReload() +{ + // 4J Stu - It's possible these could change during the reload, so can't use the normal controls refresh of it's state + m_controlSaveIcon.setVisible(m_showingSaveIcon); + m_labelTrialTimer.setVisible(m_showingAutosaveTimer); + m_labelTrialTimer.setLabel(m_autosaveTimer); + m_labelTrialTimer.setVisible(m_showingTrialTimer); + m_labelTrialTimer.setLabel(m_trialTimer); + + bool showPressStart = false; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + bool show = m_showingPressStart[i]; + showPressStart |= show; + + if(show) + { + addTimer(0,3000); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = i; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowController , 1 , value ); + } + } + m_controlPressStartPanel.setVisible(showPressStart); +} + +void UIComponent_PressStartToPlay::handleTimerComplete(int id) +{ + m_controlPressStartPanel.setVisible(false); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_showingPressStart[i] = false; + } + ui.ClearPressStart(); +} + +void UIComponent_PressStartToPlay::showPressStart(int iPad, bool show) +{ + m_showingPressStart[iPad] = show; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_controlPressStartPanel.setVisible(show); + + if(show) + { + addTimer(0,3000); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iPad; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowController , 1 , value ); + } + } +} + +void UIComponent_PressStartToPlay::setTrialTimer(const wstring &label) +{ + m_trialTimer = label; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_labelTrialTimer.setLabel(label); + } +} + +void UIComponent_PressStartToPlay::showTrialTimer(bool show) +{ + m_showingTrialTimer = show; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_labelTrialTimer.setVisible(show); + } +} + +void UIComponent_PressStartToPlay::setAutosaveTimer(const wstring &label) +{ + m_autosaveTimer = label; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_labelTrialTimer.setLabel(label); + } +} + +void UIComponent_PressStartToPlay::showAutosaveTimer(bool show) +{ + m_showingAutosaveTimer = show; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_labelTrialTimer.setVisible(show); + } +} + +void UIComponent_PressStartToPlay::showSaveIcon(bool show) +{ + m_showingSaveIcon = show; + if(!ui.IsExpectingOrReloadingSkin() && hasMovie()) + { + m_controlSaveIcon.setVisible(show); + } + else + { + if(show) app.DebugPrintf("Tried to show save icon while texture pack reload was in progress\n"); + } +} + +void UIComponent_PressStartToPlay::showPlayerDisplayName(bool show) +{ +#ifdef _XBOX_ONE + if(show) + { + m_playerDisplayName.setLabel(ProfileManager.GetDisplayName(ProfileManager.GetPrimaryPad())); + } + m_playerDisplayName.setVisible(show); +#else + m_playerDisplayName.setVisible(false); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.h b/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.h new file mode 100644 index 0000000..a29b601 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_PressStartToPlay.h @@ -0,0 +1,60 @@ +#pragma once + +#include "UIScene.h" + +class UIComponent_PressStartToPlay : public UIScene +{ +private: + bool m_showingSaveIcon; + bool m_showingAutosaveTimer; + bool m_showingTrialTimer; + bool m_showingPressStart[XUSER_MAX_COUNT]; + wstring m_trialTimer; + wstring m_autosaveTimer; + +protected: + UIControl_Label m_labelTrialTimer, m_labelPressStart, m_playerDisplayName; + UIControl m_controlSaveIcon, m_controlPressStartPanel; + IggyName m_funcShowController; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_labelTrialTimer, "TrialTimer") + UI_MAP_ELEMENT(m_controlSaveIcon, "SaveIcon") + UI_MAP_ELEMENT(m_playerDisplayName, "PlayerName") + UI_MAP_ELEMENT(m_controlPressStartPanel, "MainPanel") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_controlPressStartPanel) + UI_MAP_ELEMENT(m_labelPressStart, "PressStartLabel" ) + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME(m_funcShowController, L"ShowController"); + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIComponent_PressStartToPlay(int iPad, void *initData, UILayer *parentLayer); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_PressStartToPlay;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + virtual void handleReload(); + virtual void handleTimerComplete(int id); + + void showPressStart(int iPad, bool show); + void setTrialTimer(const wstring &label); + void showTrialTimer(bool show); + void setAutosaveTimer(const wstring &label); + void showAutosaveTimer(bool show); + void showSaveIcon(bool show); + void showPlayerDisplayName(bool show); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp b/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp new file mode 100644 index 0000000..63eba1a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Tooltips.cpp @@ -0,0 +1,501 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_Tooltips.h" + +UIComponent_Tooltips::UIComponent_Tooltips(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + for(int i=0;igetViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + m_bSplitscreen = true; + return L"ToolTipsSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + m_bSplitscreen = false; + return L"ToolTips"; + break; + } +} + +F64 UIComponent_Tooltips::getSafeZoneHalfWidth() +{ + float width = ui.getScreenWidth(); + + float safeWidth = 0.0f; + +#ifndef __PSVITA__ + // 85% safezone for tooltips in either SD mode + if( !RenderManager.IsHiDef() ) + { + // 85% safezone + safeWidth = m_movieWidth * (0.15f / 2); + } + else + { + // 90% safezone + safeWidth = width * (0.1f / 2); + } +#endif + return safeWidth; +} + +void UIComponent_Tooltips::updateSafeZone() +{ + // Distance from edge + F64 safeTop = 0.0; + F64 safeBottom = 0.0; + F64 safeLeft = 0.0; + F64 safeRight = 0.0; + + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + safeTop = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + safeLeft = getSafeZoneHalfWidth(); + safeBottom = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + safeRight = getSafeZoneHalfWidth(); + safeBottom = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + safeTop = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + safeTop = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + safeBottom = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + safeTop = getSafeZoneHalfHeight(); + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + safeRight = getSafeZoneHalfWidth(); + break; + } + setSafeZone(safeTop, safeBottom, safeLeft, safeRight); +} + +void UIComponent_Tooltips::tick() +{ + UIScene::tick(); + + // set the opacity of the tooltip items + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + float fVal; + + if(ucAlpha<80) + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(m_iPad) && (ucAlpha<15)) + { + ucAlpha=15; + } + + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(m_iPad); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=0.01f*80.0f; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + } + else + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(m_iPad) && (ucAlpha<15)) + { + ucAlpha=15; + } + fVal=0.01f*(float)ucAlpha; + } + setOpacity(fVal); +} + +void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if((ProfileManager.GetLockedProfile()!=-1) && !ui.GetMenuDisplayed(m_iPad) && (app.GetGameSettings(m_iPad,eGameSetting_Tooltips)==0 || app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)==0)) + { + return; + } + + if(m_bSplitscreen) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + } + ui.setupRenderPosition(xPos, yPos); + + S32 tileXStart = 0; + S32 tileYStart = 0; + S32 tileWidth = width; + S32 tileHeight = height; + + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + tileHeight = (S32)(ui.getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + tileYStart = (S32)(m_movieHeight / 2); + break; + } + + IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight ); + + IggyPlayerDrawTilesStart ( getMovie() ); + + m_renderWidth = tileWidth; + m_renderHeight = tileHeight; + IggyPlayerDrawTile ( getMovie() , + tileXStart , + tileYStart , + tileXStart + tileWidth , + tileYStart + tileHeight , + 0 ); + IggyPlayerDrawTilesEnd ( getMovie() ); + } + else + { + UIScene::render(width, height, viewport); + } +} + +void UIComponent_Tooltips::SetTooltipText( unsigned int tooltip, int iTextID ) +{ + if( _SetTooltip(tooltip, iTextID) ) _Relayout(); +} + +void UIComponent_Tooltips::SetEnableTooltips( bool bVal ) +{ +} + +void UIComponent_Tooltips::ShowTooltip( unsigned int tooltip, bool show ) +{ + if(show != m_tooltipValues[tooltip].show) + { + _SetTooltip(tooltip, L"", show); + _Relayout(); + } +} + +void UIComponent_Tooltips::SetTooltips( int iA, int iB, int iX, int iY , int iLT, int iRT, int iLB, int iRB, int iLS, bool forceUpdate) +{ + bool needsRelayout = false; + needsRelayout = _SetTooltip( eToolTipButtonA, iA ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonB, iB ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonX, iX ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonY, iY ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonLT, iLT ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonRT, iRT ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonLB, iLB ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonRB, iRB ) || needsRelayout; + needsRelayout = _SetTooltip( eToolTipButtonLS, iLS ) || needsRelayout; + + if(needsRelayout)_Relayout(); +} + +void UIComponent_Tooltips::EnableTooltip( unsigned int tooltip, bool enable ) +{ +} + +bool UIComponent_Tooltips::_SetTooltip(unsigned int iToolTip, int iTextID) +{ + bool changed = false; + if(iTextID != m_tooltipValues[iToolTip].iString || (iTextID > -1 && !m_tooltipValues[iToolTip].show)) + { + m_tooltipValues[iToolTip].iString = iTextID; + changed = true; + if(iTextID > -1) _SetTooltip(iToolTip, app.GetString(iTextID), true); + else if(iTextID == -2) _SetTooltip(iToolTip, L"", true); + else _SetTooltip(iToolTip, L"", false); + } + return changed; +} + +void UIComponent_Tooltips::_SetTooltip(unsigned int iToolTipId, const wstring &label, bool show, bool force) +{ + if(!force && !show && !m_tooltipValues[iToolTipId].show) + { + return; + } + m_tooltipValues[iToolTipId].show = show; + + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iToolTipId; + + value[1].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[1].string16 = stringVal; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetTooltip , 3 , value ); + + app.DebugPrintf("Actual tooltip update!\n"); +} + +void UIComponent_Tooltips::_Relayout() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcUpdateLayout, 0 , NULL ); + +#ifdef __PSVITA__ + // rebuild touchboxes + ui.TouchBoxRebuild(this); +#endif +} + +#ifdef __PSVITA__ +void UIComponent_Tooltips::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) +{ + //app.DebugPrintf("ToolTip Touch ID = %i\n", iId); + bool handled = false; + + // perform action on release + if(bReleased) + { + switch(iId) + { + case ETouchInput_Touch_A: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_X\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_X); + break; + case ETouchInput_Touch_B: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_O\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_O); + break; + case ETouchInput_Touch_X: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_SQUARE\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_SQUARE); + break; + case ETouchInput_Touch_Y: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_TRIANGLE\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_TRIANGLE); + break; + case ETouchInput_Touch_LT: + /* not in use on vita */ + app.DebugPrintf("ToolTip no action\n", iId); + break; + case ETouchInput_Touch_RightTrigger: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_SELECT\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_SELECT); + break; + case ETouchInput_Touch_LeftBumper: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_L1\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_L1); + break; + case ETouchInput_Touch_RightBumper: + app.DebugPrintf("ToolTip Map Touch to _PSV_JOY_BUTTON_R1\n", iId); + InputManager.MapTouchInput(iPad, _PSV_JOY_BUTTON_R1); + break; + case ETouchInput_Touch_LeftStick: + app.DebugPrintf("ToolTip no action\n", iId); + /* no action */ + break; + } + } +} +#endif + +void UIComponent_Tooltips::handleReload() +{ + app.DebugPrintf("UIComponent_Tooltips::handleReload\n"); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(InputManager.IsCircleCrossSwapped()) + { + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = true; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetABSwap , 1 , value ); + } +#endif + + for(unsigned int i = 0; i < eToolTipNumButtons; ++i) + { + _SetTooltip(i,app.GetString(m_tooltipValues[i].iString), m_tooltipValues[i].show, true); + } + _Relayout(); +} + +void UIComponent_Tooltips::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_overrideSFX[iPad][key]) + { + // don't play a sound for this action + switch(key) + { + case ACTION_MENU_A: + case ACTION_MENU_OK: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_X: + case ACTION_MENU_Y: + case ACTION_MENU_B: + case ACTION_MENU_CANCEL: + case ACTION_MENU_LEFT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } + } + else + { + switch(key) + { + case ACTION_MENU_OK: + case ACTION_MENU_CANCEL: + // 4J-PB - We get both A and OK, and B and Cancel, so only play a sound on one of them. + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_A: + case ACTION_MENU_X: + case ACTION_MENU_Y: + // 4J-PB - play a Press sound + //CD - Removed, causes a sound on all presses + /*if(pressed) + { + ui.PlayUISFX(eSFX_Press); + }*/ + sendInputToMovie(key, repeat, pressed, released); + break; + + case ACTION_MENU_B: + // 4J-PB - play a Press sound + //CD - Removed, causes a sound on all presses + /*if(pressed) + { + ui.PlayUISFX(eSFX_Back); + }*/ + sendInputToMovie(key, repeat, pressed, released); + break; + + case ACTION_MENU_LEFT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: + //CD - Removed, causes a sound on all presses + /*if(pressed) + { + ui.PlayUISFX(eSFX_Scroll); + }*/ + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } + } +} + +void UIComponent_Tooltips::overrideSFX(int iPad, int key, bool bVal) +{ + m_overrideSFX[iPad][key]=bVal; +} diff --git a/Minecraft.Client/Common/UI/UIComponent_Tooltips.h b/Minecraft.Client/Common/UI/UIComponent_Tooltips.h new file mode 100644 index 0000000..4b4c826 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_Tooltips.h @@ -0,0 +1,110 @@ +#pragma once + +#include "UIScene.h" + +class UIComponent_Tooltips : public UIScene +{ +private: + bool m_bSplitscreen; + +protected: + typedef struct _TooltipValues + { + bool show; + int iString; + + _TooltipValues() + { + show = false; + iString = -1; + } + } TooltipValues; + + TooltipValues m_tooltipValues[eToolTipNumButtons]; + + IggyName m_funcSetTooltip, m_funcSetOpacity, m_funcSetABSwap, m_funcUpdateLayout; + +#ifdef __PSVITA__ + enum ETouchInput + { + ETouchInput_Touch_A, + ETouchInput_Touch_B, + ETouchInput_Touch_X, + ETouchInput_Touch_Y, + ETouchInput_Touch_LT, + ETouchInput_Touch_RightTrigger, + ETouchInput_Touch_LeftBumper, + ETouchInput_Touch_RightBumper, + ETouchInput_Touch_LeftStick, + + ETouchInput_Count, + }; + UIControl_Touch m_TouchController[ETouchInput_Count]; +#endif + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) +#ifdef __PSVITA__ + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_A], "Touch_A") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_B], "Touch_B") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_X], "Touch_X") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_Y], "Touch_Y") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_LT], "Touch_LT") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_RightTrigger], "Touch_RightTrigger") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_LeftBumper], "Touch_LeftBumper") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_RightBumper], "Touch_RightBumper") + UI_MAP_ELEMENT( m_TouchController[ETouchInput_Touch_LeftStick], "Touch_LeftStick") +#endif + UI_MAP_NAME( m_funcSetTooltip, L"SetToolTip") + UI_MAP_NAME( m_funcSetOpacity, L"SetOpacity") + UI_MAP_NAME( m_funcSetABSwap, L"SetABSwap") + UI_MAP_NAME( m_funcUpdateLayout, L"UpdateLayout") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + + virtual F64 getSafeZoneHalfWidth(); + +public: + UIComponent_Tooltips(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIComponent_Tooltips;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + virtual void updateSafeZone(); + + virtual void tick(); + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); + + virtual void SetTooltipText( unsigned int tooltip, int iTextID ); + virtual void SetEnableTooltips( bool bVal ); + virtual void ShowTooltip( unsigned int tooltip, bool show ); + virtual void SetTooltips( int iA, int iB=-1, int iX=-1, int iY=-1 , int iLT=-1, int iRT=-1, int iLB=-1, int iRB=-1, int iLS=-1, bool forceUpdate = false); + virtual void EnableTooltip( unsigned int tooltip, bool enable ); + + virtual void handleReload(); + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + void overrideSFX(int iPad, int key, bool bVal); + + +private: + bool _SetTooltip(unsigned int iToolTip, int iTextID); + void _SetTooltip(unsigned int iToolTipId, const wstring &label, bool show, bool force = false); + void _Relayout(); + + bool m_overrideSFX[XUSER_MAX_COUNT][ACTION_MAX_MENU]; + +#ifdef __PSVITA__ + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.cpp b/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.cpp new file mode 100644 index 0000000..858a8b4 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.cpp @@ -0,0 +1,539 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIComponent_TutorialPopup.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Minecraft.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" + +UIComponent_TutorialPopup::UIComponent_TutorialPopup(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_interactScene = NULL; + m_lastInteractSceneMoved = NULL; + m_lastSceneMovedLeft = false; + m_bAllowFade = false; + m_iconItem = nullptr; + m_iconIsFoil = false; + + m_bContainerMenuVisible = false; + m_bSplitscreenGamertagVisible = false; + + m_labelDescription.init(L""); +} + +wstring UIComponent_TutorialPopup::getMoviePath() +{ + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + return L"TutorialPopupSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + return L"TutorialPopup"; + break; + } +} + +void UIComponent_TutorialPopup::UpdateTutorialPopup() +{ + // has the Splitscreen Gamertag visibility been changed? Re-Adjust Layout to prevent overlaps! + if(m_bSplitscreenGamertagVisible != (bool)(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags) != 0)) + { + m_bSplitscreenGamertagVisible = (bool)(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags) != 0); + handleReload(); + } +} + +void UIComponent_TutorialPopup::handleReload() +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = (bool)((app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags)!=0) && !m_bContainerMenuVisible); // 4J - TomK - Offset for splitscreen gamertag? + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAdjustLayout, 1 , value ); +} + +void UIComponent_TutorialPopup::SetTutorialDescription(TutorialPopupInfo *info) +{ + m_interactScene = info->interactScene; + + wstring parsed = _SetIcon(info->icon, info->iAuxVal, info->isFoil, info->desc); + parsed = _SetImage( parsed ); + parsed = ParseDescription(m_iPad, parsed); + + if(parsed.empty()) + { + _SetDescription( info->interactScene, L"", L"", info->allowFade, info->isReminder ); + } + else + { + _SetDescription( info->interactScene, parsed, info->title, info->allowFade, info->isReminder ); + } +} + +void UIComponent_TutorialPopup::RemoveInteractSceneReference(UIScene *scene) +{ + if( m_interactScene == scene ) + { + m_interactScene = NULL; + } +} + +void UIComponent_TutorialPopup::SetVisible(bool visible) +{ + m_parentLayer->showComponent(0,eUIComponent_TutorialPopup,visible); + + if( visible && m_bAllowFade ) + { + //Initialise a timer to fade us out again + app.DebugPrintf("UIComponent_TutorialPopup::SetVisible: setting TUTORIAL_POPUP_FADE_TIMER_ID to %d\n",m_tutorial->GetTutorialDisplayMessageTime()); + addTimer(TUTORIAL_POPUP_FADE_TIMER_ID,m_tutorial->GetTutorialDisplayMessageTime()); + } +} + +bool UIComponent_TutorialPopup::IsVisible() +{ + return m_parentLayer->isComponentVisible(eUIComponent_TutorialPopup); +} + +void UIComponent_TutorialPopup::handleTimerComplete(int id) +{ + switch(id) + { + case TUTORIAL_POPUP_FADE_TIMER_ID: + SetVisible(false); + killTimer(id); + app.DebugPrintf("handleTimerComplete: setting TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID\n"); + addTimer(TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + break; + case TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID: + UpdateInteractScenePosition(IsVisible()); + killTimer(id); + break; + } +} + +void UIComponent_TutorialPopup::_SetDescription(UIScene *interactScene, const wstring &desc, const wstring &title, bool allowFade, bool isReminder) +{ + m_interactScene = interactScene; + app.DebugPrintf("Setting m_interactScene to %08x\n", m_interactScene); + if( interactScene != m_lastInteractSceneMoved ) m_lastInteractSceneMoved = NULL; + if(desc.empty()) + { + SetVisible( false ); + app.DebugPrintf("_SetDescription1: setting TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID\n"); + addTimer(TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + killTimer(TUTORIAL_POPUP_FADE_TIMER_ID); + } + else + { + SetVisible( true ); + app.DebugPrintf("_SetDescription2: setting TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID\n"); + addTimer(TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + + if( allowFade ) + { + //Initialise a timer to fade us out again + app.DebugPrintf("_SetDescription: setting TUTORIAL_POPUP_FADE_TIMER_ID\n"); + addTimer(TUTORIAL_POPUP_FADE_TIMER_ID,m_tutorial->GetTutorialDisplayMessageTime()); + } + else + { + app.DebugPrintf("_SetDescription: killing TUTORIAL_POPUP_FADE_TIMER_ID\n"); + killTimer(TUTORIAL_POPUP_FADE_TIMER_ID); + } + m_bAllowFade = allowFade; + + if(isReminder) + { + wstring text(app.GetString( IDS_TUTORIAL_REMINDER )); + text.append( desc ); + stripWhitespaceForHtml( text ); + // set the text colour + wchar_t formatting[40]; + // 4J Stu - Don't set HTML font size, that's set at design time in flash + //swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White),m_textFontSize); + swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White)); + text = formatting + text; + + m_labelDescription.setLabel( text, true ); + } + else + { + wstring text(desc); + stripWhitespaceForHtml( text ); + // set the text colour + wchar_t formatting[40]; + // 4J Stu - Don't set HTML font size, that's set at design time in flash + //swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White),m_textFontSize); + swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White)); + text = formatting + text; + + m_labelDescription.setLabel( text, true ); + + } + + m_labelTitle.setLabel( title, true ); + m_labelTitle.setVisible(!title.empty()); + + + // read host setting if gamertag is visible or not and pass on to Adjust Layout function (so we can offset it to stay clear of the gamertag) + m_bSplitscreenGamertagVisible = (bool)(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags)!=0); + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = (m_bSplitscreenGamertagVisible && !m_bContainerMenuVisible); // 4J - TomK - Offset for splitscreen gamertag? + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAdjustLayout, 1 , value ); + } +} + +wstring UIComponent_TutorialPopup::_SetIcon(int icon, int iAuxVal, bool isFoil, LPCWSTR desc) +{ + wstring temp(desc); + + bool isFixedIcon = false; + + m_iconIsFoil = isFoil; + if( icon != TUTORIAL_NO_ICON ) + { + m_iconIsFoil = false; + m_iconItem = shared_ptr(new ItemInstance(icon,1,iAuxVal)); + } + else + { + m_iconItem = nullptr; + wstring openTag(L"{*ICON*}"); + wstring closeTag(L"{*/ICON*}"); + int iconTagStartPos = (int)temp.find(openTag); + int iconStartPos = iconTagStartPos + (int)openTag.length(); + if( iconTagStartPos > 0 && iconStartPos < (int)temp.length() ) + { + int iconEndPos = (int)temp.find( closeTag, iconStartPos ); + + if(iconEndPos > iconStartPos && iconEndPos < (int)temp.length() ) + { + wstring id = temp.substr(iconStartPos, iconEndPos - iconStartPos); + + vector idAndAux = stringSplit(id,L':'); + + int iconId = _fromString(idAndAux[0]); + + if(idAndAux.size() > 1) + { + iAuxVal = _fromString(idAndAux[1]); + } + else + { + iAuxVal = 0; + } + m_iconItem = shared_ptr(new ItemInstance(iconId,1,iAuxVal)); + + temp.replace(iconTagStartPos, iconEndPos - iconTagStartPos + closeTag.length(), L""); + } + } + + // remove any icon text + else if(temp.find(L"{*CraftingTableIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::workBench_Id,1,0)); + } + else if(temp.find(L"{*SticksIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::stick_Id,1,0)); + } + else if(temp.find(L"{*PlanksIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::wood_Id,1,0)); + } + else if(temp.find(L"{*WoodenShovelIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::shovel_wood_Id,1,0)); + } + else if(temp.find(L"{*WoodenHatchetIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::hatchet_wood_Id,1,0)); + } + else if(temp.find(L"{*WoodenPickaxeIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::pickAxe_wood_Id,1,0)); + } + else if(temp.find(L"{*FurnaceIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::furnace_Id,1,0)); + } + else if(temp.find(L"{*WoodenDoorIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::door_wood,1,0)); + } + else if(temp.find(L"{*TorchIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::torch_Id,1,0)); + } + else if(temp.find(L"{*BoatIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::boat_Id,1,0)); + } + else if(temp.find(L"{*FishingRodIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::fishingRod_Id,1,0)); + } + else if(temp.find(L"{*FishIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::fish_raw_Id,1,0)); + } + else if(temp.find(L"{*MinecartIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Item::minecart_Id,1,0)); + } + else if(temp.find(L"{*RailIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::rail_Id,1,0)); + } + else if(temp.find(L"{*PoweredRailIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::goldenRail_Id,1,0)); + } + else if(temp.find(L"{*StructuresIcon*}")!=wstring::npos) + { + isFixedIcon = true; + setupIconHolder(e_ICON_TYPE_STRUCTURES); + } + else if(temp.find(L"{*ToolsIcon*}")!=wstring::npos) + { + isFixedIcon = true; + setupIconHolder(e_ICON_TYPE_TOOLS); + } + else if(temp.find(L"{*StoneIcon*}")!=wstring::npos) + { + m_iconItem = shared_ptr(new ItemInstance(Tile::rock_Id,1,0)); + } + else + { + m_iconItem = nullptr; + } + } + if(!isFixedIcon && m_iconItem != NULL) setupIconHolder(e_ICON_TYPE_IGGY); + m_controlIconHolder.setVisible( isFixedIcon || m_iconItem != NULL); + + return temp; +} + +wstring UIComponent_TutorialPopup::_SetImage(wstring &desc) +{ + // 4J Stu - Unused +#if 0 + BOOL imageShowAtStart = m_image.IsShown(); + + wstring openTag(L"{*IMAGE*}"); + wstring closeTag(L"{*/IMAGE*}"); + int imageTagStartPos = (int)desc.find(openTag); + int imageStartPos = imageTagStartPos + (int)openTag.length(); + if( imageTagStartPos > 0 && imageStartPos < (int)desc.length() ) + { + int imageEndPos = (int)desc.find( closeTag, imageStartPos ); + + if(imageEndPos > imageStartPos && imageEndPos < (int)desc.length() ) + { + wstring id = desc.substr(imageStartPos, imageEndPos - imageStartPos); + m_image.SetImagePath( id.c_str() ); + m_image.SetShow( TRUE ); + + desc.replace(imageTagStartPos, imageEndPos - imageTagStartPos + closeTag.length(), L""); + } + } + else + { + // hide the icon slot + m_image.SetShow( FALSE ); + } + + BOOL imageShowAtEnd = m_image.IsShown(); + if(imageShowAtStart != imageShowAtEnd) + { + float fHeight, fWidth, fIconHeight, fDescHeight, fDescWidth; + m_image.GetBounds(&fWidth,&fIconHeight); + GetBounds(&fWidth,&fHeight); + + + // 4J Stu - For some reason when we resize the scene it resets the size of the HTML control + // We don't want that to happen, so get it's size before and set it back after + m_description.GetBounds(&fDescWidth,&fDescHeight); + if(imageShowAtEnd) + { + SetBounds(fWidth, fHeight + fIconHeight); + } + else + { + SetBounds(fWidth, fHeight - fIconHeight); + } + m_description.SetBounds(fDescWidth, fDescHeight); + } +#endif + return desc; +} + + +wstring UIComponent_TutorialPopup::ParseDescription(int iPad, wstring &text) +{ + text = replaceAll(text, L"{*CraftingTableIcon*}", L""); + text = replaceAll(text, L"{*SticksIcon*}", L""); + text = replaceAll(text, L"{*PlanksIcon*}", L""); + text = replaceAll(text, L"{*WoodenShovelIcon*}", L""); + text = replaceAll(text, L"{*WoodenHatchetIcon*}", L""); + text = replaceAll(text, L"{*WoodenPickaxeIcon*}", L""); + text = replaceAll(text, L"{*FurnaceIcon*}", L""); + text = replaceAll(text, L"{*WoodenDoorIcon*}", L""); + text = replaceAll(text, L"{*TorchIcon*}", L""); + text = replaceAll(text, L"{*MinecartIcon*}", L""); + text = replaceAll(text, L"{*BoatIcon*}", L""); + text = replaceAll(text, L"{*FishingRodIcon*}", L""); + text = replaceAll(text, L"{*FishIcon*}", L""); + text = replaceAll(text, L"{*RailIcon*}", L""); + text = replaceAll(text, L"{*PoweredRailIcon*}", L""); + text = replaceAll(text, L"{*StructuresIcon*}", L""); + text = replaceAll(text, L"{*ToolsIcon*}", L""); + text = replaceAll(text, L"{*StoneIcon*}", L""); + + bool exitScreenshot = false; + size_t pos = text.find(L"{*EXIT_PICTURE*}"); + if(pos != wstring::npos) exitScreenshot = true; + text = replaceAll(text, L"{*EXIT_PICTURE*}", L""); + m_controlExitScreenshot.setVisible(exitScreenshot); + /* +#define MINECRAFT_ACTION_RENDER_DEBUG ACTION_INGAME_13 +#define MINECRAFT_ACTION_PAUSEMENU ACTION_INGAME_15 +#define MINECRAFT_ACTION_SNEAK_TOGGLE ACTION_INGAME_17 + */ + + return app.FormatHTMLString(iPad,text); +} + +void UIComponent_TutorialPopup::UpdateInteractScenePosition(bool visible) +{ + if( m_interactScene == NULL ) return; + + // 4J-PB - check this players screen section to see if we should allow the animation + bool bAllowAnim=false; + bool isCraftingScene = (m_interactScene->getSceneType() == eUIScene_Crafting2x2Menu) || (m_interactScene->getSceneType() == eUIScene_Crafting3x3Menu); + bool isCreativeScene = (m_interactScene->getSceneType() == eUIScene_CreativeMenu); + switch(Minecraft::GetInstance()->localplayers[m_iPad]->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + bAllowAnim=true; + break; + default: + // anim allowed for everything except the crafting 2x2 and 3x3, and the creative menu + if(!isCraftingScene && !isCreativeScene) + { + bAllowAnim=true; + } + break; + } + + if(bAllowAnim) + { + bool movingLeft = visible; + + if( (m_lastInteractSceneMoved != m_interactScene && movingLeft) || ( m_lastInteractSceneMoved == m_interactScene && m_lastSceneMovedLeft != movingLeft ) ) + { + if(movingLeft) + { + m_interactScene->slideLeft(); + } + else + { + m_interactScene->slideRight(); + } + + m_lastInteractSceneMoved = m_interactScene; + m_lastSceneMovedLeft = movingLeft; + } + } + +} + +void UIComponent_TutorialPopup::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(viewport != C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + } + //Adjust for safezone + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + yPos += getSafeZoneHalfHeight(); + break; + } + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos -= getSafeZoneHalfWidth(); + break; + } + ui.setupRenderPosition(xPos, yPos); + + IggyPlayerSetDisplaySize( getMovie(), width, height ); + IggyPlayerDraw( getMovie() ); + } + else + { + UIScene::render(width, height, viewport); + } +} + +void UIComponent_TutorialPopup::customDraw(IggyCustomDrawCallbackRegion *region) +{ + if(m_iconItem != NULL) customDrawSlotControl(region,m_iPad,m_iconItem,1.0f,m_iconItem->isFoil() || m_iconIsFoil,false); +} + +void UIComponent_TutorialPopup::setupIconHolder(EIcons icon) +{ + app.DebugPrintf("Setting icon holder to %d\n", icon); + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = (F64)icon; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetupIconHolder , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.h b/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.h new file mode 100644 index 0000000..36f7830 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIComponent_TutorialPopup.h @@ -0,0 +1,101 @@ +#pragma once + +#include "UIScene.h" + +#define TUTORIAL_POPUP_FADE_TIMER_ID 0 +#define TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID 1 +#define TUTORIAL_POPUP_MOVE_SCENE_TIME 500 + + +class UIComponent_TutorialPopup : public UIScene +{ +private: + // A scene that may be displayed behind the popup that the player is using, that will need shifted so we can see it clearly. + UIScene *m_interactScene, *m_lastInteractSceneMoved; + bool m_lastSceneMovedLeft; + bool m_bAllowFade; + Tutorial *m_tutorial; + shared_ptr m_iconItem; + bool m_iconIsFoil; + //int m_iLocalPlayerC; + + bool m_bContainerMenuVisible; + bool m_bSplitscreenGamertagVisible; + + // Maps to values in AS + enum EIcons + { + e_ICON_TYPE_IGGY = 0, + e_ICON_TYPE_ARMOUR = 1, + e_ICON_TYPE_BREWING = 2, + e_ICON_TYPE_DECORATION = 3, + e_ICON_TYPE_FOOD = 4, + e_ICON_TYPE_MATERIALS = 5, + e_ICON_TYPE_MECHANISMS = 6, + e_ICON_TYPE_MISC = 7, + e_ICON_TYPE_REDSTONE_AND_TRANSPORT = 8, + e_ICON_TYPE_STRUCTURES = 9, + e_ICON_TYPE_TOOLS = 10, + e_ICON_TYPE_TRANSPORT = 11, + }; + +public: + UIComponent_TutorialPopup(int iPad, void *initData, UILayer *parentLayer); + +protected: + UIControl_Label m_labelDescription, m_labelTitle; + UIControl m_controlIconHolder; + UIControl m_controlExitScreenshot; + IggyName m_funcAdjustLayout, m_funcSetupIconHolder; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_MAP_ELEMENT( m_labelDescription, "Description") + UI_MAP_ELEMENT( m_controlIconHolder, "IconHolder") + UI_MAP_ELEMENT( m_controlExitScreenshot, "ExitScreenShot") + + UI_MAP_NAME( m_funcAdjustLayout, L"AdjustLayout") + UI_MAP_NAME( m_funcSetupIconHolder, L"SetupIconHolder") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIComponent_TutorialPopup;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + virtual void handleReload(); + + void SetContainerMenuVisible(bool bContainerMenuVisible) { m_bContainerMenuVisible = bContainerMenuVisible; } + void UpdateTutorialPopup(); + + void SetTutorial( Tutorial *tutorial ) { m_tutorial = tutorial; } + void SetTutorialDescription(TutorialPopupInfo *info); + void RemoveInteractSceneReference(UIScene *scene); + void SetVisible(bool visible); + bool IsVisible(); + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + +protected: + void handleTimerComplete(int id); + +private: + void _SetDescription(UIScene *interactScene, const wstring &desc, const wstring &title, bool allowFade, bool isReminder); + wstring _SetIcon(int icon, int iAuxVal, bool isFoil, LPCWSTR desc); + wstring _SetImage(wstring &desc); + wstring ParseDescription(int iPad, wstring &text); + void UpdateInteractScenePosition(bool visible); + + void setupIconHolder(EIcons icon); +}; diff --git a/Minecraft.Client/Common/UI/UIControl.cpp b/Minecraft.Client/Common/UI/UIControl.cpp new file mode 100644 index 0000000..ec2e13d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\JavaMath.h" + +UIControl::UIControl() +{ + m_parentScene = NULL; + m_lastOpacity = 1.0f; + m_controlName = ""; + m_isVisible = true; + m_bHidden = false; + m_eControlType = eNoControl; +} + +bool UIControl::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + m_parentScene = scene; + m_controlName = controlName; + + rrbool res = IggyValuePathMakeNameRef ( &m_iggyPath , parent , controlName.c_str() ); + + m_nameXPos = registerFastName(L"x"); + m_nameYPos = registerFastName(L"y"); + m_nameWidth = registerFastName(L"width"); + m_nameHeight = registerFastName(L"height"); + m_funcSetAlpha = registerFastName(L"SetControlAlpha"); + m_nameVisible = registerFastName(L"visible"); + + F64 fx, fy, fwidth, fheight; + IggyValueGetF64RS( getIggyValuePath() , m_nameXPos , NULL , &fx ); + IggyValueGetF64RS( getIggyValuePath() , m_nameYPos , NULL , &fy ); + IggyValueGetF64RS( getIggyValuePath() , m_nameWidth , NULL , &fwidth ); + IggyValueGetF64RS( getIggyValuePath() , m_nameHeight , NULL , &fheight ); + + m_x = (S32)fx; + m_y = (S32)fy; + m_width = (S32)Math::round(fwidth); + m_height = (S32)Math::round(fheight); + + return res; +} + +#ifdef __PSVITA__ +void UIControl::UpdateControl() +{ + F64 fx, fy, fwidth, fheight; + IggyValueGetF64RS( getIggyValuePath() , m_nameXPos , NULL , &fx ); + IggyValueGetF64RS( getIggyValuePath() , m_nameYPos , NULL , &fy ); + IggyValueGetF64RS( getIggyValuePath() , m_nameWidth , NULL , &fwidth ); + IggyValueGetF64RS( getIggyValuePath() , m_nameHeight , NULL , &fheight ); + m_x = (S32)fx; + m_y = (S32)fy; + m_width = (S32)Math::round(fwidth); + m_height = (S32)Math::round(fheight); +} +#endif // __PSVITA__ + +void UIControl::ReInit() +{ + if(m_lastOpacity != 1.0f) + { + IggyDataValue result; + IggyDataValue value[2]; + IggyStringUTF8 stringVal; + + stringVal.string = (char *)m_controlName.c_str(); + stringVal.length = m_controlName.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_lastOpacity; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, m_parentScene->m_rootPath , m_funcSetAlpha , 2 , value ); + } + + IggyValueSetBooleanRS( getIggyValuePath(), m_nameVisible, NULL, m_isVisible ); +} + +IggyValuePath *UIControl::getIggyValuePath() +{ + return &m_iggyPath; +} + +S32 UIControl::getXPos() +{ + return m_x; +} + +S32 UIControl::getYPos() +{ + return m_y; +} + +S32 UIControl::getWidth() +{ + return m_width; +} + +S32 UIControl::getHeight() +{ + return m_height; +} + +void UIControl::setOpacity(float percent) +{ + if(percent != m_lastOpacity) + { + m_lastOpacity = percent; + + IggyDataValue result; + IggyDataValue value[2]; + IggyStringUTF8 stringVal; + + stringVal.string = (char *)m_controlName.c_str(); + stringVal.length = m_controlName.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_lastOpacity; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, m_parentScene->m_rootPath , m_funcSetAlpha , 2 , value ); + } +} + +void UIControl::setVisible(bool visible) +{ + if(visible != m_isVisible) + { + rrbool succ = IggyValueSetBooleanRS( getIggyValuePath(), m_nameVisible, NULL, visible ); + if(succ) m_isVisible = visible; + else app.DebugPrintf("Failed to set visibility for control\n"); + } +} + +bool UIControl::getVisible() +{ + rrbool bVisible = false; + + IggyResult result = IggyValueGetBooleanRS ( getIggyValuePath() , m_nameVisible, NULL, &bVisible ); + + m_isVisible = bVisible; + + return bVisible; +} + +IggyName UIControl::registerFastName(const wstring &name) +{ + return m_parentScene->registerFastName(name); +} diff --git a/Minecraft.Client/Common/UI/UIControl.h b/Minecraft.Client/Common/UI/UIControl.h new file mode 100644 index 0000000..3b4ef05 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl.h @@ -0,0 +1,92 @@ +#pragma once + +// This class for any name object in the flash scene +class UIControl +{ + +public: + enum eUIControlType + { + eNoControl, + eButton, + eButtonList, + eCheckBox, + eCursor, + eDLCList, + eDynamicLabel, + eEnchantmentBook, + eEnchantmentButton, + eHTMLLabel, + eLabel, + eLeaderboardList, + eMinecraftPlayer, + ePlayerList, + ePlayerSkinPreview, + eProgress, + eSaveList, + eSlider, + eSlotList, + eTextInput, + eTexturePackList, + eBitmapIcon, + eTouchControl, + }; +protected: + eUIControlType m_eControlType; + int m_id; + bool m_bHidden; // set by the Remove call + +public: + + void setControlType(eUIControlType eType) {m_eControlType=eType;} + eUIControlType getControlType() {return m_eControlType;} + void setId(int iID) { m_id=iID; } + int getId() { return m_id; } + UIScene * getParentScene() {return m_parentScene;} + +protected: + IggyValuePath m_iggyPath; + UIScene *m_parentScene; + string m_controlName; + + IggyName m_nameXPos, m_nameYPos, m_nameWidth, m_nameHeight; + IggyName m_funcSetAlpha, m_nameVisible; + + S32 m_x,m_y,m_width,m_height; + float m_lastOpacity; + bool m_isVisible; + +public: + UIControl(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); +#ifdef __PSVITA__ + void UpdateControl(); + void setHidden(bool bHidden) {m_bHidden=bHidden;} + bool getHidden(void) {return m_bHidden;} +#endif + + IggyValuePath *getIggyValuePath(); + + string getControlName() { return m_controlName; } + + virtual void tick() {} + virtual void ReInit(); + + virtual void setFocus(bool focus) {} + + S32 getXPos(); + S32 getYPos(); + S32 getWidth(); + S32 getHeight(); + + void setOpacity(float percent); + void setVisible(bool visible); + bool getVisible(); + bool isVisible() { return m_isVisible; } + + virtual bool hasFocus() { return false; } + +protected: + IggyName registerFastName(const wstring &name); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_Base.cpp b/Minecraft.Client/Common/UI/UIControl_Base.cpp new file mode 100644 index 0000000..18af2f6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Base.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\JavaMath.h" + +UIControl_Base::UIControl_Base() +{ + m_bLabelChanged = false; + m_label = L""; + m_id = 0; +} + +bool UIControl_Base::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + bool success = UIControl::setupControl(scene,parent,controlName); + + m_setLabelFunc = registerFastName(L"SetLabel"); + m_initFunc = registerFastName(L"Init"); + m_funcGetLabel = registerFastName(L"GetLabel"); + m_funcCheckLabelWidths = registerFastName(L"CheckLabelWidths"); + + return success; +} + +void UIControl_Base::tick() +{ + UIControl::tick(); + + if(m_bLabelChanged) + { + //app.DebugPrintf("Calling SetLabel - '%ls'\n", m_label.c_str()); + m_bLabelChanged = false; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)m_label.c_str(); + stringVal.length = m_label.length(); + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setLabelFunc , 1 , value ); + } +} + +void UIControl_Base::setLabel(const wstring &label, bool instant, bool force) +{ + if( force || ((!m_label.empty() || !label.empty()) && m_label.compare(label) != 0) ) m_bLabelChanged = true; + m_label = label; + + if(m_bLabelChanged && instant) + { + m_bLabelChanged = false; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)m_label.c_str(); + stringVal.length = m_label.length(); + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setLabelFunc , 1 , value ); + } +} + +void UIControl_Base::setLabel(const string &label) +{ + wstring wlabel = convStringToWstring(label); + setLabel(wlabel); +} + +const wchar_t* UIControl_Base::getLabel() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetLabel , 0 , NULL ); + + if(result.type == IGGY_DATATYPE_string_UTF16) + { + m_label = wstring( (wchar_t *)result.string16.string, result.string16.length); + } + + return m_label.c_str(); +} + +void UIControl_Base::setAllPossibleLabels(int labelCount, wchar_t labels[][256]) +{ + IggyDataValue result; + IggyDataValue *value = new IggyDataValue[labelCount]; + IggyStringUTF16 * stringVal = new IggyStringUTF16[labelCount]; + + for(unsigned int i = 0; i < labelCount; ++i) + { + stringVal[i].string = (IggyUTF16 *)labels[i]; + stringVal[i].length = wcslen(labels[i]); + value[i].type = IGGY_DATATYPE_string_UTF16; + value[i].string16 = stringVal[i]; + } + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcCheckLabelWidths , labelCount , value ); + + delete [] value; + delete [] stringVal; +} + +bool UIControl_Base::hasFocus() +{ + return m_parentScene->controlHasFocus( this ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_Base.h b/Minecraft.Client/Common/UI/UIControl_Base.h new file mode 100644 index 0000000..e70997c --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Base.h @@ -0,0 +1,30 @@ +#pragma once + +#include "UIControl.h" + +// This class maps to the FJ_Base class in actionscript +class UIControl_Base : public UIControl +{ +protected: + IggyName m_initFunc; + IggyName m_setLabelFunc; + IggyName m_funcGetLabel; + IggyName m_funcCheckLabelWidths; + + bool m_bLabelChanged; + wstring m_label; +public: + UIControl_Base(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + virtual void tick(); + + virtual void setLabel(const wstring &label, bool instant = false, bool force = false); + virtual void setLabel(const string &label); + const wchar_t* getLabel(); + virtual void setAllPossibleLabels(int labelCount, wchar_t labels[][256]); + int getId() { return m_id; } + + virtual bool hasFocus(); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_BitmapIcon.cpp b/Minecraft.Client/Common/UI/UIControl_BitmapIcon.cpp new file mode 100644 index 0000000..4956190 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_BitmapIcon.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_BitmapIcon.h" + +bool UIControl_BitmapIcon::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eBitmapIcon); + bool success = UIControl::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_funcSetTextureName = registerFastName(L"SetTextureName"); + + return success; +} + +void UIControl_BitmapIcon::setTextureName(const wstring &iconName) +{ + IggyDataValue result; + IggyDataValue value[1]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)iconName.c_str(); + stringVal.length = iconName.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetTextureName , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_BitmapIcon.h b/Minecraft.Client/Common/UI/UIControl_BitmapIcon.h new file mode 100644 index 0000000..9b2fe0a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_BitmapIcon.h @@ -0,0 +1,14 @@ +#pragma once + +#include "UIControl.h" + +class UIControl_BitmapIcon : public UIControl +{ +private: + IggyName m_funcSetTextureName; + +public: + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void setTextureName(const wstring &iconName); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Button.cpp b/Minecraft.Client/Common/UI/UIControl_Button.cpp new file mode 100644 index 0000000..96ddb8e --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Button.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Button.h" + +UIControl_Button::UIControl_Button() +{ +} + +bool UIControl_Button::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eButton); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Button specific initialisers + m_funcEnableButton = registerFastName(L"EnableButton"); + + return success; +} + +void UIControl_Button::init(const wstring &label, int id) +{ + m_label = label; + m_id = id; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 2 , value ); + +#ifdef __PSVITA__ + // 4J-PB - add this button to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Error: + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } +#endif +} + +void UIControl_Button::ReInit() +{ + UIControl_Base::ReInit(); + + init(m_label, m_id); +} + +void UIControl_Button::setEnable(bool enable) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = enable; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcEnableButton , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_Button.h b/Minecraft.Client/Common/UI/UIControl_Button.h new file mode 100644 index 0000000..367a48d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Button.h @@ -0,0 +1,19 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Button : public UIControl_Base +{ +private: + IggyName m_funcEnableButton; + +public: + UIControl_Button(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id); + virtual void ReInit(); + + void setEnable(bool enable); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp b/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp new file mode 100644 index 0000000..32db484 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_ButtonList.cpp @@ -0,0 +1,197 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_ButtonList.h" + +UIControl_ButtonList::UIControl_ButtonList() +{ + m_itemCount = 0; + m_iCurrentSelection = 0; +} + +bool UIControl_ButtonList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eButtonList); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_addNewItemFunc = registerFastName(L"addNewItem"); + m_removeAllItemsFunc = registerFastName(L"removeAllItems"); + m_funcHighlightItem = registerFastName(L"HighlightItem"); + m_funcRemoveItem = registerFastName(L"RemoveItem"); + m_funcSetButtonLabel = registerFastName(L"SetButtonLabel"); + m_funcSetTouchFocus = registerFastName(L"SetTouchFocus"); + m_funcCanTouchTrigger = registerFastName(L"CanTouchTrigger"); + + return success; +} + +void UIControl_ButtonList::init(int id) +{ + m_id = id; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); + + #ifdef __PSVITA__ + // 4J-PB - add this buttonlist to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; +} + #endif +} + +void UIControl_ButtonList::ReInit() +{ + UIControl_Base::ReInit(); + init(m_id); + m_itemCount = 0; + m_iCurrentSelection = 0; +} + +void UIControl_ButtonList::clearList() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_removeAllItemsFunc , 0 , NULL ); + + m_itemCount = 0; +} + +void UIControl_ButtonList::addItem(const string &label) +{ + addItem(label, m_itemCount); +} + +void UIControl_ButtonList::addItem(const wstring &label) +{ + addItem(label, m_itemCount); +} + +void UIControl_ButtonList::addItem(const string &label, int data) +{ + IggyDataValue result; + IggyDataValue value[2]; + + IggyStringUTF8 stringVal; + stringVal.string = (char*)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = data; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 2 , value ); + + ++m_itemCount; +} + +void UIControl_ButtonList::addItem(const wstring &label, int data) +{ + IggyDataValue result; + IggyDataValue value[2]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = data; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 2 , value ); + + ++m_itemCount; +} + +void UIControl_ButtonList::removeItem(int index) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = index; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcRemoveItem , 1 , value ); + + --m_itemCount; +} + +void UIControl_ButtonList::setCurrentSelection(int iSelection) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iSelection; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcHighlightItem , 1 , value ); +} + +int UIControl_ButtonList::getCurrentSelection() +{ + return m_iCurrentSelection; +} + +void UIControl_ButtonList::updateChildFocus(int iChild) +{ + m_iCurrentSelection = iChild; +} + +void UIControl_ButtonList::setButtonLabel(int iButtonId, const wstring &label) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iButtonId; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[1].type = IGGY_DATATYPE_string_UTF16; + value[1].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcSetButtonLabel, 2 , value ); +} + +#ifdef __PSVITA__ +void UIControl_ButtonList::SetTouchFocus(S32 iX, S32 iY, bool bRepeat) +{ + IggyDataValue result; + IggyDataValue value[3]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iX; + value[1].type = IGGY_DATATYPE_number; + value[1].number = iY; + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = bRepeat; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcSetTouchFocus, 3 , value ); +} + +bool UIControl_ButtonList::CanTouchTrigger(S32 iX, S32 iY) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iX; + value[1].type = IGGY_DATATYPE_number; + value[1].number = iY; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcCanTouchTrigger, 2 , value ); + + S32 bCanTouchTrigger = false; + if(result.type == IGGY_DATATYPE_boolean) + { + bCanTouchTrigger = (bool)result.boolval; + } + return bCanTouchTrigger; +} +#endif diff --git a/Minecraft.Client/Common/UI/UIControl_ButtonList.h b/Minecraft.Client/Common/UI/UIControl_ButtonList.h new file mode 100644 index 0000000..a3c5da3 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_ButtonList.h @@ -0,0 +1,44 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_ButtonList : public UIControl_Base +{ +protected: + IggyName m_addNewItemFunc, m_removeAllItemsFunc, m_funcHighlightItem, m_funcRemoveItem, m_funcSetButtonLabel, m_funcSetTouchFocus, m_funcCanTouchTrigger; + + int m_itemCount; + int m_iCurrentSelection; + +public: + UIControl_ButtonList(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(int id); + virtual void ReInit(); + + void clearList(); + + void addItem(const wstring &label); + void addItem(const string &label); + + void addItem(const wstring &label, int data); + void addItem(const string &label, int data); + + void removeItem(int index); + + int getItemCount() { return m_itemCount; } + + void setCurrentSelection(int iSelection); + int getCurrentSelection(); + + void updateChildFocus(int iChild); + + void setButtonLabel(int iButtonId, const wstring &label); + +#ifdef __PSVITA__ + void SetTouchFocus(S32 iX, S32 iY, bool bRepeat); + bool CanTouchTrigger(S32 iX, S32 iY); +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIControl_CheckBox.cpp b/Minecraft.Client/Common/UI/UIControl_CheckBox.cpp new file mode 100644 index 0000000..d3bdf75 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_CheckBox.cpp @@ -0,0 +1,109 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_CheckBox.h" + +UIControl_CheckBox::UIControl_CheckBox() +{ +} + +bool UIControl_CheckBox::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eCheckBox); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //CheckBox specific initialisers + m_checkedProp = registerFastName(L"Checked"); + m_funcEnable = registerFastName(L"EnableCheckBox"); + m_funcSetCheckBox = registerFastName(L"SetCheckBox"); + + m_bEnabled = true; + + return success; +} + +void UIControl_CheckBox::init(const wstring &label, int id, bool checked) +{ + m_label = label; + m_id = id; + m_bChecked = checked; + + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (int)id; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = checked; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 3 , value ); + +#ifdef __PSVITA__ + // 4J-TomK - add checkbox to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; +} +#endif +} + +bool UIControl_CheckBox::IsChecked() +{ + rrbool checked = false; + IggyResult result = IggyValueGetBooleanRS ( &m_iggyPath , m_checkedProp, NULL, &checked ); + m_bChecked = checked; + return checked; +} + +bool UIControl_CheckBox::IsEnabled() +{ + return m_bEnabled; +} + +void UIControl_CheckBox::SetEnable(bool enable) +{ + m_bEnabled = enable; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = enable; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcEnable , 1 , value ); +} + +// 4J HEG - this is only ever used when required, most of this should happen in the flash +void UIControl_CheckBox::setChecked(bool checked) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = checked; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCheckBox , 1 , value ); +} + +// 4J-TomK we need to trigger this one via function instead of key down event because of how it works +void UIControl_CheckBox::TouchSetCheckbox(bool checked) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = checked; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCheckBox , 1 , value ); +} + +void UIControl_CheckBox::ReInit() +{ + UIControl_Base::ReInit(); + + init(m_label, m_id, m_bChecked); +} diff --git a/Minecraft.Client/Common/UI/UIControl_CheckBox.h b/Minecraft.Client/Common/UI/UIControl_CheckBox.h new file mode 100644 index 0000000..924e9c6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_CheckBox.h @@ -0,0 +1,27 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_CheckBox : public UIControl_Base +{ +private: + IggyName m_checkedProp, m_funcEnable, m_funcSetCheckBox; + + bool m_bChecked, m_bEnabled; + +public: + UIControl_CheckBox(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id, bool checked); + + bool IsChecked(); + bool IsEnabled(); + void SetEnable(bool enable); + void setChecked(bool checked); + void TouchSetCheckbox(bool checked); + + virtual void ReInit(); + +}; diff --git a/Minecraft.Client/Common/UI/UIControl_Cursor.cpp b/Minecraft.Client/Common/UI/UIControl_Cursor.cpp new file mode 100644 index 0000000..2ac5ce8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Cursor.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Cursor.h" + +UIControl_Cursor::UIControl_Cursor() +{ +} + +bool UIControl_Cursor::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eCursor); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Label specific initialisers + + return success; +} diff --git a/Minecraft.Client/Common/UI/UIControl_Cursor.h b/Minecraft.Client/Common/UI/UIControl_Cursor.h new file mode 100644 index 0000000..cc7705d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Cursor.h @@ -0,0 +1,11 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Cursor : public UIControl_Base +{ +public: + UIControl_Cursor(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_DLCList.cpp b/Minecraft.Client/Common/UI/UIControl_DLCList.cpp new file mode 100644 index 0000000..35e6b08 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_DLCList.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_DLCList.h" + +bool UIControl_DLCList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eDLCList); + bool success = UIControl_ButtonList::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_funcShowTick = registerFastName(L"ShowTick"); + + return success; +} + +void UIControl_DLCList::addItem(const string &label, bool showTick, int iId) +{ + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF8 stringVal; + stringVal.string = (char*)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iId; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = showTick; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 3 , value ); + + ++m_itemCount; +} + +void UIControl_DLCList::addItem(const wstring &label, bool showTick, int iId) +{ + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16 *)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iId; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = showTick; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 3 , value ); + + ++m_itemCount; +} + +void UIControl_DLCList::showTick(int iId, bool showTick) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iId; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = showTick; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcShowTick , 2 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_DLCList.h b/Minecraft.Client/Common/UI/UIControl_DLCList.h new file mode 100644 index 0000000..9c917cb --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_DLCList.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UIControl_ButtonList.h" + +class UIControl_DLCList : public UIControl_ButtonList +{ +private: + IggyName m_funcShowTick; + +public: + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + using UIControl_ButtonList::addItem; + void addItem(const string &label, bool showTick, int iId); + void addItem(const wstring &label, bool showTick, int iId); + void showTick(int iId, bool showTick); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_DynamicLabel.cpp b/Minecraft.Client/Common/UI/UIControl_DynamicLabel.cpp new file mode 100644 index 0000000..fa29a13 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_DynamicLabel.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_DynamicLabel.h" + +UIControl_DynamicLabel::UIControl_DynamicLabel() +{ +} + +bool UIControl_DynamicLabel::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eDynamicLabel); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Label specific initialisers + m_funcAddText = registerFastName(L"AddText"); + m_funcTouchScroll = registerFastName(L"TouchScroll"); + m_funcGetRealWidth = registerFastName(L"GetRealWidth"); + m_funcGetRealHeight = registerFastName(L"GetRealHeight"); + + return success; +} + +void UIControl_DynamicLabel::addText(const wstring &text, bool bLastEntry) +{ + IggyDataValue result; + IggyDataValue value[2]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)text.c_str(); + stringVal.length = text.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bLastEntry; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcAddText , 2 , value ); +} + +void UIControl_DynamicLabel::ReInit() +{ + UIControl_Base::ReInit(); +} + +void UIControl_DynamicLabel::SetupTouch() +{ + #ifdef __PSVITA__ + // 4J-TomK - add this dynamic label to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } + #endif +} + +void UIControl_DynamicLabel::TouchScroll(S32 iY, bool bActive) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iY; + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bActive; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcTouchScroll, 2 , value ); +} + +S32 UIControl_DynamicLabel::GetRealWidth() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealWidth, 0 , NULL ); + + S32 iRealWidth = m_width; + if(result.type == IGGY_DATATYPE_number) + { + iRealWidth = (S32)result.number; + } + return iRealWidth; +} + +S32 UIControl_DynamicLabel::GetRealHeight() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealHeight, 0 , NULL ); + + S32 iRealHeight = m_height; + if(result.type == IGGY_DATATYPE_number) + { + iRealHeight = (S32)result.number; + } + return iRealHeight; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_DynamicLabel.h b/Minecraft.Client/Common/UI/UIControl_DynamicLabel.h new file mode 100644 index 0000000..902c706 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_DynamicLabel.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_DynamicLabel : public UIControl_Label +{ +private: + IggyName m_funcAddText, m_funcTouchScroll, m_funcGetRealWidth, m_funcGetRealHeight; + +public: + UIControl_DynamicLabel(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + virtual void addText(const wstring &text, bool bLastEntry); + + virtual void ReInit(); + + virtual void SetupTouch(); + + virtual void TouchScroll(S32 iY, bool bActive); + + S32 GetRealWidth(); + S32 GetRealHeight(); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.cpp b/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.cpp new file mode 100644 index 0000000..ef7c0e9 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_EnchantmentBook.h" +#include "..\..\Minecraft.h" +#include "..\..\TileEntityRenderDispatcher.h" +#include "..\..\EnchantTableRenderer.h" +#include "..\..\Lighting.h" +#include "..\..\BookModel.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +UIControl_EnchantmentBook::UIControl_EnchantmentBook() +{ + UIControl::setControlType(UIControl::eEnchantmentBook); + model = NULL; + last = nullptr; + + time = 0; + flip = oFlip = flipT = flipA = 0.0f; + open = oOpen = 0.0f; +} + +void UIControl_EnchantmentBook::render(IggyCustomDrawCallbackRegion *region) +{ + glPushMatrix(); + float width = region->x1 - region->x0; + float height = region->y1 - region->y0; + + // Revert the scale from the setup + float ssX = width/m_width; + float ssY = height/m_height; + glScalef(ssX, ssY,1.0f); + + glTranslatef(m_width/2, m_height/2, 50.0f); + + // Add a uniform scale + glScalef(1/ssX, 1/ssX, 1.0f); + + glScalef(50.0f,50.0f,1.0f); + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + //float sss = 4; + + //glTranslatef(0, 3.3f, -16); + //glScalef(sss, sss, sss); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + int tex = pMinecraft->textures->loadTexture(TN_ITEM_BOOK); // 4J was L"/1_2_2/item/book.png" + pMinecraft->textures->bind(tex); + + glRotatef(20, 1, 0, 0); + + float a = 1; + float o = oOpen + (open - oOpen) * a; + glTranslatef((1 - o) * 0.2f, (1 - o) * 0.1f, (1 - o) * 0.25f); + glRotatef(-(1 - o) * 90 - 90, 0, 1, 0); + glRotatef(180, 1, 0, 0); + + float ff1 = oFlip + (flip - oFlip) * a + 0.25f; + float ff2 = oFlip + (flip - oFlip) * a + 0.75f; + ff1 = (ff1 - floor(ff1)) * 1.6f - 0.3f; + ff2 = (ff2 - floor(ff2)) * 1.6f - 0.3f; + + if (ff1 < 0) ff1 = 0; + if (ff2 < 0) ff2 = 0; + if (ff1 > 1) ff1 = 1; + if (ff2 > 1) ff2 = 1; + + glEnable(GL_CULL_FACE); + + if(model == NULL) + { + // Share the model the the EnchantTableRenderer + + EnchantTableRenderer *etr = (EnchantTableRenderer*)TileEntityRenderDispatcher::instance->getRenderer(eTYPE_ENCHANTMENTTABLEENTITY); + if(etr != NULL) + { + model = etr->bookModel; + } + else + { + model = new BookModel(); + } + } + + model->render(nullptr, 0, ff1, ff2, o, 0, 1 / 16.0f,true); + glDisable(GL_CULL_FACE); + + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + tickBook(); +} + +void UIControl_EnchantmentBook::tickBook() +{ + UIScene_EnchantingMenu *m_containerScene = (UIScene_EnchantingMenu *)m_parentScene; + EnchantmentMenu *menu = m_containerScene->getMenu(); + shared_ptr current = menu->getSlot(0)->getItem(); + if (!ItemInstance::matches(current, last)) + { + last = current; + + do + { + flipT += random.nextInt(4) - random.nextInt(4); + } while (flip <= flipT + 1 && flip >= flipT - 1); + } + + time++; + oFlip = flip; + oOpen = open; + + bool shouldBeOpen = false; + for (int i = 0; i < 3; i++) + { + if (menu->costs[i] != 0) + { + shouldBeOpen = true; + } + } + + if (shouldBeOpen) open += 0.2f; + else open -= 0.2f; + if (open < 0) open = 0; + if (open > 1) open = 1; + + + float diff = (flipT - flip) * 0.4f; + float max = 0.2f; + if (diff < -max) diff = -max; + if (diff > +max) diff = +max; + flipA += (diff - flipA) * 0.9f; + + flip = flip + flipA; +} diff --git a/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.h b/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.h new file mode 100644 index 0000000..cbe2cf2 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_EnchantmentBook.h @@ -0,0 +1,33 @@ +#pragma once + +#include "UIControl.h" + +class UIScene_EnchantingMenu; +class BookModel; + +class UIControl_EnchantmentBook : public UIControl +{ +private: + BookModel *model; + Random random; + + // 4J JEV: Book animation variables. + int time; + float flip, oFlip, flipT, flipA; + float open, oOpen; + + //BOOL m_bDirty; + //float m_fScale,m_fAlpha; + //int m_iPad; + shared_ptr last; + + //float m_fScreenWidth,m_fScreenHeight; + //float m_fRawWidth,m_fRawHeight; + + void tickBook(); + +public: + UIControl_EnchantmentBook(); + + void render(IggyCustomDrawCallbackRegion *region); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.cpp b/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.cpp new file mode 100644 index 0000000..e1490e0 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.cpp @@ -0,0 +1,205 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_EnchantmentButton.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIControl_EnchantmentButton::UIControl_EnchantmentButton() +{ + m_index = 0; + m_lastState = eState_Inactive; + m_lastCost = 0; + m_enchantmentString = L""; + m_bHasFocus = false; + + m_textColour = app.GetHTMLColour(eTextColor_Enchant); + m_textFocusColour = app.GetHTMLColour(eTextColor_EnchantFocus); + m_textDisabledColour = app.GetHTMLColour(eTextColor_EnchantDisabled); +} + +bool UIControl_EnchantmentButton::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eEnchantmentButton); + bool success = UIControl_Button::setupControl(scene,parent,controlName); + + //Button specific initialisers + m_funcChangeState = registerFastName(L"ChangeState"); + + return success; +} + +void UIControl_EnchantmentButton::init(int index) +{ + m_index = index; +} + +void UIControl_EnchantmentButton::tick() +{ + updateState(); + UIControl_Button::tick(); +} + +void UIControl_EnchantmentButton::render(IggyCustomDrawCallbackRegion *region) +{ + UIScene_EnchantingMenu *enchantingScene = (UIScene_EnchantingMenu *)m_parentScene; + EnchantmentMenu *menu = enchantingScene->getMenu(); + + float width = region->x1 - region->x0; + float height = region->y1 - region->y0; + float xo = width/2; + float yo = height; + //glTranslatef(xo, yo, 50.0f); + + // Revert the scale from the setup + float ssX = width/m_width; + float ssY = height/m_height; + glScalef(ssX, ssY,1.0f); + + float ss = 1.0f; + +#if TO_BE_IMPLEMENTED + if(!enchantingScene->m_bSplitscreen) +#endif + { + switch(enchantingScene->getSceneResolution()) + { + case UIScene::eSceneResolution_1080: + ss = 3.0f; + break; + default: + ss = 2.0f; + break; + } + } + + glScalef(ss, ss, ss); + + int cost = menu->costs[m_index]; + + //if(cost != m_lastCost) + //{ + // updateState(); + //} + + glColor4f(1, 1, 1, 1); + if (cost != 0) + { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + Minecraft *pMinecraft = Minecraft::GetInstance(); + wstring line = _toString(cost); + Font *font = pMinecraft->altFont; + //int col = 0x685E4A; + unsigned int col = m_textColour; + if (pMinecraft->localplayers[enchantingScene->getPad()]->experienceLevel < cost && !pMinecraft->localplayers[enchantingScene->getPad()]->abilities.instabuild) + { + col = m_textDisabledColour; + font->drawWordWrap(m_enchantmentString, 0, 0, (float)m_width/ss, col, (float)m_height/ss); + font = pMinecraft->font; + //col = (0x80ff20 & 0xfefefe) >> 1; + //font->drawShadow(line, (bwidth - font->width(line))/ss, 7, col); + } + else + { + if (m_bHasFocus) + { + //col = 0xffff80; + col = m_textFocusColour; + } + font->drawWordWrap(m_enchantmentString, 0, 0, (float)m_width/ss, col, (float)m_height/ss); + font = pMinecraft->font; + //col = 0x80ff20; + //font->drawShadow(line, (bwidth - font->width(line))/ss, 7, col); + } + glDisable(GL_ALPHA_TEST); + } + else + { + } + + //Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +} + +void UIControl_EnchantmentButton::updateState() +{ + UIScene_EnchantingMenu *enchantingScene = (UIScene_EnchantingMenu *)m_parentScene; + EnchantmentMenu *menu = enchantingScene->getMenu(); + + EState state = eState_Inactive; + + int cost = menu->costs[m_index]; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(cost > pMinecraft->localplayers[enchantingScene->getPad()]->experienceLevel && !pMinecraft->localplayers[enchantingScene->getPad()]->abilities.instabuild) + { + // Dark background + state = eState_Inactive; + } + else + { + // Light background and focus background + if(m_bHasFocus) + { + state = eState_Selected; + } + else + { + state = eState_Active; + } + } + + if(cost != m_lastCost) + { + setLabel( _toString(cost) ); + m_lastCost = cost; + m_enchantmentString = EnchantmentNames::instance.getRandomName(); + } + if(cost == 0) + { + // Dark background + state = eState_Inactive; + setLabel(L""); + } + + if(state != m_lastState) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = (int)state; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcChangeState , 1 , value ); + + if(out == IGGY_RESULT_SUCCESS) m_lastState = state; + } +} + +void UIControl_EnchantmentButton::setFocus(bool focus) +{ + m_bHasFocus = focus; + updateState(); +} + +UIControl_EnchantmentButton::EnchantmentNames UIControl_EnchantmentButton::EnchantmentNames::instance; + +UIControl_EnchantmentButton::EnchantmentNames::EnchantmentNames() +{ + wstring allWords = L"the elder scrolls klaatu berata niktu xyzzy bless curse light darkness fire air earth water hot dry cold wet ignite snuff embiggen twist shorten stretch fiddle destroy imbue galvanize enchant free limited range of towards inside sphere cube self other ball mental physical grow shrink demon elemental spirit animal creature beast humanoid undead fresh stale "; + std::wistringstream iss(allWords); + std::copy(std::istream_iterator< std::wstring, wchar_t, std::char_traits >(iss), std::istream_iterator< std::wstring, wchar_t, std::char_traits >(),std::back_inserter(words)); +} + +wstring UIControl_EnchantmentButton::EnchantmentNames::getRandomName() +{ + int wordCount = random.nextInt(2) + 3; + wstring word = L""; + for (int i = 0; i < wordCount; i++) + { + if (i > 0) word += L" "; + word += words[random.nextInt(words.size())]; + } + return word; +} diff --git a/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.h b/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.h new file mode 100644 index 0000000..9b8a3c8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_EnchantmentButton.h @@ -0,0 +1,55 @@ +#pragma once + +#include "UIControl_Button.h" + +class UIControl_EnchantmentButton : public UIControl_Button +{ +private: + // Maps to values in AS + enum EState + { + eState_Inactive = 0, + eState_Active = 1, + eState_Selected = 2, + }; + + EState m_lastState; + int m_lastCost; + int m_index; + wstring m_enchantmentString; + bool m_bHasFocus; + + IggyName m_funcChangeState; + + unsigned int m_textColour, m_textFocusColour, m_textDisabledColour; + + class EnchantmentNames + { + public: + static EnchantmentNames instance; + + private: + Random random; + vector words; + + EnchantmentNames(); + + public: + wstring getRandomName(); + }; + +public: + UIControl_EnchantmentButton(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + virtual void tick(); + + void init(int index); + + void render(IggyCustomDrawCallbackRegion *region); + + void updateState(); + + virtual void setFocus(bool focus); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_HTMLLabel.cpp b/Minecraft.Client/Common/UI/UIControl_HTMLLabel.cpp new file mode 100644 index 0000000..8b7eb9a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_HTMLLabel.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_HTMLLabel.h" + +UIControl_HTMLLabel::UIControl_HTMLLabel() +{ +} + +bool UIControl_HTMLLabel::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eHTMLLabel); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Label specific initialisers + m_funcStartAutoScroll = registerFastName(L"StartAutoScroll"); + m_funcTouchScroll = registerFastName(L"TouchScroll"); + m_funcGetRealWidth = registerFastName(L"GetRealWidth"); + m_funcGetRealHeight = registerFastName(L"GetRealHeight"); + + return success; +} + +void UIControl_HTMLLabel::startAutoScroll() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcStartAutoScroll , 0 , NULL ); +} + +void UIControl_HTMLLabel::ReInit() +{ + UIControl_Base::ReInit(); + // Don't set the label, HTML sizes will have changed. Let the scene update us. + init(L""); +} + +void UIControl_HTMLLabel::setLabel(const string &label) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF8; + IggyStringUTF8 stringVal; + + stringVal.string = (char *) label.c_str(); + stringVal.length = label.length(); + value[0].string8 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setLabelFunc , 1 , value ); +} + +void UIControl_HTMLLabel::SetupTouch() +{ + #ifdef __PSVITA__ + // 4J-TomK - add this dynamic label to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } + #endif +} + +void UIControl_HTMLLabel::TouchScroll(S32 iY, bool bActive) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iY; + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bActive; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcTouchScroll, 2 , value ); +} + +S32 UIControl_HTMLLabel::GetRealWidth() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealWidth, 0 , NULL ); + + S32 iRealWidth = m_width; + if(result.type == IGGY_DATATYPE_number) + { + iRealWidth = (S32)result.number; + } + return iRealWidth; +} + +S32 UIControl_HTMLLabel::GetRealHeight() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealHeight, 0 , NULL ); + + S32 iRealHeight = m_height; + if(result.type == IGGY_DATATYPE_number) + { + iRealHeight = (S32)result.number; + } + return iRealHeight; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_HTMLLabel.h b/Minecraft.Client/Common/UI/UIControl_HTMLLabel.h new file mode 100644 index 0000000..17e7cfb --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_HTMLLabel.h @@ -0,0 +1,27 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_HTMLLabel : public UIControl_Label +{ +private: + IggyName m_funcStartAutoScroll, m_funcTouchScroll, m_funcGetRealWidth, m_funcGetRealHeight; + +public: + UIControl_HTMLLabel(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void startAutoScroll(); + virtual void ReInit(); + + using UIControl_Base::setLabel; + void setLabel(const string &label); + + virtual void SetupTouch(); + + virtual void TouchScroll(S32 iY, bool bActive); + + S32 GetRealWidth(); + S32 GetRealHeight(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Label.cpp b/Minecraft.Client/Common/UI/UIControl_Label.cpp new file mode 100644 index 0000000..1481fea --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Label.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Label.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIControl_Label::UIControl_Label() +{ +} + +bool UIControl_Label::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eLabel); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Label specific initialisers + + return success; +} + +void UIControl_Label::init(const wstring &label) +{ + m_label = label; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); +} + +void UIControl_Label::init(const string &label) +{ + m_label = convStringToWstring(label); + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF8; + IggyStringUTF8 stringVal; + + stringVal.string = (char *)label.c_str(); + stringVal.length = label.length(); + value[0].string8 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); +} + +void UIControl_Label::ReInit() +{ + UIControl_Base::ReInit(); + init(m_label); +} diff --git a/Minecraft.Client/Common/UI/UIControl_Label.h b/Minecraft.Client/Common/UI/UIControl_Label.h new file mode 100644 index 0000000..aa0f3f1 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Label.h @@ -0,0 +1,15 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Label : public UIControl_Base +{ +public: + UIControl_Label(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label); + void init(const string &label); + virtual void ReInit(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_LeaderboardList.cpp b/Minecraft.Client/Common/UI/UIControl_LeaderboardList.cpp new file mode 100644 index 0000000..c34b5e8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_LeaderboardList.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_LeaderboardList.h" + +UIControl_LeaderboardList::UIControl_LeaderboardList() +{ +} + +bool UIControl_LeaderboardList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eLeaderboardList); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //UIControl_LeaderboardList specific initialisers + m_funcInitLeaderboard = registerFastName(L"InitLeaderboard"); + m_funcAddDataSet = registerFastName(L"AddDataSet"); + m_funcResetLeaderboard = registerFastName(L"ResetLeaderboard"); + m_funcSetupTitles = registerFastName(L"SetupTitles"); + m_funcSetColumnIcon = registerFastName(L"SetColumnIcon"); +#ifdef __PSVITA__ + m_funcSetTouchFocus = registerFastName(L"SetTouchFocus"); + m_bTouchInitialised = false; +#endif + + return success; +} + +void UIControl_LeaderboardList::init(int id) +{ + m_id = id; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); +} + +void UIControl_LeaderboardList::ReInit() +{ + UIControl_Base::ReInit(); + init(m_id); +} + +void UIControl_LeaderboardList::clearList() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcResetLeaderboard , 0 , NULL ); +} + +void UIControl_LeaderboardList::setupTitles(const wstring &rank, const wstring &gamertag) +{ + IggyDataValue result; + IggyDataValue value[2]; + + IggyStringUTF16 stringVal0; + stringVal0.string = (IggyUTF16*)rank.c_str(); + stringVal0.length = rank.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal0; + + IggyStringUTF16 stringVal1; + stringVal1.string = (IggyUTF16*)gamertag.c_str(); + stringVal1.length = gamertag.length(); + value[1].type = IGGY_DATATYPE_string_UTF16; + value[1].string16 = stringVal1; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetupTitles , 2 , value ); +} + +void UIControl_LeaderboardList::initLeaderboard(int iFirstFocus, int iTotalEntries, int iNumColumns) +{ + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iFirstFocus; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iTotalEntries; + + value[2].type = IGGY_DATATYPE_number; + value[2].number = iNumColumns; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcInitLeaderboard , 3 , value ); + +#ifdef __PSVITA__ + // 4J-PB - add this button to the vita touch box list + if(!m_bTouchInitialised) + { + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + ui.TouchBoxAdd(this,m_parentScene); + break; + } + m_bTouchInitialised = true; + } +#endif +} + +void UIControl_LeaderboardList::setColumnIcon(int iColumn, int iType) +{ + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iColumn; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (iType<=32000)?0:(iType-32000); + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetColumnIcon , 2 , value ); +} + +void UIControl_LeaderboardList::addDataSet(bool bLast, int iId, int iRank, const wstring &gamertag, bool bDisplayMessage, const wstring &col0, const wstring &col1, const wstring &col2, const wstring &col3, const wstring &col4, const wstring &col5, const wstring &col6) +{ + IggyDataValue result; + IggyDataValue value[12]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = bLast; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iId; + + value[2].type = IGGY_DATATYPE_number; + value[2].number = iRank; + + IggyStringUTF16 stringVal0; + stringVal0.string = (IggyUTF16*)gamertag.c_str(); + stringVal0.length = gamertag.length(); + value[3].type = IGGY_DATATYPE_string_UTF16; + value[3].string16 = stringVal0; + + value[4].type = IGGY_DATATYPE_boolean; + value[4].boolval = bDisplayMessage; + + IggyStringUTF16 stringVal1; + stringVal1.string = (IggyUTF16*)col0.c_str(); + stringVal1.length = col0.length(); + value[5].type = IGGY_DATATYPE_string_UTF16; + value[5].string16 = stringVal1; + + if(col1.empty()) + { + value[6].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal2; + stringVal2.string = (IggyUTF16*)col1.c_str(); + stringVal2.length = col1.length(); + value[6].type = IGGY_DATATYPE_string_UTF16; + value[6].string16 = stringVal2; + } + + if(col2.empty()) + { + value[7].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal3; + stringVal3.string = (IggyUTF16*)col2.c_str(); + stringVal3.length = col2.length(); + value[7].type = IGGY_DATATYPE_string_UTF16; + value[7].string16 = stringVal3; + } + + if(col3.empty()) + { + value[8].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal4; + stringVal4.string = (IggyUTF16*)col3.c_str(); + stringVal4.length = col3.length(); + value[8].type = IGGY_DATATYPE_string_UTF16; + value[8].string16 = stringVal4; + } + + if(col4.empty()) + { + value[9].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal5; + stringVal5.string = (IggyUTF16*)col4.c_str(); + stringVal5.length = col4.length(); + value[9].type = IGGY_DATATYPE_string_UTF16; + value[9].string16 = stringVal5; + } + + if(col5.empty()) + { + value[10].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal6; + stringVal6.string = (IggyUTF16*)col5.c_str(); + stringVal6.length = col5.length(); + value[10].type = IGGY_DATATYPE_string_UTF16; + value[10].string16 = stringVal6; + } + + if(col6.empty()) + { + value[11].type = IGGY_DATATYPE_null; + } + else + { + IggyStringUTF16 stringVal7; + stringVal7.string = (IggyUTF16*)col6.c_str(); + stringVal7.length = col6.length(); + value[11].type = IGGY_DATATYPE_string_UTF16; + value[11].string16 = stringVal7; + } + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcAddDataSet , 12 , value ); +} + +#ifdef __PSVITA__ +void UIControl_LeaderboardList::SetTouchFocus(S32 iX, S32 iY, bool bRepeat) +{ + IggyDataValue result; + IggyDataValue value[3]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iX; + value[1].type = IGGY_DATATYPE_number; + value[1].number = iY; + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = bRepeat; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcSetTouchFocus, 3 , value ); +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_LeaderboardList.h b/Minecraft.Client/Common/UI/UIControl_LeaderboardList.h new file mode 100644 index 0000000..e102ddd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_LeaderboardList.h @@ -0,0 +1,50 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_LeaderboardList : public UIControl_Base +{ +private: + IggyName m_funcInitLeaderboard, m_funcAddDataSet; + IggyName m_funcResetLeaderboard; + IggyName m_funcSetupTitles, m_funcSetColumnIcon; +#ifdef __PSVITA__ + IggyName m_funcSetTouchFocus; + bool m_bTouchInitialised; +#endif +public: + enum ELeaderboardIcons + { + e_ICON_TYPE_IGGY = 0, + e_ICON_TYPE_CLIMBED = 32001, + e_ICON_TYPE_FALLEN = 32002, + e_ICON_TYPE_WALKED = 32003, + e_ICON_TYPE_SWAM = 32004, + e_ICON_TYPE_ZOMBIE = 32005, + e_ICON_TYPE_ZOMBIEPIGMAN = 32006, + e_ICON_TYPE_GHAST = 32007, + e_ICON_TYPE_CREEPER = 32008, + e_ICON_TYPE_SKELETON = 32009, + e_ICON_TYPE_SPIDER = 32010, + e_ICON_TYPE_SPIDERJOKEY = 32011, + e_ICON_TYPE_SLIME = 32012, + e_ICON_TYPE_PORTAL = 32013, + }; + UIControl_LeaderboardList(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(int id); + virtual void ReInit(); + + void clearList(); + + void setupTitles(const wstring &rank, const wstring &gamertag); + void initLeaderboard(int iFirstFocus, int iTotalEntries, int iNumColumns); + void setColumnIcon(int iColumn, int iType); + void addDataSet(bool bLast, int iId, int iRank, const wstring &gamertag, bool bDisplayMessage, const wstring &col0, const wstring &col1, const wstring &col2, const wstring &col3, const wstring &col4, const wstring &col5, const wstring &col6); + +#ifdef __PSVITA__ + void SetTouchFocus(S32 iX, S32 iY, bool bRepeat); +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp new file mode 100644 index 0000000..3a18ed5 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\EntityRenderDispatcher.h" +#include "..\..\PlayerRenderer.h" +#include "..\..\HumanoidModel.h" +#include "..\..\Lighting.h" +#include "..\..\ModelPart.h" +#include "..\..\Options.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "UI.h" +#include "UIControl_MinecraftPlayer.h" + +UIControl_MinecraftPlayer::UIControl_MinecraftPlayer() +{ + UIControl::setControlType(UIControl::eMinecraftPlayer); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; +} + +void UIControl_MinecraftPlayer::render(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + glPushMatrix(); + + float width = region->x1 - region->x0; + float height = region->y1 - region->y0; + float xo = width/2; + float yo = height; + + glTranslatef(xo, yo - 7.0f, 50.0f); + + float ss; + + // Base scale on height of this control + // Potentially we might want separate x & y scales here + ss = width / (m_fScreenWidth / m_fScreenHeight); + + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + UIScene_InventoryMenu *containerMenu = (UIScene_InventoryMenu *)m_parentScene; + + float oybr = pMinecraft->localplayers[containerMenu->getPad()]->yBodyRot; + float oyr = pMinecraft->localplayers[containerMenu->getPad()]->yRot; + float oxr = pMinecraft->localplayers[containerMenu->getPad()]->xRot; + float oyhr = pMinecraft->localplayers[containerMenu->getPad()]->yHeadRot; + + //float xd = ( matrix._41 + ( (bwidth*matrix._11)/2) ) - m_pointerPos.x; + float xd = (m_x + m_width/2) - containerMenu->m_pointerPos.x; + + // Need to base Y on head position, not centre of mass + //float yd = ( matrix._42 + ( (bheight*matrix._22) / 2) - 40 ) - m_pointerPos.y; + float yd = (m_y + m_height/2 - 40) - containerMenu->m_pointerPos.y; + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float) atan(yd / 40.0f) * 20, 1, 0, 0); + + pMinecraft->localplayers[containerMenu->getPad()]->yBodyRot = (float) atan(xd / 40.0f) * 20; + pMinecraft->localplayers[containerMenu->getPad()]->yRot = (float) atan(xd / 40.0f) * 40; + pMinecraft->localplayers[containerMenu->getPad()]->xRot = -(float) atan(yd / 40.0f) * 20; + pMinecraft->localplayers[containerMenu->getPad()]->yHeadRot = pMinecraft->localplayers[containerMenu->getPad()]->yRot; + //pMinecraft->localplayers[m_iPad]->glow = 1; + glTranslatef(0, pMinecraft->localplayers[containerMenu->getPad()]->heightOffset, 0); + EntityRenderDispatcher::instance->playerRotY = 180; + + // 4J Stu - Turning on hideGui while we do this stops the name rendering in split-screen + bool wasHidingGui = pMinecraft->options->hideGui; + pMinecraft->options->hideGui = true; + EntityRenderDispatcher::instance->render(pMinecraft->localplayers[containerMenu->getPad()], 0, 0, 0, 0, 1, false, false); + pMinecraft->options->hideGui = wasHidingGui; + //pMinecraft->localplayers[m_iPad]->glow = 0; + + pMinecraft->localplayers[containerMenu->getPad()]->yBodyRot = oybr; + pMinecraft->localplayers[containerMenu->getPad()]->yRot = oyr; + pMinecraft->localplayers[containerMenu->getPad()]->xRot = oxr; + pMinecraft->localplayers[containerMenu->getPad()]->yHeadRot = oyhr; + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +} diff --git a/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.h b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.h new file mode 100644 index 0000000..3b032f7 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_MinecraftPlayer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "UIControl.h" + +class UIControl_MinecraftPlayer : public UIControl +{ +private: + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + +public: + UIControl_MinecraftPlayer(); + + void render(IggyCustomDrawCallbackRegion *region); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_PlayerList.cpp b/Minecraft.Client/Common/UI/UIControl_PlayerList.cpp new file mode 100644 index 0000000..41534dc --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_PlayerList.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_PlayerList.h" + +bool UIControl_PlayerList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::ePlayerList); + bool success = UIControl_ButtonList::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_funcSetPlayerIcon = registerFastName(L"SetPlayerIcon"); + m_funcSetVOIPIcon = registerFastName(L"SetVOIPIcon"); + + return success; +} + +void UIControl_PlayerList::addItem(const wstring &label, int iPlayerIcon, int iVOIPIcon) +{ + IggyDataValue result; + IggyDataValue value[4]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_itemCount; + + value[2].type = IGGY_DATATYPE_number; + value[2].number = iPlayerIcon + 1; + + value[3].type = IGGY_DATATYPE_number; + value[3].number = iVOIPIcon + 1; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 4 , value ); + + ++m_itemCount; +} + +void UIControl_PlayerList::setPlayerIcon(int iId, int iPlayerIcon) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iId; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iPlayerIcon + 1; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetPlayerIcon , 2 , value ); +} + +void UIControl_PlayerList::setVOIPIcon(int iId, int iVOIPIcon) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iId; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = iVOIPIcon + 1; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetVOIPIcon , 2 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_PlayerList.h b/Minecraft.Client/Common/UI/UIControl_PlayerList.h new file mode 100644 index 0000000..8647d95 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_PlayerList.h @@ -0,0 +1,17 @@ +#pragma once + +#include "UIControl_ButtonList.h" + +class UIControl_PlayerList : public UIControl_ButtonList +{ +private: + IggyName m_funcSetPlayerIcon, m_funcSetVOIPIcon; + +public: + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + using UIControl_ButtonList::addItem; + void addItem(const wstring &label, int iPlayerIcon, int iVOIPIcon); + void setPlayerIcon(int iId, int iPlayerIcon); + void setVOIPIcon(int iId, int iVOIPIcon); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.cpp b/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.cpp new file mode 100644 index 0000000..544b4da --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.cpp @@ -0,0 +1,514 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\EntityRenderDispatcher.h" +#include "..\..\PlayerRenderer.h" +#include "..\..\HumanoidModel.h" +#include "..\..\Lighting.h" +#include "..\..\ModelPart.h" +#include "..\..\Options.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "UIControl_PlayerSkinPreview.h" + +//#define SKIN_PREVIEW_BOB_ANIM +#define SKIN_PREVIEW_WALKING_ANIM + +UIControl_PlayerSkinPreview::UIControl_PlayerSkinPreview() +{ + UIControl::setControlType(UIControl::ePlayerSkinPreview); + m_bDirty = FALSE; + m_fScale = 1.0f; + m_fAlpha = 1.0f; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + m_customTextureUrl = L"default"; + m_backupTexture = TN_MOB_CHAR; + m_capeTextureUrl = L""; + + m_yRot = 0; + m_xRot = 0; + + m_swingTime = 0.0f; + m_bobTick = 0.0f; + m_walkAnimSpeedO = 0.0f; + m_walkAnimSpeed = 0.0f; + m_walkAnimPos = 0.0f; + + m_bAutoRotate = false; + m_bRotatingLeft = false; + + m_incXRot = false; + m_decXRot = false; + m_incYRot = false; + m_decYRot = false; + + m_currentAnimation = e_SkinPreviewAnimation_Walking; + + m_fTargetRotation = 0.0f; + m_fOriginalRotation = 0.0f; + m_framesAnimatingRotation = 0; + m_bAnimatingToFacing = false; + m_pvAdditionalModelParts=NULL; + m_uiAnimOverrideBitmask=0L; +} + +void UIControl_PlayerSkinPreview::tick() +{ + UIControl::tick(); + + if( m_bAnimatingToFacing ) + { + ++m_framesAnimatingRotation; + m_yRot = m_fOriginalRotation + m_framesAnimatingRotation * ( (m_fTargetRotation - m_fOriginalRotation) / CHANGING_SKIN_FRAMES ); + + //if(m_framesAnimatingRotation == CHANGING_SKIN_FRAMES) m_bAnimatingToFacing = false; + } + else + { + if( m_incXRot ) IncrementXRotation(); + if( m_decXRot ) DecrementXRotation(); + if( m_incYRot ) IncrementYRotation(); + if( m_decYRot ) DecrementYRotation(); + + if(m_bAutoRotate) + { + ++m_rotateTick; + + if(m_rotateTick%4==0) + { + if(m_yRot >= LOOK_LEFT_EXTENT) + { + m_bRotatingLeft = false; + } + else if(m_yRot <= LOOK_RIGHT_EXTENT) + { + m_bRotatingLeft = true; + } + + if(m_bRotatingLeft) + { + IncrementYRotation(); + } + else + { + DecrementYRotation(); + } + } + } + } +} + +void UIControl_PlayerSkinPreview::SetTexture(const wstring &url, TEXTURE_NAME backupTexture) +{ + m_customTextureUrl = url; + m_backupTexture = backupTexture; + + unsigned int uiAnimOverrideBitmask = Player::getSkinAnimOverrideBitmask( app.getSkinIdFromPath(m_customTextureUrl) ); + + if(app.GetGameSettings(eGameSetting_CustomSkinAnim)==0 ) + { + // We have a force animation for some skins (claptrap) + // 4J-PB - treat all the eAnim_Disable flags as a force anim + + if((uiAnimOverrideBitmask & HumanoidModel::m_staticBitmaskIgnorePlayerCustomAnimSetting)!=0) + { + m_uiAnimOverrideBitmask=uiAnimOverrideBitmask; + } + else + { + m_uiAnimOverrideBitmask=0; + } + } + else + { + m_uiAnimOverrideBitmask = uiAnimOverrideBitmask; + } + + m_pvAdditionalModelParts=app.GetAdditionalModelParts(app.getSkinIdFromPath(m_customTextureUrl)); +} + +void UIControl_PlayerSkinPreview::SetFacing(ESkinPreviewFacing facing, bool bAnimate /*= false*/) +{ + switch(facing) + { + case e_SkinPreviewFacing_Forward: + m_fTargetRotation = 0; + m_bRotatingLeft = true; + break; + case e_SkinPreviewFacing_Left: + m_fTargetRotation = LOOK_LEFT_EXTENT; + m_bRotatingLeft = false; + break; + case e_SkinPreviewFacing_Right: + m_fTargetRotation = LOOK_RIGHT_EXTENT; + m_bRotatingLeft = true; + break; + } + + if(!bAnimate) + { + m_yRot = m_fTargetRotation; + m_bAnimatingToFacing = false; + } + else + { + m_fOriginalRotation = m_yRot; + m_bAnimatingToFacing = true; + m_framesAnimatingRotation = 0; + } +} + +void UIControl_PlayerSkinPreview::CycleNextAnimation() +{ + m_currentAnimation = (ESkinPreviewAnimations)(m_currentAnimation + 1); + if(m_currentAnimation >= e_SkinPreviewAnimation_Count) m_currentAnimation = e_SkinPreviewAnimation_Walking; + + m_swingTime = 0.0f; +} + +void UIControl_PlayerSkinPreview::CyclePreviousAnimation() +{ + m_currentAnimation = (ESkinPreviewAnimations)(m_currentAnimation - 1); + if(m_currentAnimation < e_SkinPreviewAnimation_Walking) m_currentAnimation = (ESkinPreviewAnimations)(e_SkinPreviewAnimation_Count - 1); + + m_swingTime = 0.0f; +} + +void UIControl_PlayerSkinPreview::render(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + glPushMatrix(); + + float width = region->x1 - region->x0; + float height = region->y1 - region->y0; + float xo = width/2; + float yo = height; + + glTranslatef(xo, yo - 3.5f, 50.0f); + //glTranslatef(120.0f, 294, 0.0f); + + float ss; + + // Base scale on height of this control + // Potentially we might want separate x & y scales here + ss = width / (m_fScreenWidth / m_fScreenHeight); + + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + //glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + //glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float)m_xRot, 1, 0, 0); + + // 4J Stu - Turning on hideGui while we do this stops the name rendering in split-screen + bool wasHidingGui = pMinecraft->options->hideGui; + pMinecraft->options->hideGui = true; + + //EntityRenderDispatcher::instance->render(pMinecraft->localplayers[0], 0, 0, 0, 0, 1); + EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); + if (renderer != NULL) + { + // 4J-PB - any additional parts to turn on for this player (skin dependent) + //vector *pAdditionalModelParts=mob->GetAdditionalModelParts(); + + if(m_pvAdditionalModelParts && m_pvAdditionalModelParts->size()!=0) + { + for(AUTO_VAR(it, m_pvAdditionalModelParts->begin()); it != m_pvAdditionalModelParts->end(); ++it) + { + ModelPart *pModelPart=*it; + + pModelPart->visible=true; + } + } + + render(renderer,0,0,0,0,1); + //renderer->postRender(entity, x, y, z, rot, a); + + // hide the additional parts + if(m_pvAdditionalModelParts && m_pvAdditionalModelParts->size()!=0) + { + for(AUTO_VAR(it, m_pvAdditionalModelParts->begin()); it != m_pvAdditionalModelParts->end(); ++it) + { + ModelPart *pModelPart=*it; + + pModelPart->visible=false; + } + } + } + + pMinecraft->options->hideGui = wasHidingGui; + + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +} + +// 4J Stu - Modified version of MobRenderer::render that does not require an actual entity +void UIControl_PlayerSkinPreview::render(EntityRenderer *renderer, double x, double y, double z, float rot, float a) +{ + glPushMatrix(); + glDisable(GL_CULL_FACE); + + HumanoidModel *model = (HumanoidModel *)renderer->getModel(); + + //getAttackAnim(mob, a); + //if (armor != NULL) armor->attackTime = model->attackTime; + //model->riding = mob->isRiding(); + //if (armor != NULL) armor->riding = model->riding; + + // 4J Stu - Remember to reset these values once the rendering is done if you add another one + model->attackTime = 0; + model->sneaking = false; + model->holdingRightHand = false; + model->holdingLeftHand = false; + model->idle = false; + model->eating = false; + model->eating_swing = 0; + model->eating_t = 0; + model->young = false; + model->riding = false; + + model->m_uiAnimOverrideBitmask = m_uiAnimOverrideBitmask; + + if( !m_bAnimatingToFacing ) + { + switch( m_currentAnimation ) + { + case e_SkinPreviewAnimation_Sneaking: + model->sneaking = true; + break; + case e_SkinPreviewAnimation_Attacking: + model->holdingRightHand = true; + m_swingTime++; + if (m_swingTime >= (Player::SWING_DURATION * 3) ) + { + m_swingTime = 0; + } + model->attackTime = m_swingTime / (float) (Player::SWING_DURATION * 3); + break; + default: + break; + }; + } + + + float bodyRot = m_yRot; //(mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a); + float headRot = m_yRot; //(mob->yRotO + (mob->yRot - mob->yRotO) * a); + float headRotx = 0; //(mob->xRotO + (mob->xRot - mob->xRotO) * a); + + //setupPosition(mob, x, y, z); + // is equivalent to + glTranslatef((float) x, (float) y, (float) z); + + //float bob = getBob(mob, a); +#ifdef SKIN_PREVIEW_BOB_ANIM + float bob = (m_bobTick + a)/2; + + ++m_bobTick; + if(m_bobTick>=360*2) m_bobTick = 0; +#else + float bob = 0.0f; +#endif + + //setupRotations(mob, bob, bodyRot, a); + // is equivalent to + glRotatef(180 - bodyRot, 0, 1, 0); + + float _scale = 1 / 16.0f; + glEnable(GL_RESCALE_NORMAL); + glScalef(-1, -1, 1); + + //scale(mob, a); + // is equivalent to + float s = 15 / 16.0f; + glScalef(s, s, s); + + glTranslatef(0, -24 * _scale - 0.125f / 16.0f, 0); + +#ifdef SKIN_PREVIEW_WALKING_ANIM + m_walkAnimSpeedO = m_walkAnimSpeed; + m_walkAnimSpeed += (0.1f - m_walkAnimSpeed) * 0.4f; + m_walkAnimPos += m_walkAnimSpeed; + float ws = m_walkAnimSpeedO + (m_walkAnimSpeed - m_walkAnimSpeedO) * a; + float wp = m_walkAnimPos - m_walkAnimSpeed * (1 - a); +#else + float ws = 0; + float wp = 0; +#endif + + if (ws > 1) ws = 1; + + MemSect(31); + bindTexture(m_customTextureUrl, m_backupTexture); + MemSect(0); + glEnable(GL_ALPHA_TEST); + + //model->prepareMobModel(mob, wp, ws, a); + model->render(nullptr, wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + /*for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmor(mob, i, a)) + { + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + } + }*/ + + //additionalRendering(mob, a); + if (bindTexture(m_capeTextureUrl, L"" )) + { + glPushMatrix(); + glTranslatef(0, 0, 2 / 16.0f); + + double xd = 0;//(mob->xCloakO + (mob->xCloak - mob->xCloakO) * a) - (mob->xo + (mob->x - mob->xo) * a); + double yd = 0;//(mob->yCloakO + (mob->yCloak - mob->yCloakO) * a) - (mob->yo + (mob->y - mob->yo) * a); + double zd = 0;//(mob->zCloakO + (mob->zCloak - mob->zCloakO) * a) - (mob->zo + (mob->z - mob->zo) * a); + + float yr = 1;//mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a; + + double xa = sin(yr * PI / 180); + double za = -cos(yr * PI / 180); + + float flap = (float) yd * 10; + if (flap < -6) flap = -6; + if (flap > 32) flap = 32; + float lean = (float) (xd * xa + zd * za) * 100; + float lean2 = (float) (xd * za - zd * xa) * 100; + if (lean < 0) lean = 0; + + //float pow = 1;//mob->oBob + (bob - mob->oBob) * a; + + flap += 1;//sin((mob->walkDistO + (mob->walkDist - mob->walkDistO) * a) * 6) * 32 * pow; + if (model->sneaking) + { + flap += 25; + } + + glRotatef(6.0f + lean / 2 + flap, 1, 0, 0); + glRotatef(lean2 / 2, 0, 0, 1); + glRotatef(-lean2 / 2, 0, 1, 0); + glRotatef(180, 0, 1, 0); + model->renderCloak(1 / 16.0f,true); + glPopMatrix(); + } + /* + float br = mob->getBrightness(a); + int overlayColor = getOverlayColor(mob, br, a); + + if (((overlayColor >> 24) & 0xff) > 0 || mob->hurtTime > 0 || mob->deathTime > 0) + { + glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_EQUAL); + + // 4J - changed these renders to not use the compiled version of their models, because otherwise the render states set + // about (in particular the depth & alpha test) don't work with our command buffer versions + if (mob->hurtTime > 0 || mob->deathTime > 0) + { + glColor4f(br, 0, 0, 0.4f); + model->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a)) + { + glColor4f(br, 0, 0, 0.4f); + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + if (((overlayColor >> 24) & 0xff) > 0) + { + float r = ((overlayColor >> 16) & 0xff) / 255.0f; + float g = ((overlayColor >> 8) & 0xff) / 255.0f; + float b = ((overlayColor) & 0xff) / 255.0f; + float aa = ((overlayColor >> 24) & 0xff) / 255.0f; + glColor4f(r, g, b, aa); + model->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a)) + { + glColor4f(r, g, b, aa); + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + glDepthFunc(GL_LEQUAL); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_TEXTURE_2D); + } + */ + glDisable(GL_RESCALE_NORMAL); + + glEnable(GL_CULL_FACE); + + glPopMatrix(); + + MemSect(31); + //renderName(mob, x, y, z); + MemSect(0); + + // Reset the model values to stop the changes we made here affecting anything in game (like the player hand render) + model->attackTime = 0; + model->sneaking = false; + model->holdingRightHand = false; + model->holdingLeftHand = false; +} + +bool UIControl_PlayerSkinPreview::bindTexture(const wstring& urlTexture, int backupTexture) +{ + Textures *t = Minecraft::GetInstance()->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + t->bind(id); + return true; + } + else + { + return false; + } +} + +bool UIControl_PlayerSkinPreview::bindTexture(const wstring& urlTexture, const wstring& backupTexture) +{ + Textures *t = Minecraft::GetInstance()->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + t->bind(id); + return true; + } + else + { + return false; + } +} diff --git a/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.h b/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.h new file mode 100644 index 0000000..a7c3126 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_PlayerSkinPreview.h @@ -0,0 +1,90 @@ +#pragma once + +#include "UIControl.h" +#include "..\..\Textures.h" + +class ModelPart; +class EntityRenderer; + +class UIControl_PlayerSkinPreview : public UIControl +{ +private: + static const int LOOK_LEFT_EXTENT = 45; + static const int LOOK_RIGHT_EXTENT = -45; + + static const int CHANGING_SKIN_FRAMES = 15; + + enum ESkinPreviewAnimations + { + e_SkinPreviewAnimation_Walking, + e_SkinPreviewAnimation_Sneaking, + e_SkinPreviewAnimation_Attacking, + + e_SkinPreviewAnimation_Count, + }; + + BOOL m_bDirty; + float m_fScale,m_fAlpha; + + wstring m_customTextureUrl; + TEXTURE_NAME m_backupTexture; + wstring m_capeTextureUrl; + unsigned int m_uiAnimOverrideBitmask; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + + int m_yRot,m_xRot; + + float m_bobTick; + + float m_walkAnimSpeedO; + float m_walkAnimSpeed; + float m_walkAnimPos; + + bool m_bAutoRotate, m_bRotatingLeft; + BYTE m_rotateTick; + float m_fTargetRotation, m_fOriginalRotation; + int m_framesAnimatingRotation; + bool m_bAnimatingToFacing; + + float m_swingTime; + + ESkinPreviewAnimations m_currentAnimation; + //vector *m_pvAdditionalBoxes; + vector *m_pvAdditionalModelParts; +public: + enum ESkinPreviewFacing + { + e_SkinPreviewFacing_Forward, + e_SkinPreviewFacing_Left, + e_SkinPreviewFacing_Right, + }; + + UIControl_PlayerSkinPreview(); + + virtual void tick(); + + void render(IggyCustomDrawCallbackRegion *region); + + void SetTexture(const wstring &url, TEXTURE_NAME backupTexture = TN_MOB_CHAR); + void SetCapeTexture(const wstring &url) { m_capeTextureUrl = url; } + void ResetRotation() { m_xRot = 0; m_yRot = 0; } + void IncrementYRotation() { m_yRot = (m_yRot+4); if(m_yRot >= 180) m_yRot = -180; } + void DecrementYRotation() { m_yRot = (m_yRot-4); if(m_yRot <= -180) m_yRot = 180; } + void IncrementXRotation() { m_xRot = (m_xRot+2); if(m_xRot > 22) m_xRot = 22; } + void DecrementXRotation() { m_xRot = (m_xRot-2); if(m_xRot < -22) m_xRot = -22; } + void SetAutoRotate(bool autoRotate) { m_bAutoRotate = autoRotate; } + void SetFacing(ESkinPreviewFacing facing, bool bAnimate = false); + + void CycleNextAnimation(); + void CyclePreviousAnimation(); + + bool m_incXRot, m_decXRot; + bool m_incYRot, m_decYRot; + +private: + void render(EntityRenderer *renderer, double x, double y, double z, float rot, float a); + bool bindTexture(const wstring& urlTexture, int backupTexture); + bool bindTexture(const wstring& urlTexture, const wstring& backupTexture); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Progress.cpp b/Minecraft.Client/Common/UI/UIControl_Progress.cpp new file mode 100644 index 0000000..e2ab817 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Progress.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Progress.h" + +UIControl_Progress::UIControl_Progress() +{ + m_min = 0; + m_max = 100; + m_current = 0; + m_lastPercent = 0.0f; + m_showingBar = true; +} + +bool UIControl_Progress::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eProgress); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Progress specific initialisers + m_setProgressFunc = registerFastName(L"setProgress"); + m_showBarFunc = registerFastName(L"ShowBar"); + + return success; +} + +void UIControl_Progress::init(const wstring &label, int id, int min, int max, int current) +{ + m_label = label; + m_id = id; + m_min = min; + m_max = max; + m_current = current; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); +} + +void UIControl_Progress::ReInit() +{ + UIControl_Base::ReInit(); + init(m_label, m_id, m_min, m_max, m_current); +} + +void UIControl_Progress::setProgress(int current) +{ + m_current = current; + + float percent = (float)((m_current-m_min))/(m_max-m_min); + + if(percent != m_lastPercent) + { + m_lastPercent = percent; + //app.DebugPrintf("Setting progress value to %d/%f\n", m_current, percent); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = percent; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setProgressFunc , 1 , value ); + } +} + +void UIControl_Progress::showBar(bool show) +{ + if(show != m_showingBar) + { + m_showingBar = show; + //app.DebugPrintf("Setting progress value to %d/%f\n", m_current, percent); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_showBarFunc , 1 , value ); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Progress.h b/Minecraft.Client/Common/UI/UIControl_Progress.h new file mode 100644 index 0000000..8398a18 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Progress.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Progress : public UIControl_Base +{ +private: + IggyName m_setProgressFunc, m_showBarFunc; + int m_min; + int m_max; + int m_current; + float m_lastPercent; + bool m_showingBar; + +public: + UIControl_Progress(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id, int min, int max, int current); + virtual void ReInit(); + + void setProgress(int current); + void showBar(bool show); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_SaveList.cpp b/Minecraft.Client/Common/UI/UIControl_SaveList.cpp new file mode 100644 index 0000000..f83454d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SaveList.cpp @@ -0,0 +1,106 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_SaveList.h" + +bool UIControl_SaveList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eSaveList); + bool success = UIControl_ButtonList::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_funcSetTextureName = registerFastName(L"SetTextureName"); + + return success; +} + +void UIControl_SaveList::addItem(const wstring &label) +{ + addItem(label, L""); +} + +void UIControl_SaveList::addItem(const string &label) +{ + addItem(label, L""); +} + +void UIControl_SaveList::addItem(const wstring &label, int data) +{ + addItem(label, L"", data); +} + +void UIControl_SaveList::addItem(const string &label, int data) +{ + addItem(label, L"", data); +} + +void UIControl_SaveList::addItem(const string &label, const wstring &iconName) +{ + addItem(label, iconName, m_itemCount); + ++m_itemCount; +} + +void UIControl_SaveList::addItem(const wstring &label, const wstring &iconName) +{ + addItem(label, iconName, m_itemCount); + ++m_itemCount; +} + +void UIControl_SaveList::addItem(const string &label, const wstring &iconName, int data) +{ + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF8 stringVal; + stringVal.string = (char*)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_itemCount; + + IggyStringUTF16 stringVal2; + stringVal2.string = (IggyUTF16*)iconName.c_str(); + stringVal2.length = iconName.length(); + value[2].type = IGGY_DATATYPE_string_UTF16; + value[2].string16 = stringVal2; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 3 , value ); +} + +void UIControl_SaveList::addItem(const wstring &label, const wstring &iconName, int data) +{ + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = (S32)label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_itemCount; + + IggyStringUTF16 stringVal2; + stringVal2.string = (IggyUTF16*)iconName.c_str(); + stringVal2.length = iconName.length(); + value[2].type = IGGY_DATATYPE_string_UTF16; + value[2].string16 = stringVal2; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addNewItemFunc , 3 , value ); +} + +void UIControl_SaveList::setTextureName(int iId, const wstring &iconName) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iId; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)iconName.c_str(); + stringVal.length = iconName.length(); + value[1].type = IGGY_DATATYPE_string_UTF16; + value[1].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSetTextureName , 2 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_SaveList.h b/Minecraft.Client/Common/UI/UIControl_SaveList.h new file mode 100644 index 0000000..7c72fea --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SaveList.h @@ -0,0 +1,29 @@ +#pragma once + +#include "UIControl_ButtonList.h" + +class UIControl_SaveList : public UIControl_ButtonList +{ +private: + IggyName m_funcSetTextureName; + +public: + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + using UIControl_ButtonList::addItem; + + void addItem(const wstring &label); + void addItem(const string &label); + + void addItem(const wstring &label, int data); + void addItem(const string &label, int data); + + void addItem(const string &label, const wstring &iconName); + void addItem(const wstring &label, const wstring &iconName); + void setTextureName(int iId, const wstring &iconName); + +private: + void addItem(const string &label, const wstring &iconName, int data); + void addItem(const wstring &label, const wstring &iconName, int data); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Slider.cpp b/Minecraft.Client/Common/UI/UIControl_Slider.cpp new file mode 100644 index 0000000..bd3b1ad --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Slider.cpp @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Slider.h" + +UIControl_Slider::UIControl_Slider() +{ + m_id = 0; + m_min = 0; + m_max = 100; + m_current = 0; +} + +bool UIControl_Slider::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eSlider); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Slider specific initialisers + m_funcSetRelativeSliderPos = registerFastName(L"SetRelativeSliderPos"); + m_funcGetRealWidth = registerFastName(L"GetRealWidth"); + + return success; +} + +void UIControl_Slider::init(const wstring &label, int id, int min, int max, int current) +{ + m_label = label; + m_id = id; + m_min = min; + m_max = max; + m_current = current; + + IggyDataValue result; + IggyDataValue value[5]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (int)id; + + value[2].type = IGGY_DATATYPE_number; + value[2].number = (int)min; + + value[3].type = IGGY_DATATYPE_number; + value[3].number = (int)max; + + value[4].type = IGGY_DATATYPE_number; + value[4].number = (int)current; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 5 , value ); + +#ifdef __PSVITA__ + // 4J-TomK - add slider to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } +#endif +} + +void UIControl_Slider::handleSliderMove(int newValue) +{ + if (m_current!=newValue) + { + ui.PlayUISFX(eSFX_Scroll); + m_current = newValue; + + if(newValue < m_allPossibleLabels.size()) + { + setLabel(m_allPossibleLabels[newValue]); + } + } +} + +void UIControl_Slider::SetSliderTouchPos(float fTouchPos) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = fTouchPos; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetRelativeSliderPos , 1 , value ); + } + +S32 UIControl_Slider::GetRealWidth() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealWidth , 0 , NULL ); + + S32 iRealWidth = m_width; + if(result.type == IGGY_DATATYPE_number) + { + iRealWidth = (S32)result.number; + } + return iRealWidth; +} + +void UIControl_Slider::setAllPossibleLabels(int labelCount, wchar_t labels[][256]) +{ + for(unsigned int i = 0; i < labelCount; ++i) + { + m_allPossibleLabels.push_back(labels[i]); + } + UIControl_Base::setAllPossibleLabels(labelCount, labels); +} + +void UIControl_Slider::ReInit() +{ + UIControl_Base::ReInit(); + + init(m_label, m_id, m_min, m_max, m_current); +} diff --git a/Minecraft.Client/Common/UI/UIControl_Slider.h b/Minecraft.Client/Common/UI/UIControl_Slider.h new file mode 100644 index 0000000..0b57c2f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Slider.h @@ -0,0 +1,32 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Slider : public UIControl_Base +{ +private: + //int m_id; // 4J-TomK this is part of class UIControl and doesn't need to be here! + int m_min; + int m_max; + int m_current; + + vector m_allPossibleLabels; + + // 4J-TomK - function for setting slider position on touch + IggyName m_funcSetRelativeSliderPos; + IggyName m_funcGetRealWidth; + +public: + UIControl_Slider(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id, int min, int max, int current); + + void handleSliderMove(int newValue); + void SetSliderTouchPos(float fTouchPos); + virtual void setAllPossibleLabels(int labelCount, wchar_t labels[][256]); + + S32 GetRealWidth(); + virtual void ReInit(); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_SlotList.cpp b/Minecraft.Client/Common/UI/UIControl_SlotList.cpp new file mode 100644 index 0000000..e213043 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SlotList.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_SlotList.h" + +UIControl_SlotList::UIControl_SlotList() +{ + m_lastHighlighted = -1; +} + +bool UIControl_SlotList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eSlotList); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_addSlotFunc = registerFastName(L"addSlot"); + m_setRedBoxFunc = registerFastName(L"SetSlotRedBox"); + m_setHighlightFunc = registerFastName(L"SetSlotHighlight"); + + m_lastHighlighted = 0; + + return success; +} + +void UIControl_SlotList::addSlot(int id) +{ + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = id; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = false; + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = false; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addSlotFunc ,3 , value ); +} + +void UIControl_SlotList::addSlots(int iStartValue, int iCount) +{ + for(unsigned int i = iStartValue; i < iStartValue + iCount; ++i) + { + addSlot(i); + } +} + + +void UIControl_SlotList::setHighlightSlot(int index) +{ + if(index != m_lastHighlighted) + { + if(m_lastHighlighted != -1) + { + setSlotHighlighted(m_lastHighlighted, false); + } + setSlotHighlighted(index, true); + m_lastHighlighted = index; + } +} + +void UIControl_SlotList::setSlotHighlighted(int index, bool highlight) +{ + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = index; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = highlight; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_setHighlightFunc , 2 , value ); +} + +void UIControl_SlotList::showSlotRedBox(int index, bool show) +{ + //app.DebugPrintf("Setting red box at index %d to %s\n", index, show?"on":"off"); + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = index; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_setRedBoxFunc , 2, value ); +} + +void UIControl_SlotList::setFocus(bool focus) +{ + if(m_lastHighlighted != -1) + { + if(focus) setSlotHighlighted(m_lastHighlighted, true); + else setSlotHighlighted(m_lastHighlighted, false); + } +} diff --git a/Minecraft.Client/Common/UI/UIControl_SlotList.h b/Minecraft.Client/Common/UI/UIControl_SlotList.h new file mode 100644 index 0000000..ee741c4 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SlotList.h @@ -0,0 +1,28 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_SlotList : public UIControl_Base +{ +private: + //IggyName m_addSlotFunc, m_getSlotFunc, m_setRedBoxFunc, m_setHighlightFunc; + IggyName m_addSlotFunc, m_setRedBoxFunc, m_setHighlightFunc; + + int m_lastHighlighted; + +public: + UIControl_SlotList(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void addSlot(int id); + void addSlots(int iStartValue, int iCount); + + void setHighlightSlot(int index); + void showSlotRedBox(int index, bool show); + + virtual void setFocus(bool focus); + +private: + void setSlotHighlighted(int index, bool highlight); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.cpp b/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.cpp new file mode 100644 index 0000000..dfdea93 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_SpaceIndicatorBar.h" + +UIControl_SpaceIndicatorBar::UIControl_SpaceIndicatorBar() +{ + m_min = 0; + m_max = 100; + m_currentSave = 0; + m_currentTotal = 0; + m_currentOffset = 0.0f; +} + +bool UIControl_SpaceIndicatorBar::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eProgress); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //Progress specific initialisers + m_setSaveSizeFunc = registerFastName(L"setSaveGameSize"); + m_setTotalSizeFunc = registerFastName(L"setTotalSize"); + m_setSaveGameOffsetFunc = registerFastName(L"setSaveGameOffset"); + + return success; +} + +void UIControl_SpaceIndicatorBar::init(const wstring &label, int id, __int64 min, __int64 max) +{ + m_label = label; + m_id = id; + m_min = min; + m_max = max; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 1 , value ); +} + +void UIControl_SpaceIndicatorBar::ReInit() +{ + UIControl_Base::ReInit(); + init(m_label, m_id, m_min, m_max); + setSaveSize(m_currentSave); + setTotalSize(m_currentTotal); + setSaveGameOffset(m_currentOffset); +} + +void UIControl_SpaceIndicatorBar::reset() +{ + m_sizeAndOffsets.clear(); + m_currentTotal = 0; + setTotalSize(0); + setSaveSize(0); + setSaveGameOffset(0.0f); +} + +void UIControl_SpaceIndicatorBar::addSave(__int64 size) +{ + float startPercent = (float)((m_currentTotal-m_min))/(m_max-m_min); + + m_sizeAndOffsets.push_back( pair<__int64, float>(size, startPercent) ); + + m_currentTotal += size; + setTotalSize(m_currentTotal); +} + +void UIControl_SpaceIndicatorBar::selectSave(int index) +{ + if(index >= 0 && index < m_sizeAndOffsets.size()) + { + pair<__int64,float> values = m_sizeAndOffsets[index]; + setSaveSize(values.first); + setSaveGameOffset(values.second); + } + else + { + setSaveSize(0); + setSaveGameOffset(0); + } +} + +void UIControl_SpaceIndicatorBar::setSaveSize(__int64 size) +{ + m_currentSave = size; + + float percent = (float)((m_currentSave-m_min))/(m_max-m_min); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = percent; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setSaveSizeFunc , 1 , value ); +} + +void UIControl_SpaceIndicatorBar::setTotalSize(__int64 size) +{ + float percent = (float)((m_currentTotal-m_min))/(m_max-m_min); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = percent; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setTotalSizeFunc , 1 , value ); +} + +void UIControl_SpaceIndicatorBar::setSaveGameOffset(float offset) +{ + m_currentOffset = offset; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_currentOffset; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_setSaveGameOffsetFunc , 1 , value ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.h b/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.h new file mode 100644 index 0000000..39f9a74 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_SpaceIndicatorBar.h @@ -0,0 +1,33 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_SpaceIndicatorBar : public UIControl_Base +{ +private: + IggyName m_setSaveSizeFunc, m_setTotalSizeFunc, m_setSaveGameOffsetFunc; + __int64 m_min; + __int64 m_max; + __int64 m_currentSave, m_currentTotal; + float m_currentOffset; + + vector > m_sizeAndOffsets; + +public: + UIControl_SpaceIndicatorBar(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id, __int64 min, __int64 max); + virtual void ReInit(); + void reset(); + + void addSave(__int64 size); + void selectSave(int index); + + +private: + void setSaveSize(__int64 size); + void setTotalSize(__int64 totalSize); + void setSaveGameOffset(float offset); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.cpp b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp new file mode 100644 index 0000000..4cb78d5 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_TextInput.h" + +UIControl_TextInput::UIControl_TextInput() +{ + m_bHasFocus = false; +} + +bool UIControl_TextInput::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eTextInput); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //TextInput specific initialisers + m_textName = registerFastName(L"text"); + m_funcChangeState = registerFastName(L"ChangeState"); + m_funcSetCharLimit = registerFastName(L"SetCharLimit"); + + return success; +} + +void UIControl_TextInput::init(const wstring &label, int id) +{ + m_label = label; + m_id = id; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 2 , value ); + + #ifdef __PSVITA__ + // 4J-TomK - add this buttonlist to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } + #endif +} + +void UIControl_TextInput::ReInit() +{ + UIControl_Base::ReInit(); + + init(m_label, m_id); +} + +void UIControl_TextInput::setFocus(bool focus) +{ + if(m_bHasFocus != focus) + { + m_bHasFocus = focus; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = focus?0:1; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcChangeState , 1 , value ); + } +} + +void UIControl_TextInput::SetCharLimit(int iLimit) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iLimit; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcSetCharLimit , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIControl_TextInput.h b/Minecraft.Client/Common/UI/UIControl_TextInput.h new file mode 100644 index 0000000..d402388 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_TextInput.h @@ -0,0 +1,22 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_TextInput : public UIControl_Base +{ +private: + IggyName m_textName, m_funcChangeState, m_funcSetCharLimit; + bool m_bHasFocus; + +public: + UIControl_TextInput(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id); + void ReInit(); + + virtual void setFocus(bool focus); + + void SetCharLimit(int iLimit); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_TexturePackList.cpp b/Minecraft.Client/Common/UI/UIControl_TexturePackList.cpp new file mode 100644 index 0000000..02336e0 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_TexturePackList.cpp @@ -0,0 +1,145 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_TexturePackList.h" + +UIControl_TexturePackList::UIControl_TexturePackList() +{ +} + +bool UIControl_TexturePackList::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eTexturePackList); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + //SlotList specific initialisers + m_addPackFunc = registerFastName(L"addPack"); + m_clearSlotsFunc = registerFastName(L"removeAllItems"); + m_funcSelectSlot = registerFastName(L"SelectSlot"); + m_funcEnableSelector = registerFastName(L"EnableSelector"); + m_funcSetTouchFocus = registerFastName(L"SetTouchFocus"); + m_funcCanTouchTrigger = registerFastName(L"CanTouchTrigger"); + m_funcGetRealHeight = registerFastName(L"GetRealHeight"); + + return success; +} + +void UIControl_TexturePackList::init(const wstring &label, int id) +{ + m_label = label; + m_id = id; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_initFunc , 2 , value ); + +#ifdef __PSVITA__ + // 4J-TomK - add this texturepack list to the vita touch box list + + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } +#endif +} + +void UIControl_TexturePackList::addPack(int id, const wstring &textureName) +{ + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = id; + + value[1].type = IGGY_DATATYPE_string_UTF16; + IggyStringUTF16 stringVal; + + stringVal.string = (IggyUTF16*)textureName.c_str(); + stringVal.length = textureName.length(); + value[1].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_addPackFunc ,2 , value ); +} + +void UIControl_TexturePackList::selectSlot(int id) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = id; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcSelectSlot ,1 , value ); +} + +void UIControl_TexturePackList::clearSlots() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_clearSlotsFunc ,0 , NULL ); +} + +void UIControl_TexturePackList::setEnabled(bool enable) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].number = enable; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath(), m_funcEnableSelector ,1 , value ); +} + +void UIControl_TexturePackList::SetTouchFocus(S32 iX, S32 iY, bool bRepeat) +{ + IggyDataValue result; + IggyDataValue value[3]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iX; + value[1].type = IGGY_DATATYPE_number; + value[1].number = iY; + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = bRepeat; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcSetTouchFocus, 3 , value ); +} + +bool UIControl_TexturePackList::CanTouchTrigger(S32 iX, S32 iY) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iX; + value[1].type = IGGY_DATATYPE_number; + value[1].number = iY; + + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie(), &result, getIggyValuePath(), m_funcCanTouchTrigger, 2 , value ); + + S32 bCanTouchTrigger = false; + if(result.type == IGGY_DATATYPE_boolean) + { + bCanTouchTrigger = (bool)result.boolval; + } + return bCanTouchTrigger; +} + +S32 UIControl_TexturePackList::GetRealHeight() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( m_parentScene->getMovie() , &result, getIggyValuePath() , m_funcGetRealHeight, 0 , NULL ); + + S32 iRealHeight = m_height; + if(result.type == IGGY_DATATYPE_number) + { + iRealHeight = (S32)result.number; + } + return iRealHeight; +} + diff --git a/Minecraft.Client/Common/UI/UIControl_TexturePackList.h b/Minecraft.Client/Common/UI/UIControl_TexturePackList.h new file mode 100644 index 0000000..ce476fb --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_TexturePackList.h @@ -0,0 +1,27 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_TexturePackList : public UIControl_Base +{ +private: + IggyName m_addPackFunc, m_funcSelectSlot, m_funcSetTouchFocus, m_funcCanTouchTrigger, m_funcGetRealHeight,m_clearSlotsFunc; + IggyName m_funcEnableSelector; + +public: + UIControl_TexturePackList(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(const wstring &label, int id); + + void addPack(int id, const wstring &textureName); + void selectSlot(int id); + void clearSlots(); + + virtual void setEnabled(bool enable); + + void SetTouchFocus(S32 iX, S32 iY, bool bRepeat); + bool CanTouchTrigger(S32 iX, S32 iY); + S32 GetRealHeight(); +}; diff --git a/Minecraft.Client/Common/UI/UIControl_Touch.cpp b/Minecraft.Client/Common/UI/UIControl_Touch.cpp new file mode 100644 index 0000000..bd57882 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Touch.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIControl_Touch.h" + +UIControl_Touch::UIControl_Touch() +{ +} + +bool UIControl_Touch::setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName) +{ + UIControl::setControlType(UIControl::eTouchControl); + bool success = UIControl_Base::setupControl(scene,parent,controlName); + + return success; +} + +void UIControl_Touch::init(int iId) +{ + m_id = iId; + + // 4J-TomK - add this touch control to the vita touch box list + switch(m_parentScene->GetParentLayer()->m_iLayer) + { + case eUILayer_Error: + case eUILayer_Fullscreen: + case eUILayer_Scene: + case eUILayer_HUD: + ui.TouchBoxAdd(this,m_parentScene); + break; + } +} + +void UIControl_Touch::ReInit() +{ + UIControl_Base::ReInit(); + + init(m_id); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIControl_Touch.h b/Minecraft.Client/Common/UI/UIControl_Touch.h new file mode 100644 index 0000000..8ae799a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIControl_Touch.h @@ -0,0 +1,16 @@ +#pragma once + +#include "UIControl_Base.h" + +class UIControl_Touch : public UIControl_Base +{ +private: + +public: + UIControl_Touch(); + + virtual bool setupControl(UIScene *scene, IggyValuePath *parent, const string &controlName); + + void init(int id); + virtual void ReInit(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp new file mode 100644 index 0000000..2978d14 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -0,0 +1,3053 @@ +#include "stdafx.h" +#include "UIController.h" +#include "UI.h" +#include "UIScene.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\LocalPlayer.h" +#include "..\..\DLCTexturePack.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\Minecraft.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#ifdef _WINDOWS64 +#include "..\..\KeyboardMouseInput.h" +#endif +#include "..\..\EnderDragonRenderer.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "UIFontData.h" +#ifdef __PSVITA__ +#include +#endif + +// 4J Stu - Enable this to override the Iggy Allocator +//#define ENABLE_IGGY_ALLOCATOR +//#define EXCLUDE_IGGY_ALLOCATIONS_FROM_HEAP_INSPECTOR + +//#define ENABLE_IGGY_EXPLORER +#ifdef ENABLE_IGGY_EXPLORER +#include "Windows64\Iggy\include\iggyexpruntime.h" +#endif + +//#define ENABLE_IGGY_PERFMON +#ifdef ENABLE_IGGY_PERFMON + +#define PM_ORIGIN_X 24 +#define PM_ORIGIN_Y 34 + +#ifdef __ORBIS__ +#include "Orbis\Iggy\include\iggyperfmon.h" +#include "Orbis\Iggy\include\iggyperfmon_orbis.h" +#elif defined _DURANGO +#include "Durango\Iggy\include\iggyperfmon.h" +#elif defined __PS3__ +#include "PS3\Iggy\include\iggyperfmon.h" +#include "PS3\Iggy\include\iggyperfmon_ps3.h" +#elif defined __PSVITA__ +#include "PSVita\Iggy\include\iggyperfmon.h" +#include "PSVita\Iggy\include\iggyperfmon_psp2.h" +#elif defined __WINDOWS64 +#include "Windows64\Iggy\include\iggyperfmon.h" +#endif + +#endif + +CRITICAL_SECTION UIController::ms_reloadSkinCS; +bool UIController::ms_bReloadSkinCSInitialised = false; + +DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; + +static void RADLINK WarningCallback(void *user_callback_data, Iggy *player, IggyResult code, const char *message) +{ + //enum IggyResult{ IGGY_RESULT_SUCCESS = 0, IGGY_RESULT_Warning_None = 0, + // IGGY_RESULT_Warning_Misc = 100, IGGY_RESULT_Warning_GDraw = 101, + // IGGY_RESULT_Warning_ProgramFlow = 102, + // IGGY_RESULT_Warning_Actionscript = 103, + // IGGY_RESULT_Warning_Graphics = 104, IGGY_RESULT_Warning_Font = 105, + // IGGY_RESULT_Warning_Timeline = 106, IGGY_RESULT_Warning_Library = 107, + // IGGY_RESULT_Warning_CannotSustainFrameRate = 201, + // IGGY_RESULT_Warning_ThrewException = 202, + // IGGY_RESULT_Error_Threshhold = 400, IGGY_RESULT_Error_Misc = 400, + // IGGY_RESULT_Error_GDraw = 401, IGGY_RESULT_Error_ProgramFlow = 402, + // IGGY_RESULT_Error_Actionscript = 403, IGGY_RESULT_Error_Graphics = 404, + // IGGY_RESULT_Error_Font = 405, IGGY_RESULT_Error_Create = 406, + // IGGY_RESULT_Error_Library = 407, IGGY_RESULT_Error_ValuePath = 408, + // IGGY_RESULT_Error_Audio = 409, IGGY_RESULT_Error_Internal = 499, + // IGGY_RESULT_Error_InvalidIggy = 501, + // IGGY_RESULT_Error_InvalidArgument = 502, + // IGGY_RESULT_Error_InvalidEntity = 503, + // IGGY_RESULT_Error_UndefinedEntity = 504, + // IGGY_RESULT_Error_OutOfMemory = 1001,}; + + switch(code) + { + case IGGY_RESULT_Warning_CannotSustainFrameRate: + // Ignore warning + break; + default: + /* Normally, we'd want to issue this warning to some kind of + logging system or error reporting system, but since this is a + tutorial app, we just use Win32's default error stream. Since + ActionScript 3 exceptions are routed through this warning + callback, it's definitely a good idea to make sure these + warnings get printed somewhere that's easy for you to read and + use for debugging, otherwise debugging errors in the + ActionScript 3 code in your Flash content will be very + difficult! */ + app.DebugPrintf(app.USER_SR, message); + app.DebugPrintf(app.USER_SR, "\n"); + break; + }; +} + + +/* Flash provides a way for ActionScript 3 code to print debug output +using a function called "trace". It's very useful for debugging +Flash programs, so ideally, when using Iggy, we'd like to see any +trace output alongside our own debugging output. To facilitate +this, Iggy allows us to install a callback that will be called +any time ActionScript code calls trace. */ +static void RADLINK TraceCallback(void *user_callback_data, Iggy *player, char const *utf8_string, S32 length_in_bytes) +{ + app.DebugPrintf(app.USER_UI, (char *)utf8_string); +} + +#ifdef ENABLE_IGGY_PERFMON +static void *RADLINK perf_malloc(void *handle, U32 size) +{ + return malloc(size); +} + +static void RADLINK perf_free(void *handle, void *ptr) +{ + return free(ptr); +} +#endif + +#ifdef EXCLUDE_IGGY_ALLOCATIONS_FROM_HEAP_INSPECTOR +extern "C" void *__real_malloc(size_t t); +extern "C" void __real_free(void *t); +#endif + +__int64 UIController::iggyAllocCount = 0; +static unordered_map allocations; +static void * RADLINK AllocateFunction ( void * alloc_callback_user_data , size_t size_requested , size_t * size_returned ) +{ + UIController *controller = (UIController *)alloc_callback_user_data; + EnterCriticalSection(&controller->m_Allocatorlock); +#ifdef EXCLUDE_IGGY_ALLOCATIONS_FROM_HEAP_INSPECTOR + void *alloc = __real_malloc(size_requested); +#else + void *alloc = malloc(size_requested); +#endif + *size_returned = size_requested; + UIController::iggyAllocCount += size_requested; + allocations[alloc] = size_requested; + app.DebugPrintf(app.USER_SR, "Allocating %d, new total: %d\n", size_requested, UIController::iggyAllocCount); + LeaveCriticalSection(&controller->m_Allocatorlock); + return alloc; +} + +static void RADLINK DeallocateFunction ( void * alloc_callback_user_data , void * ptr ) +{ + UIController *controller = (UIController *)alloc_callback_user_data; + EnterCriticalSection(&controller->m_Allocatorlock); + size_t size = allocations[ptr]; + UIController::iggyAllocCount -= size; + allocations.erase(ptr); + app.DebugPrintf(app.USER_SR, "Freeing %d, new total %d\n", size, UIController::iggyAllocCount); +#ifdef EXCLUDE_IGGY_ALLOCATIONS_FROM_HEAP_INSPECTOR + __real_free(ptr); +#else + free(ptr); +#endif + LeaveCriticalSection(&controller->m_Allocatorlock); +} + +UIController::UIController() +{ + m_uiDebugConsole = NULL; + m_reloadSkinThread = NULL; + m_navigateToHomeOnReload = false; + m_mcTTFFont= NULL; + m_moj7 = NULL; + m_moj11 = NULL; + +#ifdef ENABLE_IGGY_ALLOCATOR + InitializeCriticalSection(&m_Allocatorlock); +#endif + + // 4J Stu - This is a bit of a hack until we change the Minecraft initialisation to store the proper screen size for other platforms +#if defined _WINDOWS64 || defined _DURANGO || defined __ORBIS__ + m_fScreenWidth = 1920.0f; + m_fScreenHeight = 1080.0f; + m_bScreenWidthSetup = true; +#else + m_fScreenWidth = 1280.0f; + m_fScreenHeight = 720.0f; + m_bScreenWidthSetup = false; +#endif + + for(unsigned int i = 0; i < eLibrary_Count; ++i) + { + m_iggyLibraries[i] = IGGY_INVALID_LIBRARY; + } + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_bMenuDisplayed[i] = false; + m_iCountDown[i]=0; + m_bMenuToBeClosed[i]=false; + + for(unsigned int key = 0; key <= ACTION_MAX_MENU; ++key) + { + m_actionRepeatTimer[i][key] = 0; + } + } + + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + m_bCloseAllScenes[i] = false; + } + + m_iPressStartQuadrantsMask = 0; + + m_currentRenderViewport = C4JRender::VIEWPORT_TYPE_FULLSCREEN; + m_bCustomRenderPosition = false; + m_winUserIndex = 0; + m_accumulatedTicks = 0; + + InitializeCriticalSection(&m_navigationLock); + InitializeCriticalSection(&m_registeredCallbackScenesCS); + //m_bSysUIShowing=false; + m_bSystemUIShowing=false; +#ifdef __PSVITA__ + m_bTouchscreenPressed=false; +#endif + + if(!ms_bReloadSkinCSInitialised) + { + // MGH - added to prevent crash loading Iggy movies while the skins were being reloaded + InitializeCriticalSection(&ms_reloadSkinCS); + ms_bReloadSkinCSInitialised = true; + } +} + +void UIController::SetSysUIShowing(bool bVal) +{ + if(bVal) app.DebugPrintf("System UI showing\n"); + else app.DebugPrintf("System UI stopped showing\n"); + m_bSystemUIShowing=bVal; +} + +void UIController::SetSystemUIShowing(LPVOID lpParam,bool bVal) +{ + UIController *pClass=(UIController *)lpParam; + pClass->SetSysUIShowing(bVal); +} + +// SETUP +void UIController::preInit(S32 width, S32 height) +{ + m_fScreenWidth = width; + m_fScreenHeight = height; + m_bScreenWidthSetup = true; + +#ifdef ENABLE_IGGY_ALLOCATOR + IggyAllocator allocator; + allocator.user_callback_data = this; + allocator.mem_alloc = &AllocateFunction; + allocator.mem_free = &DeallocateFunction; + IggyInit(&allocator); +#else + IggyInit(0); +#endif + + IggySetWarningCallback(WarningCallback, 0); + IggySetTraceCallbackUTF8(TraceCallback, 0); + + setFontCachingCalculationBuffer(-1); +} + +void UIController::postInit() +{ + // set up a custom rendering callback + IggySetCustomDrawCallback(&UIController::CustomDrawCallback, this); + IggySetAS3ExternalFunctionCallbackUTF16 ( &UIController::ExternalFunctionCallback, this ); + IggySetTextureSubstitutionCallbacks ( &UIController::TextureSubstitutionCreateCallback , &UIController::TextureSubstitutionDestroyCallback, this ); + + SetupFont(); + // + loadSkins(); + + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + m_groups[i] = new UIGroup((EUIGroup)i,i-1); + } + + +#ifdef ENABLE_IGGY_EXPLORER + iggy_explorer = IggyExpCreate("127.0.0.1", 9190, malloc(IGGYEXP_MIN_STORAGE), IGGYEXP_MIN_STORAGE); + if ( iggy_explorer == NULL ) + { + // not normally an error, just an error for this demo! + app.DebugPrintf( "Couldn't connect to Iggy Explorer, did you run it first?" ); + } + else + { + IggyUseExplorer( m_groups[1]->getHUD()->getMovie(), iggy_explorer); + } +#endif + +#ifdef ENABLE_IGGY_PERFMON + m_iggyPerfmonEnabled = false; + iggy_perfmon = IggyPerfmonCreate(perf_malloc, perf_free, NULL); + IggyInstallPerfmon(iggy_perfmon); +#endif + + NavigateToScene(0, eUIScene_Intro); +} + +void UIController::SetupFont() +{ + bool bBitmapFont=false; + + if(m_mcTTFFont!=NULL) + { + delete m_mcTTFFont; + } + + switch(XGetLanguage()) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + case XC_LANGUAGE_JAPANESE: + m_mcTTFFont = new UITTFFont("Common/Media/font/JPN/DF-DotDotGothic16.ttf", 0x203B); // JPN + break; + case XC_LANGUAGE_SCHINESE: //TODO + case XC_LANGUAGE_TCHINESE: + m_mcTTFFont = new UITTFFont("Common/Media/font/CHT/DFTT_R5.TTC", 0x203B); // CHT + break; + case XC_LANGUAGE_KOREAN: + m_mcTTFFont = new UITTFFont("Common/Media/font/KOR/candadite2.ttf", 0x203B); // KOR + break; + // 4J-JEV, Cyrillic characters have been added to this font now, (4/July/14) + //case XC_LANGUAGE_RUSSIAN: + //case XC_LANGUAGE_GREEK: +#else + case XC_LANGUAGE_JAPANESE: + m_mcTTFFont = new UITTFFont("Common/Media/font/JPN/DFGMaruGothic-Md.ttf", 0x2022); // JPN + break; + case XC_LANGUAGE_SCHINESE: //TODO + case XC_LANGUAGE_TCHINESE: + m_mcTTFFont = new UITTFFont("Common/Media/font/CHT/DFHeiMedium-B5.ttf", 0x2022); // CHT + break; + case XC_LANGUAGE_KOREAN: + m_mcTTFFont = new UITTFFont("Common/Media/font/KOR/BOKMSD.ttf", 0x2022); // KOR + break; +#endif + default: + bBitmapFont=true; + // m_mcTTFFont = new UITTFFont("Common/Media/font/Mojangles.ttf", 0x2022); // 4J-JEV: Shouldn't be using this. + break; + } + + if(bBitmapFont) + { + // these may have been set up by a previous language being chosen + if(m_moj7==NULL) + { + m_moj7 = new UIBitmapFont(SFontData::Mojangles_7); + m_moj7->registerFont(); + } + if(m_moj11==NULL) + { + m_moj11 = new UIBitmapFont(SFontData::Mojangles_11); + m_moj11->registerFont(); + } + } + else + { + app.DebugPrintf("IggyFontSetIndirectUTF8\n"); + IggyFontSetIndirectUTF8( "Mojangles7", -1, IGGY_FONTFLAG_all, "Mojangles_TTF",-1 ,IGGY_FONTFLAG_none ); + IggyFontSetIndirectUTF8( "Mojangles11", -1, IGGY_FONTFLAG_all, "Mojangles_TTF",-1 ,IGGY_FONTFLAG_none ); + } +} + +// TICKING +void UIController::tick() +{ + if(m_navigateToHomeOnReload && !ui.IsReloadingSkin()) + { + ui.CleanUpSkinReload(); + m_navigateToHomeOnReload = false; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MainMenu); + } + + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + if(m_bCloseAllScenes[i]) + { + m_groups[i]->closeAllScenes(); + m_groups[i]->getTooltips()->SetTooltips(-1); + m_bCloseAllScenes[i] = false; + } + } + + if(m_accumulatedTicks == 0) tickInput(); + m_accumulatedTicks = 0; + + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + m_groups[i]->tick(); + + // TODO: May wish to skip ticking other groups here + } + + // Fix for HUD ticks so that they all tick before this reference is cleared + EnderDragonRenderer::bossInstance = nullptr; + + // Clear out the cached movie file data + __int64 currentTime = System::currentTimeMillis(); + for(AUTO_VAR(it, m_cachedMovieData.begin()); it != m_cachedMovieData.end();) + { + if(it->second.m_expiry < currentTime) + { + delete [] it->second.m_ba.data; + it = m_cachedMovieData.erase(it); + } + else + { + ++it; + } + } +} + +void UIController::loadSkins() +{ + wstring platformSkinPath = L""; + +#ifdef __PS3__ + platformSkinPath = L"skinPS3.swf"; +#elif defined __PSVITA__ + platformSkinPath = L"skinVita.swf"; +#elif defined _WINDOWS64 + if(m_fScreenHeight==1080.0f) + { + platformSkinPath = L"skinHDWin.swf"; + } + else + { + platformSkinPath = L"skinWin.swf"; + } +#elif defined _DURANGO + if(m_fScreenHeight==1080.0f) + { + platformSkinPath = L"skinHDDurango.swf"; + } + else + { + platformSkinPath = L"skinDurango.swf"; + } +#elif defined __ORBIS__ + if(m_fScreenHeight==1080.0f) + { + platformSkinPath = L"skinHDOrbis.swf"; + } + else + { + platformSkinPath = L"skinOrbis.swf"; + } + +#endif + // Every platform has one of these, so nothing shared + if(m_fScreenHeight==1080.0f) + { + m_iggyLibraries[eLibrary_Platform] = loadSkin(platformSkinPath, L"platformskinHD.swf"); + } + else + { + m_iggyLibraries[eLibrary_Platform] = loadSkin(platformSkinPath, L"platformskin.swf"); + } + +#if defined(__PS3__) || defined(__PSVITA__) + m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinGraphics.swf", L"skinGraphics.swf"); + m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinGraphicsHud.swf", L"skinGraphicsHud.swf"); + m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinGraphicsInGame.swf", L"skinGraphicsInGame.swf"); + m_iggyLibraries[eLibrary_GraphicsTooltips] = loadSkin(L"skinGraphicsTooltips.swf", L"skinGraphicsTooltips.swf"); + m_iggyLibraries[eLibrary_GraphicsLabels] = loadSkin(L"skinGraphicsLabels.swf", L"skinGraphicsLabels.swf"); + m_iggyLibraries[eLibrary_Labels] = loadSkin(L"skinLabels.swf", L"skinLabels.swf"); + m_iggyLibraries[eLibrary_InGame] = loadSkin(L"skinInGame.swf", L"skinInGame.swf"); + m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHud.swf", L"skinHud.swf"); + m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinTooltips.swf", L"skinTooltips.swf"); + m_iggyLibraries[eLibrary_Default] = loadSkin(L"skin.swf", L"skin.swf"); +#endif + +#if ( defined(_WINDOWS64) || defined(_DURANGO) || defined(__ORBIS__) ) + +#if defined(_WINDOWS64) + // 4J Stu - Load the 720/480 skins so that we have something to fallback on during development +#ifndef _FINAL_BUILD + m_iggyLibraries[eLibraryFallback_GraphicsDefault] = loadSkin(L"skinGraphics.swf", L"skinGraphics.swf"); + m_iggyLibraries[eLibraryFallback_GraphicsHUD] = loadSkin(L"skinGraphicsHud.swf", L"skinGraphicsHud.swf"); + m_iggyLibraries[eLibraryFallback_GraphicsInGame] = loadSkin(L"skinGraphicsInGame.swf", L"skinGraphicsInGame.swf"); + m_iggyLibraries[eLibraryFallback_GraphicsTooltips] = loadSkin(L"skinGraphicsTooltips.swf", L"skinGraphicsTooltips.swf"); + m_iggyLibraries[eLibraryFallback_GraphicsLabels] = loadSkin(L"skinGraphicsLabels.swf", L"skinGraphicsLabels.swf"); + m_iggyLibraries[eLibraryFallback_Labels] = loadSkin(L"skinLabels.swf", L"skinLabels.swf"); + m_iggyLibraries[eLibraryFallback_InGame] = loadSkin(L"skinInGame.swf", L"skinInGame.swf"); + m_iggyLibraries[eLibraryFallback_HUD] = loadSkin(L"skinHud.swf", L"skinHud.swf"); + m_iggyLibraries[eLibraryFallback_Tooltips] = loadSkin(L"skinTooltips.swf", L"skinTooltips.swf"); + m_iggyLibraries[eLibraryFallback_Default] = loadSkin(L"skin.swf", L"skin.swf"); +#endif +#endif + + m_iggyLibraries[eLibrary_GraphicsDefault] = loadSkin(L"skinHDGraphics.swf", L"skinHDGraphics.swf"); + m_iggyLibraries[eLibrary_GraphicsHUD] = loadSkin(L"skinHDGraphicsHud.swf", L"skinHDGraphicsHud.swf"); + m_iggyLibraries[eLibrary_GraphicsInGame] = loadSkin(L"skinHDGraphicsInGame.swf", L"skinHDGraphicsInGame.swf"); + m_iggyLibraries[eLibrary_GraphicsTooltips] = loadSkin(L"skinHDGraphicsTooltips.swf", L"skinHDGraphicsTooltips.swf"); + m_iggyLibraries[eLibrary_GraphicsLabels] = loadSkin(L"skinHDGraphicsLabels.swf", L"skinHDGraphicsLabels.swf"); + m_iggyLibraries[eLibrary_Labels] = loadSkin(L"skinHDLabels.swf", L"skinHDLabels.swf"); + m_iggyLibraries[eLibrary_InGame] = loadSkin(L"skinHDInGame.swf", L"skinHDInGame.swf"); + m_iggyLibraries[eLibrary_HUD] = loadSkin(L"skinHDHud.swf", L"skinHDHud.swf"); + m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinHDTooltips.swf", L"skinHDTooltips.swf"); + m_iggyLibraries[eLibrary_Default] = loadSkin(L"skinHD.swf", L"skinHD.swf"); +#endif // HD platforms +} + +IggyLibrary UIController::loadSkin(const wstring &skinPath, const wstring &skinName) +{ + IggyLibrary lib = IGGY_INVALID_LIBRARY; + // 4J Stu - We need to load the platformskin before the normal skin, as the normal skin requires some elements from the platform skin + if(!skinPath.empty() && app.hasArchiveFile(skinPath)) + { + byteArray baFile = app.getArchiveFile(skinPath); + lib = IggyLibraryCreateFromMemoryUTF16( (IggyUTF16 *)skinName.c_str() , (void *)baFile.data, baFile.length, NULL ); + + delete[] baFile.data; +#ifdef _DEBUG + IggyMemoryUseInfo memoryInfo; + rrbool res; + int iteration = 0; + __int64 totalStatic = 0; + while(res = IggyDebugGetMemoryUseInfo ( NULL , + lib , + "" , + 0 , + iteration , + &memoryInfo )) + { + totalStatic += memoryInfo.static_allocation_bytes; + app.DebugPrintf(app.USER_SR, "%ls - %.*s, static: %dB, dynamic: %dB\n", skinPath.c_str(), memoryInfo.subcategory_stringlen, memoryInfo.subcategory, memoryInfo.static_allocation_bytes, memoryInfo.dynamic_allocation_bytes); + ++iteration; + } + + app.DebugPrintf(app.USER_SR, "%ls - Total static: %dB (%dKB)\n", skinPath.c_str(), totalStatic, totalStatic/1024); +#endif + } + return lib; +} + +void UIController::ReloadSkin() +{ + // Destroy all scene swf + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + //m_bCloseAllScenes[i] = true; + m_groups[i]->DestroyAll(); + } + + // Unload the current libraries + // Some libraries reference others, so we destroy in reverse order + for(int i = eLibrary_Count - 1; i >= 0; --i) + { + if(m_iggyLibraries[i] != IGGY_INVALID_LIBRARY) IggyLibraryDestroy(m_iggyLibraries[i]); + m_iggyLibraries[i] = IGGY_INVALID_LIBRARY; + } + +#ifdef _WINDOWS64 + // 4J Stu - Don't load on a thread on windows. I haven't investigated this in detail, so a quick fix + reloadSkinThreadProc(this); +#else + // Navigate to the timer scene so that we can display something while the loading is happening + ui.NavigateToScene(0,eUIScene_Timer,(void *)1,eUILayer_Tooltips,eUIGroup_Fullscreen); + + m_reloadSkinThread = new C4JThread(reloadSkinThreadProc, (void*)this, "Reload skin thread"); + m_reloadSkinThread->SetProcessor(CPU_CORE_UI_SCENE); + //m_reloadSkinThread->Run(); + + //// Load new skin + //loadSkins(); + + //// Reload all scene swf + //for(int i = eUIGroup_Player1; i <= eUIGroup_Player4; ++i) + //{ + // m_groups[i]->ReloadAll(); + //} + + //// Always reload the fullscreen group + //m_groups[eUIGroup_Fullscreen]->ReloadAll(); +#endif +} + +void UIController::StartReloadSkinThread() +{ + if(m_reloadSkinThread) m_reloadSkinThread->Run(); +} + +int UIController::reloadSkinThreadProc(void* lpParam) +{ + EnterCriticalSection(&ms_reloadSkinCS); // MGH - added to prevent crash loading Iggy movies while the skins were being reloaded + UIController *controller = (UIController *)lpParam; + // Load new skin + controller->loadSkins(); + + // Reload all scene swf + for(int i = eUIGroup_Player1; i < eUIGroup_COUNT; ++i) + { + controller->m_groups[i]->ReloadAll(); + } + + // Always reload the fullscreen group + controller->m_groups[eUIGroup_Fullscreen]->ReloadAll(); + + // 4J Stu - Don't do this on windows, as we never navigated forwards to start with +#ifndef _WINDOW64 + controller->NavigateBack(0, false, eUIScene_COUNT, eUILayer_Tooltips); +#endif + LeaveCriticalSection(&ms_reloadSkinCS); + + return 0; +} + +bool UIController::IsReloadingSkin() +{ + return m_reloadSkinThread && (!m_reloadSkinThread->hasStarted() || m_reloadSkinThread->isRunning()); +} + +bool UIController::IsExpectingOrReloadingSkin() +{ + return Minecraft::GetInstance()->skins->getSelected()->isLoadingData() || Minecraft::GetInstance()->skins->needsUIUpdate() || IsReloadingSkin(); +} + +void UIController::CleanUpSkinReload() +{ + delete m_reloadSkinThread; + m_reloadSkinThread = NULL; + + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + if(!Minecraft::GetInstance()->skins->getSelected()->hasAudio()) + { +#ifdef _DURANGO + DWORD result = StorageManager.UnmountInstalledDLC(L"TPACK"); +#else + DWORD result = StorageManager.UnmountInstalledDLC("TPACK"); +#endif + } + } + + for(AUTO_VAR(it,m_queuedMessageBoxData.begin()); it != m_queuedMessageBoxData.end(); ++it) + { + QueuedMessageBoxData *queuedData = *it; + ui.NavigateToScene(queuedData->iPad, eUIScene_MessageBox, &queuedData->info, queuedData->layer, eUIGroup_Fullscreen); + delete queuedData->info.uiOptionA; + delete queuedData; + } + m_queuedMessageBoxData.clear(); +} + +byteArray UIController::getMovieData(const wstring &filename) +{ + // Cache everything we load in the current tick + __int64 targetTime = System::currentTimeMillis() + (1000LL * 60); + AUTO_VAR(it,m_cachedMovieData.find(filename)); + if(it == m_cachedMovieData.end() ) + { + byteArray baFile = app.getArchiveFile(filename); + CachedMovieData cmd; + cmd.m_ba = baFile; + cmd.m_expiry = targetTime; + m_cachedMovieData[filename] = cmd; + return baFile; + } + else + { + it->second.m_expiry = targetTime; + return it->second.m_ba; + } +} + +// INPUT +void UIController::tickInput() +{ + // If system/commerce UI up, don't handle input + //if(!m_bSysUIShowing && !m_bSystemUIShowing) + if(!m_bSystemUIShowing) + { +#ifdef ENABLE_IGGY_PERFMON + if (m_iggyPerfmonEnabled) + { + if(InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(), ACTION_MENU_STICK_PRESS)) m_iggyPerfmonEnabled = !m_iggyPerfmonEnabled; + } + else +#endif + { + handleInput(); + ++m_accumulatedTicks; + } + } +} + +void UIController::handleInput() +{ + // For each user, loop over each key type and send messages based on the state + for(unsigned int iPad = 0; iPad < XUSER_MAX_COUNT; ++iPad) + { +#ifdef _DURANGO + // 4J-JEV: Added exception for primary play who migh've uttered speech commands. + if(iPad != ProfileManager.GetPrimaryPad() + && (!InputManager.IsPadConnected(iPad) || !InputManager.IsPadLocked(iPad)) ) continue; +#endif + for(unsigned int key = 0; key <= ACTION_MAX_MENU; ++key) + { + handleKeyPress(iPad, key); + } + +#ifdef __PSVITA__ + //CD - Vita requires key press 40 - select [MINECRAFT_ACTION_GAME_INFO] + handleKeyPress(iPad, MINECRAFT_ACTION_GAME_INFO); +#endif + } + +#ifdef _DURANGO + if(!app.GetGameStarted()) + { + bool repeat = false; + int firstUnfocussedUnhandledPad = -1; + + // For durango, check for unmapped controllers + for(unsigned int iPad = XUSER_MAX_COUNT; iPad < (XUSER_MAX_COUNT + InputManager.MAX_GAMEPADS); ++iPad) + { + if(InputManager.IsPadLocked(iPad) || !InputManager.IsPadConnected(iPad) ) continue; + + for(unsigned int key = 0; key <= ACTION_MAX_MENU; ++key) + { + + bool pressed = InputManager.ButtonPressed(iPad,key); // Toggle + bool released = InputManager.ButtonReleased(iPad,key); // Toggle + + if(pressed || released) + { + bool handled = false; + + // Send the key to the fullscreen group first + m_groups[(int)eUIGroup_Fullscreen]->handleInput(iPad, key, repeat, pressed, released, handled); + + if(firstUnfocussedUnhandledPad < 0 && !m_groups[(int)eUIGroup_Fullscreen]->HasFocus(iPad)) + { + firstUnfocussedUnhandledPad = iPad; + } + } + } + } + + if(ProfileManager.GetLockedProfile() >= 0 && !InputManager.IsPadLocked( ProfileManager.GetLockedProfile() ) && firstUnfocussedUnhandledPad >= 0) + { + ProfileManager.RequestSignInUI(false, false, false, false, true, NULL, NULL, firstUnfocussedUnhandledPad ); + } + } +#endif +} + +void UIController::handleKeyPress(unsigned int iPad, unsigned int key) +{ + + bool down = false; + bool pressed = false; // Toggle + bool released = false; // Toggle + bool repeat = false; + +#ifdef __PSVITA__ + if(key==ACTION_MENU_OK) + { + bool bTouchScreenInput=false; + + // check the touchscreen + + // 4J-PB - use the touchscreen for quickselect + SceTouchData* pTouchData = InputManager.GetTouchPadData(iPad,false); + + if((m_bTouchscreenPressed==false) && pTouchData->reportNum==1) + { + // no active touch? clear active and highlighted touch UI elements + m_ActiveUIElement = NULL; + m_HighlightedUIElement = NULL; + + // fullscreen first + UIScene *pScene=m_groups[(int)eUIGroup_Fullscreen]->getCurrentScene(); + // also check tooltip scene if we're not touching anything in the main scene + UIScene *pToolTips=m_groups[(int)eUIGroup_Fullscreen]->getTooltips(); + if(pScene) + { + // scene touch check + if(TouchBoxHit(pScene,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=pressed=m_bTouchscreenPressed=true; + bTouchScreenInput=true; + } + // tooltip touch check + else if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=pressed=m_bTouchscreenPressed=true; + bTouchScreenInput=true; + } + } + else + { + pScene=m_groups[(EUIGroup)(iPad+1)]->getCurrentScene(); + pToolTips=m_groups[(int)iPad+1]->getTooltips(); + if(pScene) + { + // scene touch check + if(TouchBoxHit(pScene,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=pressed=m_bTouchscreenPressed=true; + bTouchScreenInput=true; + } + // tooltip touch check (if scene exists but not component has been touched) + else if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=pressed=m_bTouchscreenPressed=true; + bTouchScreenInput=true; + } + } + else if(pToolTips) + { + // tooltip touch check (if scene does not exist) + if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=pressed=m_bTouchscreenPressed=true; + bTouchScreenInput=true; + } + } + } + } + else if(m_bTouchscreenPressed && pTouchData->reportNum==1) + { + // fullscreen first + UIScene *pScene=m_groups[(int)eUIGroup_Fullscreen]->getCurrentScene(); + // also check tooltip scene if we're not touching anything in the main scene + UIScene *pToolTips=m_groups[(int)eUIGroup_Fullscreen]->getTooltips(); + if(pScene) + { + // scene touch check + if(TouchBoxHit(pScene,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=true; + bTouchScreenInput=true; + } + // tooltip touch check (if scene exists but not component has been touched) + else if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=true; + bTouchScreenInput=true; + } + } + else + { + pScene=m_groups[(EUIGroup)(iPad+1)]->getCurrentScene(); + pToolTips=m_groups[(int)iPad+1]->getTooltips(); + if(pScene) + { + // scene touch check + if(TouchBoxHit(pScene,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=true; + bTouchScreenInput=true; + } + // tooltip touch check (if scene exists but not component has been touched) + else if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=true; + bTouchScreenInput=true; + } + } + else if(pToolTips) + { + // tooltip touch check (if scene does not exist) + if(TouchBoxHit(pToolTips,pTouchData->report[0].x,pTouchData->report[0].y)) + { + down=true; + bTouchScreenInput=true; + } + } + } + } + else if(m_bTouchscreenPressed && pTouchData->reportNum==0) + { + // released + bTouchScreenInput=true; + m_bTouchscreenPressed=false; + released=true; + } + + if(pressed) + { + // Start repeat timer + m_actionRepeatTimer[iPad][key] = GetTickCount() + UI_REPEAT_KEY_DELAY_MS; + } + else if (released) + { + // Stop repeat timer + m_actionRepeatTimer[iPad][key] = 0; + } + else if (down) + { + // Check is enough time has elapsed to be a repeat key + DWORD currentTime = GetTickCount(); + if(m_actionRepeatTimer[iPad][key] > 0 && currentTime > m_actionRepeatTimer[iPad][key]) + { + repeat = true; + pressed = true; + m_actionRepeatTimer[iPad][key] = currentTime + UI_REPEAT_KEY_REPEAT_RATE_MS; + } + } + + // handle touch input + HandleTouchInput(iPad, key, pressed, repeat, released); + + // ignore any other presses if the touchscreen has been used + if(bTouchScreenInput) return; + } +#endif + + down = InputManager.ButtonDown(iPad,key); + pressed = InputManager.ButtonPressed(iPad,key); // Toggle + released = InputManager.ButtonReleased(iPad,key); // Toggle + +#ifdef _WINDOWS64 + if (iPad == 0) + { + int vk = 0; + switch (key) + { + case ACTION_MENU_OK: case ACTION_MENU_A: vk = VK_RETURN; break; + case ACTION_MENU_CANCEL: case ACTION_MENU_B: vk = VK_ESCAPE; break; + case ACTION_MENU_UP: vk = VK_UP; break; + case ACTION_MENU_DOWN: vk = VK_DOWN; break; + case ACTION_MENU_LEFT: vk = VK_LEFT; break; + case ACTION_MENU_RIGHT: vk = VK_RIGHT; break; + case ACTION_MENU_X: vk = 'E'; break; + case ACTION_MENU_Y: vk = VK_TAB; break; + case ACTION_MENU_LEFT_SCROLL: vk = 'Q'; break; + case ACTION_MENU_RIGHT_SCROLL: vk = 'R'; break; + case ACTION_MENU_PAGEUP: vk = VK_PRIOR; break; + case ACTION_MENU_PAGEDOWN: vk = VK_NEXT; break; + } + if (vk != 0) + { + if (g_KBMInput.IsKeyPressed(vk)) { pressed = true; down = true; } + if (g_KBMInput.IsKeyReleased(vk)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsKeyDown(vk)) { down = true; } + } + + if ((key == ACTION_MENU_OK || key == ACTION_MENU_A) && !g_KBMInput.IsMouseGrabbed()) + { + if (g_KBMInput.IsMouseButtonPressed(KeyboardMouseInput::MOUSE_LEFT)) { pressed = true; down = true; } + if (g_KBMInput.IsMouseButtonReleased(KeyboardMouseInput::MOUSE_LEFT)) { released = true; down = false; } + if (!pressed && !released && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)) { down = true; } + } + } +#endif + + if(pressed) app.DebugPrintf("Pressed %d\n",key); + if(released) app.DebugPrintf("Released %d\n",key); + // Repeat handling + if(pressed) + { + // Start repeat timer + m_actionRepeatTimer[iPad][key] = GetTickCount() + UI_REPEAT_KEY_DELAY_MS; + } + else if (released) + { + // Stop repeat timer + m_actionRepeatTimer[iPad][key] = 0; + } + else if (down) + { + // Check is enough time has elapsed to be a repeat key + DWORD currentTime = GetTickCount(); + if(m_actionRepeatTimer[iPad][key] > 0 && currentTime > m_actionRepeatTimer[iPad][key]) + { + repeat = true; + pressed = true; + m_actionRepeatTimer[iPad][key] = currentTime + UI_REPEAT_KEY_REPEAT_RATE_MS; + } + } + +#ifndef _CONTENT_PACKAGE + +#ifdef ENABLE_IGGY_PERFMON + if ( pressed && !repeat && key == ACTION_MENU_STICK_PRESS) + { + m_iggyPerfmonEnabled = !m_iggyPerfmonEnabled; + } +#endif + + // 4J Stu - Removed this function +#if 0 +#ifdef __PS3__ + //if ( pressed && + // !repeat && + // //app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<PrintTotalMemoryUsage(totalStatic, totalDynamic); + } + for(unsigned int i = 0; i < eLibrary_Count; ++i) + { + __int64 libraryStatic = 0; + __int64 libraryDynamic = 0; + + if(m_iggyLibraries[i] != IGGY_INVALID_LIBRARY) + { + + IggyMemoryUseInfo memoryInfo; + rrbool res; + int iteration = 0; + while(res = IggyDebugGetMemoryUseInfo ( NULL , + m_iggyLibraries[i] , + "" , + 0 , + iteration , + &memoryInfo )) + { + libraryStatic += memoryInfo.static_allocation_bytes; + libraryDynamic += memoryInfo.dynamic_allocation_bytes; + totalStatic += memoryInfo.static_allocation_bytes; + totalDynamic += memoryInfo.dynamic_allocation_bytes; + ++iteration; + } + } + + app.DebugPrintf(app.USER_SR, "Library static: %dB , Library dynamic: %d, ID: %d\n", libraryStatic, libraryDynamic, i); + } + app.DebugPrintf(app.USER_SR, "Total static: %d , Total dynamic: %d\n", totalStatic, totalDynamic); + app.DebugPrintf(app.USER_SR, "\n\nEND TOTAL SWF MEMORY USAGE\n"); + app.DebugPrintf(app.USER_SR, "********************************\n\n"); + } + else +#endif +#endif +#endif + //#endif + if(repeat || pressed || released) + { + bool handled = false; + + // Send the key to the fullscreen group first + m_groups[(int)eUIGroup_Fullscreen]->handleInput(iPad, key, repeat, pressed, released, handled); + if(!handled) + { + // If it's not been handled yet, then pass the event onto the players specific group + m_groups[(iPad+1)]->handleInput(iPad, key, repeat, pressed, released, handled); + } + } +} + +rrbool RADLINK UIController::ExternalFunctionCallback( void * user_callback_data , Iggy * player , IggyExternalFunctionCallUTF16 * call) +{ + UIScene *scene = (UIScene *)IggyPlayerGetUserdata(player); + + if(scene != NULL) + { + scene->externalCallback(call); + } + + return true; +} + +// RENDERING +void UIController::renderScenes() +{ + PIXBeginNamedEvent(0, "Rendering Iggy scenes"); + // Only render player scenes if the game is started + if(app.GetGameStarted() && !m_groups[eUIGroup_Fullscreen]->hidesLowerScenes()) + { + for(int i = eUIGroup_Player1; i < eUIGroup_COUNT; ++i) + { + PIXBeginNamedEvent(0, "Rendering layer %d scenes", i); + m_groups[i]->render(); + PIXEndNamedEvent(); + } + } + + // Always render the fullscreen group + PIXBeginNamedEvent(0, "Rendering fullscreen scenes"); + m_groups[eUIGroup_Fullscreen]->render(); + PIXEndNamedEvent(); + + PIXEndNamedEvent(); + +#ifdef ENABLE_IGGY_PERFMON + if (m_iggyPerfmonEnabled) + { + IggyPerfmonPad pm_pad; + + pm_pad.bits = 0; + pm_pad.field.dpad_up = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_UP); + pm_pad.field.dpad_down = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_DOWN); + pm_pad.field.dpad_left = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_LEFT); + pm_pad.field.dpad_right = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_RIGHT); + pm_pad.field.button_up = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_Y); + pm_pad.field.button_down = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_A); + pm_pad.field.button_left = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_X); + pm_pad.field.button_right = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_B); + pm_pad.field.shoulder_left_hi = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_LEFT_SCROLL); + pm_pad.field.shoulder_right_hi = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_RIGHT_SCROLL); + pm_pad.field.trigger_left_low = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_PAGEUP); + pm_pad.field.trigger_right_low = InputManager.ButtonPressed(ProfileManager.GetPrimaryPad(),ACTION_MENU_PAGEDOWN); + //IggyPerfmonPadFromXInputStatePointer(pm_pad, &xi_pad); + + //gdraw_D3D_SetTileOrigin( fb, + // zb, + // PM_ORIGIN_X, + // PM_ORIGIN_Y ); + IggyPerfmonTickAndDraw(iggy_perfmon, gdraw_funcs, &pm_pad, + PM_ORIGIN_X, PM_ORIGIN_Y, getScreenWidth(), getScreenHeight()); // perfmon draw area in window coords + } +#endif +} + +void UIController::getRenderDimensions(C4JRender::eViewportType viewport, S32 &width, S32 &height) +{ + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + width = (S32)(getScreenWidth()); + height = (S32)(getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + width = (S32)(getScreenWidth() / 2); + height = (S32)(getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + width = (S32)(getScreenWidth() / 2); + height = (S32)(getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + width = (S32)(getScreenWidth() / 2); + height = (S32)(getScreenHeight() / 2); + break; + } +} + +void UIController::setupRenderPosition(C4JRender::eViewportType viewport) +{ + if(m_bCustomRenderPosition || m_currentRenderViewport != viewport) + { + m_currentRenderViewport = viewport; + m_bCustomRenderPosition = false; + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + xPos = (S32)(getScreenWidth() / 4); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + xPos = (S32)(getScreenWidth() / 4); + yPos = (S32)(getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + yPos = (S32)(getScreenHeight() / 4); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + xPos = (S32)(getScreenWidth() / 2); + yPos = (S32)(getScreenHeight() / 4); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(getScreenWidth() / 2); + yPos = (S32)(getScreenHeight() / 2); + break; + } + m_tileOriginX = xPos; + m_tileOriginY = yPos; + setTileOrigin(xPos, yPos); + } +} + +void UIController::setupRenderPosition(S32 xOrigin, S32 yOrigin) +{ + m_bCustomRenderPosition = true; + m_tileOriginX = xOrigin; + m_tileOriginY = yOrigin; + setTileOrigin(xOrigin, yOrigin); +} + +void UIController::setupCustomDrawGameState() +{ + // Rest the clear rect + m_customRenderingClearRect.left = LONG_MAX; + m_customRenderingClearRect.right = LONG_MIN; + m_customRenderingClearRect.top = LONG_MAX; + m_customRenderingClearRect.bottom = LONG_MIN; + +#if defined _WINDOWS64 || _DURANGO + PIXBeginNamedEvent(0,"StartFrame"); + RenderManager.StartFrame(); + PIXEndNamedEvent(); + gdraw_D3D11_setViewport_4J(); +#elif defined __PS3__ + RenderManager.StartFrame(); +#elif defined __PSVITA__ + RenderManager.StartFrame(); +#elif defined __ORBIS__ + RenderManager.StartFrame(false); + // Set up a viewport for the render that matches Iggy's own viewport, apart form using an opengl-style z-range (Iggy uses a DX-style range on PS4), so + // that the renderer orthographic projection will work + gdraw_orbis_setViewport_4J(); +#endif + RenderManager.Set_matrixDirty(); + + // 4J Stu - We don't need to clear this here as iggy hasn't written anything to the depth buffer. + // We DO however clear after we render which is why we still setup the rectangle here + //RenderManager.Clear(GL_DEPTH_BUFFER_BIT, &m_customRenderingClearRect); + //glClear(GL_DEPTH_BUFFER_BIT); + + PIXBeginNamedEvent(0,"Final setup"); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fScreenWidth, m_fScreenHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(true); + PIXEndNamedEvent(); +} + +void UIController::setupCustomDrawMatrices(UIScene *scene, CustomDrawData *customDrawRegion) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // Clear just the region required for this control. + float sceneWidth = (float)scene->getRenderWidth(); + float sceneHeight = (float)scene->getRenderHeight(); + + LONG left, right, top, bottom; +#ifdef __PS3__ + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + // 4J Stu - Our SD target on PS3 is double width + left = m_tileOriginX + (sceneWidth + customDrawRegion->mat[(0*4)+3]*sceneWidth); + right = left + ( (sceneWidth * customDrawRegion->mat[0]) ) * customDrawRegion->x1; + } + else +#endif + { + left = m_tileOriginX + (sceneWidth + customDrawRegion->mat[(0*4)+3]*sceneWidth)/2; + right = left + ( (sceneWidth * customDrawRegion->mat[0])/2 ) * customDrawRegion->x1; + } + + top = m_tileOriginY + (sceneHeight - customDrawRegion->mat[(1*4)+3]*sceneHeight)/2; + bottom = top + (sceneHeight * -customDrawRegion->mat[(1*4) + 1])/2 * customDrawRegion->y1; + + m_customRenderingClearRect.left = min(m_customRenderingClearRect.left, left); + m_customRenderingClearRect.right = max(m_customRenderingClearRect.right, right);; + m_customRenderingClearRect.top = min(m_customRenderingClearRect.top, top); + m_customRenderingClearRect.bottom = max(m_customRenderingClearRect.bottom, bottom); + + if(!m_bScreenWidthSetup) + { + Minecraft *pMinecraft=Minecraft::GetInstance(); + if(pMinecraft != NULL) + { + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_bScreenWidthSetup = true; + } + } + + glLoadIdentity(); + glTranslatef(0, 0, -2000); + // Iggy translations are based on a double-size target, with the origin in the centre + glTranslatef((m_fScreenWidth + customDrawRegion->mat[(0*4)+3]*m_fScreenWidth)/2,(m_fScreenHeight - customDrawRegion->mat[(1*4)+3]*m_fScreenHeight)/2,0); + // Iggy scales are based on a double-size target + glScalef( (m_fScreenWidth * customDrawRegion->mat[0])/2,(m_fScreenHeight * -customDrawRegion->mat[(1*4) + 1])/2,1.0f); +} + +void UIController::setupCustomDrawGameStateAndMatrices(UIScene *scene, CustomDrawData *customDrawRegion) +{ + setupCustomDrawGameState(); + setupCustomDrawMatrices(scene, customDrawRegion); +} + +void UIController::endCustomDrawGameState() +{ +#ifdef __ORBIS__ + // TO BE IMPLEMENTED + RenderManager.Clear(GL_DEPTH_BUFFER_BIT); +#else + RenderManager.Clear(GL_DEPTH_BUFFER_BIT, &m_customRenderingClearRect); +#endif + //glClear(GL_DEPTH_BUFFER_BIT); + glDepthMask(false); + glDisable(GL_ALPHA_TEST); +} + +void UIController::endCustomDrawMatrices() +{ +} + +void UIController::endCustomDrawGameStateAndMatrices() +{ + endCustomDrawMatrices(); + endCustomDrawGameState(); +} + +void RADLINK UIController::CustomDrawCallback(void *user_callback_data, Iggy *player, IggyCustomDrawCallbackRegion *region) +{ + UIScene *scene = (UIScene *)IggyPlayerGetUserdata(player); + + if(scene != NULL) + { + scene->customDraw(region); + } +} + +//Description +//Callback to create a user-defined texture to replace SWF-defined textures. +//Parameters +//width - Input value: optional number of pixels wide specified from AS3, or -1 if not defined. Output value: the number of pixels wide to pretend to Iggy that the bitmap is. SWF and AS3 scales bitmaps based on their pixel dimensions, so you can use this to substitute a texture that is higher or lower resolution that ActionScript thinks it is. +//height - Input value: optional number of pixels high specified from AS3, or -1 if not defined. Output value: the number of pixels high to pretend to Iggy that the bitmap is. SWF and AS3 scales bitmaps based on their pixel dimensions, so you can use this to substitute a texture that is higher or lower resolution that ActionScript thinks it is. +//destroy_callback_data - Optional additional output value you can set; the value will be passed along to the corresponding Iggy_TextureSubstitutionDestroyCallback (e.g. you can store the pointer to your own internal structure here). +//return - A platform-independent wrapped texture handle provided by GDraw, or NULL (NULL with throw an ActionScript 3 ArgumentError that the Flash developer can catch) Use by calling IggySetTextureSubstitutionCallbacks. +// +//Discussion +// +//If your texture includes an alpha channel, you must use a premultiplied alpha (where the R,G, and B channels have been multiplied by the alpha value); all Iggy shaders assume premultiplied alpha (and it looks better anyway). +GDrawTexture * RADLINK UIController::TextureSubstitutionCreateCallback ( void * user_callback_data , IggyUTF16 * texture_name , S32 * width , S32 * height , void * * destroy_callback_data ) +{ + UIController *uiController = (UIController *)user_callback_data; + AUTO_VAR(it,uiController->m_substitutionTextures.find((wchar_t *)texture_name)); + + if(it != uiController->m_substitutionTextures.end()) + { + app.DebugPrintf("Found substitution texture %ls, with %d bytes\n", (wchar_t *)texture_name,it->second.length); + + BufferedImage image(it->second.data, it->second.length); + if( image.getData() != NULL ) + { + image.preMultiplyAlpha(); + Textures *t = Minecraft::GetInstance()->textures; + int id = t->getTexture(&image,C4JRender::TEXTURE_FORMAT_RxGyBzAw,false); + + // 4J Stu - All our flash controls that allow replacing textures use a special 64x64 symbol + // Force this size here so that our images don't get scaled wildly + #if (defined __ORBIS__ || defined _DURANGO ) + *width = 96; + *height = 96; + #else + *width = 64; + *height = 64; + + #endif + *destroy_callback_data = (void *)id; + + app.DebugPrintf("Found substitution texture %ls (%d) - %dx%d\n", (wchar_t *)texture_name, id, image.getWidth(), image.getHeight()); + return ui.getSubstitutionTexture(id); + } + else + { + return NULL; + } + } + else + { + app.DebugPrintf("Could not find substitution texture %ls\n", (wchar_t *)texture_name); + return NULL; + } +} + +//Description +//Callback received from Iggy when it stops using a user-defined texture. +void RADLINK UIController::TextureSubstitutionDestroyCallback ( void * user_callback_data , void * destroy_callback_data , GDrawTexture * handle ) +{ + // Orbis complains about casting a pointer to an int + LONGLONG llVal=(LONGLONG)destroy_callback_data; + int id=(int)llVal; + app.DebugPrintf("Destroying iggy texture %d\n", id); + + ui.destroySubstitutionTexture(user_callback_data, handle); + + Textures *t = Minecraft::GetInstance()->textures; + t->releaseTexture( id ); +} + +void UIController::registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength) +{ + // Remove it if it already exists + unregisterSubstitutionTexture(textureName,false); + + m_substitutionTextures[textureName] = byteArray(pbData, dwLength); +} + +void UIController::unregisterSubstitutionTexture(const wstring &textureName, bool deleteData) +{ + AUTO_VAR(it,m_substitutionTextures.find(textureName)); + + if(it != m_substitutionTextures.end()) + { + if(deleteData) delete [] it->second.data; + m_substitutionTextures.erase(it); + } +} + +// NAVIGATION +bool UIController::NavigateToScene(int iPad, EUIScene scene, void *initData, EUILayer layer, EUIGroup group) +{ + // if you're trying to navigate to the inventory,the crafting, pause or game info or any of the trigger scenes and there's already a menu up (because you were pressing a few buttons at the same time) then ignore the navigate + if(GetMenuDisplayed(iPad)) + { + switch(scene) + { + case eUIScene_PauseMenu: + case eUIScene_Crafting2x2Menu: + case eUIScene_Crafting3x3Menu: + case eUIScene_FurnaceMenu: + case eUIScene_ContainerMenu: + case eUIScene_LargeContainerMenu: + case eUIScene_InventoryMenu: + case eUIScene_CreativeMenu: + case eUIScene_DispenserMenu: + case eUIScene_SignEntryMenu: + case eUIScene_InGameInfoMenu: + case eUIScene_EnchantingMenu: + case eUIScene_BrewingStandMenu: + case eUIScene_AnvilMenu: + case eUIScene_TradingMenu: + app.DebugPrintf("IGNORING NAVIGATE - we're trying to navigate to a user selected scene when there's already a scene up: pad:%d, scene:%d\n", iPad, scene); + return false; + break; + } + } + + switch(scene) + { + case eUIScene_FullscreenProgress: + { + // 4J Stu - The fullscreen progress scene should not interfere with any other scene stack, so should be placed in it's own group/layer + layer = eUILayer_Fullscreen; + group = eUIGroup_Fullscreen; + } + break; + case eUIScene_ConnectingProgress: + { + // The connecting progress scene shouldn't interfere with other scenes + layer = eUILayer_Fullscreen; + } + break; + case eUIScene_EndPoem: + { + // The end poem scene shouldn't interfere with other scenes, but will be underneath the autosave progress + group = eUIGroup_Fullscreen; + layer = eUILayer_Scene; + } + break; + }; + int menuDisplayedPad = XUSER_INDEX_ANY; + if(group == eUIGroup_PAD) + { + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) + { + menuDisplayedPad = iPad; + group = (EUIGroup)(iPad+1); + } + else group = eUIGroup_Fullscreen; + } + else + { + layer = eUILayer_Fullscreen; + group = eUIGroup_Fullscreen; + } + } + + PerformanceTimer timer; + + EnterCriticalSection(&m_navigationLock); + SetMenuDisplayed(menuDisplayedPad,true); + bool success = m_groups[(int)group]->NavigateToScene(iPad, scene, initData, layer); + if(success && group == eUIGroup_Fullscreen) setFullscreenMenuDisplayed(true); + LeaveCriticalSection(&m_navigationLock); + + timer.PrintElapsedTime(L"Navigate to scene"); + + return success; + //return true; +} + +bool UIController::NavigateBack(int iPad, bool forceUsePad, EUIScene eScene, EUILayer eLayer) +{ + bool navComplete = false; + if( app.GetGameStarted() ) + { + bool navComplete = m_groups[(int)eUIGroup_Fullscreen]->NavigateBack(iPad, eScene, eLayer); + + if(!navComplete && ( iPad != 255 ) && ( iPad >= 0 ) ) + { + EUIGroup group = (EUIGroup)(iPad+1); + navComplete = m_groups[(int)group]->NavigateBack(iPad, eScene, eLayer); + if(!m_groups[(int)group]->GetMenuDisplayed())SetMenuDisplayed(iPad,false); + } + // 4J-PB - autosave in fullscreen doesn't clear the menuDisplayed flag + else + { + if(!m_groups[(int)eUIGroup_Fullscreen]->GetMenuDisplayed()) + { + setFullscreenMenuDisplayed(false); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + SetMenuDisplayed(i,m_groups[i+1]->GetMenuDisplayed()); + } + } + } + } + else + { + navComplete = m_groups[(int)eUIGroup_Fullscreen]->NavigateBack(iPad, eScene, eLayer); + if(!m_groups[(int)eUIGroup_Fullscreen]->GetMenuDisplayed()) SetMenuDisplayed(XUSER_INDEX_ANY,false); + } + return navComplete; +} + +void UIController::NavigateToHomeMenu() +{ + ui.CloseAllPlayersScenes(); + + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // 4J-PB - just about to switched to the default texture pack , so clean up anything texture pack related here + + // unload any texture pack audio + // if there is audio in use, clear out the audio, and unmount the pack + TexturePack *pTexPack=Minecraft::GetInstance()->skins->getSelected(); + + + DLCTexturePack *pDLCTexPack=NULL; + if(pTexPack->hasAudio()) + { + // get the dlc texture pack, and store it + pDLCTexPack=(DLCTexturePack *)pTexPack; + } + + // change to the default texture pack + pMinecraft->skins->selectTexturePackById(TexturePackRepository::DEFAULT_TEXTURE_PACK_ID); + + + if(pTexPack->hasAudio()) + { + // need to stop the streaming audio - by playing streaming audio from the default texture pack now + // reset the streaming sounds back to the normal ones + pMinecraft->soundEngine->SetStreamingSounds(eStream_Overworld_Calm1,eStream_Overworld_piano3, + eStream_Nether1,eStream_Nether4, + eStream_end_dragon,eStream_end_end, + eStream_CD_1); + pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); + + // if(pDLCTexPack->m_pStreamedWaveBank!=NULL) + // { + // pDLCTexPack->m_pStreamedWaveBank->Destroy(); + // } + // if(pDLCTexPack->m_pSoundBank!=NULL) + // { + // pDLCTexPack->m_pSoundBank->Destroy(); + // } +#ifdef _XBOX_ONE + DWORD result = StorageManager.UnmountInstalledDLC(L"TPACK"); +#else + DWORD result = StorageManager.UnmountInstalledDLC("TPACK"); +#endif + + app.DebugPrintf("Unmount result is %d\n",result); + } + + g_NetworkManager.ForceFriendsSessionRefresh(); + + if(pMinecraft->skins->needsUIUpdate()) + { + m_navigateToHomeOnReload = true; + } + else + { + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MainMenu); + } +} + +UIScene *UIController::GetTopScene(int iPad, EUILayer layer, EUIGroup group) +{ + if(group == eUIGroup_PAD) + { + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) + { + group = (EUIGroup)(iPad+1); + } + else group = eUIGroup_Fullscreen; + } + else + { + layer = eUILayer_Fullscreen; + group = eUIGroup_Fullscreen; + } + } + return m_groups[(int)group]->GetTopScene(layer); +} + +size_t UIController::RegisterForCallbackId(UIScene *scene) +{ + EnterCriticalSection(&m_registeredCallbackScenesCS); + size_t newId = GetTickCount(); + newId &= 0xFFFFFF; // Chop off the top byte, we don't need any more accuracy than that + newId |= (scene->getSceneType() << 24); // Add in the scene's type to help keep this unique + m_registeredCallbackScenes[newId] = scene; + LeaveCriticalSection(&m_registeredCallbackScenesCS); + return newId; +} + +void UIController::UnregisterCallbackId(size_t id) +{ + EnterCriticalSection(&m_registeredCallbackScenesCS); + AUTO_VAR(it, m_registeredCallbackScenes.find(id) ); + if(it != m_registeredCallbackScenes.end() ) + { + m_registeredCallbackScenes.erase(it); + } + LeaveCriticalSection(&m_registeredCallbackScenesCS); +} + +UIScene *UIController::GetSceneFromCallbackId(size_t id) +{ + UIScene *scene = NULL; + AUTO_VAR(it, m_registeredCallbackScenes.find(id) ); + if(it != m_registeredCallbackScenes.end() ) + { + scene = it->second; + } + return scene; +} + +void UIController::EnterCallbackIdCriticalSection() +{ + EnterCriticalSection(&m_registeredCallbackScenesCS); +} + +void UIController::LeaveCallbackIdCriticalSection() +{ + LeaveCriticalSection(&m_registeredCallbackScenesCS); +} + +void UIController::CloseAllPlayersScenes() +{ + m_groups[(int)eUIGroup_Fullscreen]->getTooltips()->SetTooltips(-1); + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + //m_bCloseAllScenes[i] = true; + m_groups[i]->closeAllScenes(); + m_groups[i]->getTooltips()->SetTooltips(-1); + } + + if (!m_groups[eUIGroup_Fullscreen]->GetMenuDisplayed()) { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + SetMenuDisplayed(i,false); + } + } + setFullscreenMenuDisplayed(false); +} + +void UIController::CloseUIScenes(int iPad, bool forceIPad) +{ + EUIGroup group; + if( app.GetGameStarted() || forceIPad ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + + m_groups[(int)group]->closeAllScenes(); + m_groups[(int)group]->getTooltips()->SetTooltips(-1); + + // This should cause the popup to dissappear + TutorialPopupInfo popupInfo; + if(m_groups[(int)group]->getTutorialPopup()) m_groups[(int)group]->getTutorialPopup()->SetTutorialDescription(&popupInfo); + + if(group==eUIGroup_Fullscreen) setFullscreenMenuDisplayed(false); + + SetMenuDisplayed((group == eUIGroup_Fullscreen ? XUSER_INDEX_ANY : iPad), m_groups[(int)group]->GetMenuDisplayed()); +} + +void UIController::setFullscreenMenuDisplayed(bool displayed) +{ + // Show/hide the tooltips for the fullscreen group + m_groups[(int)eUIGroup_Fullscreen]->showComponent(ProfileManager.GetPrimaryPad(),eUIComponent_Tooltips,eUILayer_Tooltips,displayed); + + // Show/hide tooltips for the other layers + for(unsigned int i = (eUIGroup_Fullscreen+1); i < eUIGroup_COUNT; ++i) + { + m_groups[i]->showComponent(i,eUIComponent_Tooltips,eUILayer_Tooltips,!displayed); + } +} + +bool UIController::IsPauseMenuDisplayed(int iPad) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + return m_groups[(int)group]->IsPauseMenuDisplayed(); +} + +bool UIController::IsContainerMenuDisplayed(int iPad) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + return m_groups[(int)group]->IsContainerMenuDisplayed(); +} + +bool UIController::IsIgnorePlayerJoinMenuDisplayed(int iPad) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + return m_groups[(int)group]->IsIgnorePlayerJoinMenuDisplayed(); +} + +bool UIController::IsIgnoreAutosaveMenuDisplayed(int iPad) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + return m_groups[(int)eUIGroup_Fullscreen]->IsIgnoreAutosaveMenuDisplayed() || (group != eUIGroup_Fullscreen && m_groups[(int)group]->IsIgnoreAutosaveMenuDisplayed()); +} + +void UIController::SetIgnoreAutosaveMenuDisplayed(int iPad, bool displayed) +{ + app.DebugPrintf(app.USER_SR, "UIController::SetIgnoreAutosaveMenuDisplayed is not implemented\n"); +} + +bool UIController::IsSceneInStack(int iPad, EUIScene eScene) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + return m_groups[(int)group]->IsSceneInStack(eScene); +} + +bool UIController::GetMenuDisplayed(int iPad) +{ + return m_bMenuDisplayed[iPad]; +} + +void UIController::SetMenuDisplayed(int iPad,bool bVal) +{ + if(bVal) + { + if(iPad==XUSER_INDEX_ANY) + { + for(int i=0;irunning) + InputManager.SetEnabledGtcButtons(_360_GTC_MENU | _360_GTC_PAUSE | _360_GTC_VIEW); +#endif + } + } +} + +void UIController::CheckMenuDisplayed() +{ + for(int iPad=0;iPadgetTooltips()) m_groups[(int)group]->getTooltips()->SetTooltipText(tooltip, iTextID); +} + +void UIController::SetEnableTooltips( unsigned int iPad, BOOL bVal ) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->SetEnableTooltips(bVal); +} + +void UIController::ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->ShowTooltip(tooltip,show); +} + +void UIController::SetTooltips( unsigned int iPad, int iA, int iB, int iX, int iY, int iLT, int iRT, int iLB, int iRB, int iLS, bool forceUpdate) +{ + EUIGroup group; + + // 4J-PB - strip out any that are not applicable on the platform +#ifndef _XBOX + if(iX==IDS_TOOLTIPS_SELECTDEVICE) iX=-1; + if(iX==IDS_TOOLTIPS_CHANGEDEVICE) iX=-1; + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(iY==IDS_TOOLTIPS_VIEW_GAMERCARD) iY=-1; + if(iY==IDS_TOOLTIPS_VIEW_GAMERPROFILE) iY=-1; + +#endif +#endif + + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->SetTooltips(iA, iB, iX, iY, iLT, iRT, iLB, iRB, iLS, forceUpdate); +} + +void UIController::EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->EnableTooltip(tooltip,enable); +} + +void UIController::RefreshTooltips(unsigned int iPad) +{ + app.DebugPrintf(app.USER_SR, "UIController::RefreshTooltips is not implemented\n"); +} + +void UIController::AnimateKeyPress(int iPad, int iAction, bool bRepeat, bool bPressed, bool bReleased) +{ + EUIGroup group; + if(bPressed==false) + { + // only animating button press + return; + } + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + bool handled = false; + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->handleInput(iPad, iAction, bRepeat, bPressed, bReleased, handled); +} + +void UIController::OverrideSFX(int iPad, int iAction,bool bVal) +{ + EUIGroup group; + + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + bool handled = false; + if(m_groups[(int)group]->getTooltips()) m_groups[(int)group]->getTooltips()->overrideSFX(iPad, iAction,bVal); +} + +void UIController::PlayUISFX(ESoundEffect eSound) +{ + Minecraft::GetInstance()->soundEngine->playUI(eSound,1.0f,1.0f); +} + +void UIController::DisplayGamertag(unsigned int iPad, bool show) +{ + // The host decides whether these are on or off + if( app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags) == 0) + { + show = false; + } + EUIGroup group = (EUIGroup)(iPad+1); + if(m_groups[(int)group]->getHUD()) m_groups[(int)group]->getHUD()->ShowDisplayName(show); + + // Update TutorialPopup in Splitscreen if no container is displayed (to make sure the Popup does not overlap with the Gamertag!) + if(app.GetLocalPlayerCount() > 1 && m_groups[(int)group]->getTutorialPopup() && !m_groups[(int)group]->IsContainerMenuDisplayed()) + { + m_groups[(int)group]->getTutorialPopup()->UpdateTutorialPopup(); + } +} + +void UIController::SetSelectedItem(unsigned int iPad, const wstring &name) +{ + EUIGroup group; + + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + bool handled = false; + if(m_groups[(int)group]->getHUD()) m_groups[(int)group]->getHUD()->SetSelectedLabel(name); +} + +void UIController::UpdateSelectedItemPos(unsigned int iPad) +{ + app.DebugPrintf(app.USER_SR, "UIController::UpdateSelectedItemPos not implemented\n"); +} + +void UIController::HandleDLCMountingComplete() +{ + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + app.DebugPrintf("UIController::HandleDLCMountingComplete - m_groups[%d]\n",i); + m_groups[i]->HandleDLCMountingComplete(); + } +} + +void UIController::HandleDLCInstalled(int iPad) +{ + //app.DebugPrintf(app.USER_SR, "UIController::HandleDLCInstalled not implemented\n"); + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + m_groups[i]->HandleDLCInstalled(); + } +} + + +#ifdef _XBOX_ONE +void UIController::HandleDLCLicenseChange() +{ + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + app.DebugPrintf("UIController::HandleDLCLicenseChange - m_groups[%d]\n",i); + m_groups[i]->HandleDLCLicenseChange(); + } +} +#endif + +void UIController::HandleTMSDLCFileRetrieved(int iPad) +{ + app.DebugPrintf(app.USER_SR, "UIController::HandleTMSDLCFileRetrieved not implemented\n"); +} + +void UIController::HandleTMSBanFileRetrieved(int iPad) +{ + app.DebugPrintf(app.USER_SR, "UIController::HandleTMSBanFileRetrieved not implemented\n"); +} + +void UIController::HandleInventoryUpdated(int iPad) +{ + app.DebugPrintf(app.USER_SR, "UIController::HandleInventoryUpdated not implemented\n"); +} + +void UIController::HandleGameTick() +{ + tickInput(); + + for(unsigned int i = 0; i < eUIGroup_COUNT; ++i) + { + if(m_groups[i]->getHUD()) m_groups[i]->getHUD()->handleGameTick(); + } +} + +void UIController::SetTutorial(int iPad, Tutorial *tutorial) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTutorialPopup()) m_groups[(int)group]->getTutorialPopup()->SetTutorial(tutorial); +} + +void UIController::SetTutorialDescription(int iPad, TutorialPopupInfo *info) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + + if(m_groups[(int)group]->getTutorialPopup()) + { + // tutorial popup needs to know if a container menu is being displayed + m_groups[(int)group]->getTutorialPopup()->SetContainerMenuVisible(m_groups[(int)group]->IsContainerMenuDisplayed()); + m_groups[(int)group]->getTutorialPopup()->SetTutorialDescription(info); + } +} + +#ifndef _XBOX +void UIController::RemoveInteractSceneReference(int iPad, UIScene *scene) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTutorialPopup()) m_groups[(int)group]->getTutorialPopup()->RemoveInteractSceneReference(scene); +} +#endif + +void UIController::SetTutorialVisible(int iPad, bool visible) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + if(m_groups[(int)group]->getTutorialPopup()) m_groups[(int)group]->getTutorialPopup()->SetVisible(visible); +} + +bool UIController::IsTutorialVisible(int iPad) +{ + EUIGroup group; + if( app.GetGameStarted() ) + { + // If the game isn't running treat as user 0, otherwise map index directly from pad + if( ( iPad != 255 ) && ( iPad >= 0 ) ) group = (EUIGroup)(iPad+1); + else group = eUIGroup_Fullscreen; + } + else + { + group = eUIGroup_Fullscreen; + } + bool visible = false; + if(m_groups[(int)group]->getTutorialPopup()) visible = m_groups[(int)group]->getTutorialPopup()->IsVisible(); + return visible; +} + +void UIController::UpdatePlayerBasePositions() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + for( BYTE idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(pMinecraft->localplayers[idx] != NULL) + { + if(pMinecraft->localplayers[idx]->m_iScreenSection==C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + DisplayGamertag(idx,false); + } + else + { + DisplayGamertag(idx,true); + } + m_groups[idx+1]->SetViewportType((C4JRender::eViewportType)pMinecraft->localplayers[idx]->m_iScreenSection); + } + else + { + // 4J Stu - This is a legacy thing from our XUI implementation that we don't need + // Changing the viewport to fullscreen for users that no longer exist is SLOW + // This should probably be on all platforms, but I don't have time to test them all just now! +#ifndef __ORBIS__ + m_groups[idx+1]->SetViewportType(C4JRender::VIEWPORT_TYPE_FULLSCREEN); +#endif + DisplayGamertag(idx,false); + } + } +} + +void UIController::SetEmptyQuadrantLogo(int iSection) +{ + // 4J Stu - We shouldn't need to implement this +} + +void UIController::HideAllGameUIElements() +{ + // 4J Stu - We might not need to implement this + app.DebugPrintf(app.USER_SR, "UIController::HideAllGameUIElements not implemented\n"); +} + +void UIController::ShowOtherPlayersBaseScene(unsigned int iPad, bool show) +{ + // 4J Stu - We shouldn't need to implement this +} + +void UIController::ShowTrialTimer(bool show) +{ + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showTrialTimer(show); +} + +void UIController::SetTrialTimerLimitSecs(unsigned int uiSeconds) +{ + UIController::m_dwTrialTimerLimitSecs = uiSeconds; +} + +void UIController::UpdateTrialTimer(unsigned int iPad) +{ + WCHAR wcTime[20]; + + DWORD dwTimeTicks=(DWORD)app.getTrialTimer(); + + if(dwTimeTicks>m_dwTrialTimerLimitSecs) + { + dwTimeTicks=m_dwTrialTimerLimitSecs; + } + + dwTimeTicks=m_dwTrialTimerLimitSecs-dwTimeTicks; + +#ifndef _CONTENT_PACKAGE + if(true) +#else + // display the time - only if there's less than 3 minutes + if(dwTimeTicks<180) +#endif + { + int iMins=dwTimeTicks/60; + int iSeconds=dwTimeTicks%60; + swprintf( wcTime, 20, L"%d:%02d",iMins,iSeconds); + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->setTrialTimer(wcTime); + } + else + { + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->setTrialTimer(L""); + } + + // are we out of time? + if((dwTimeTicks==0)) + { + // Trial over + // bring up the pause menu to stop the trial over message box being called again? + if(!ui.GetMenuDisplayed( iPad ) ) + { + ui.NavigateToScene(iPad, eUIScene_PauseMenu, NULL, eUILayer_Scene); + + app.SetAction(iPad,eAppAction_TrialOver); + } + } +} + +void UIController::ReduceTrialTimerValue() +{ + DWORD dwTimeTicks=(int)app.getTrialTimer(); + + if(dwTimeTicks>m_dwTrialTimerLimitSecs) + { + dwTimeTicks=m_dwTrialTimerLimitSecs; + } + + m_dwTrialTimerLimitSecs-=dwTimeTicks; +} + +void UIController::ShowAutosaveCountdownTimer(bool show) +{ + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showTrialTimer(show); +} + +void UIController::UpdateAutosaveCountdownTimer(unsigned int uiSeconds) +{ +#if !(defined(_XBOX_ONE) || defined(__ORBIS__)) + WCHAR wcAutosaveCountdown[100]; + swprintf( wcAutosaveCountdown, 100, app.GetString(IDS_AUTOSAVE_COUNTDOWN),uiSeconds); + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->setTrialTimer(wcAutosaveCountdown); +#endif +} + +void UIController::ShowSavingMessage(unsigned int iPad, C4JStorage::ESavingMessage eVal) +{ + bool show = false; + switch(eVal) + { + case C4JStorage::ESavingMessage_None: + show = false; + break; + case C4JStorage::ESavingMessage_Short: + case C4JStorage::ESavingMessage_Long: + show = true; + break; + } + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showSaveIcon(show); +} + +void UIController::ShowPlayerDisplayname(bool show) +{ + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showPlayerDisplayName(show); +} + +void UIController::SetWinUserIndex(unsigned int iPad) +{ + m_winUserIndex = iPad; +} + +unsigned int UIController::GetWinUserIndex() +{ + return m_winUserIndex; +} + +void UIController::ShowUIDebugConsole(bool show) +{ +#ifndef _CONTENT_PACKAGE + + if(show) + { + m_uiDebugConsole = (UIComponent_DebugUIConsole *)m_groups[eUIGroup_Fullscreen]->addComponent(0, eUIComponent_DebugUIConsole, eUILayer_Debug); + } + else + { + m_groups[eUIGroup_Fullscreen]->removeComponent(eUIComponent_DebugUIConsole, eUILayer_Debug); + m_uiDebugConsole = NULL; + } +#endif +} + +void UIController::ShowUIDebugMarketingGuide(bool show) +{ +#ifndef _CONTENT_PACKAGE + + if(show) + { + m_uiDebugMarketingGuide = (UIComponent_DebugUIMarketingGuide *)m_groups[eUIGroup_Fullscreen]->addComponent(0, eUIComponent_DebugUIMarketingGuide, eUILayer_Debug); + } + else + { + m_groups[eUIGroup_Fullscreen]->removeComponent(eUIComponent_DebugUIMarketingGuide, eUILayer_Debug); + m_uiDebugMarketingGuide = NULL; + } +#endif +} + +void UIController::logDebugString(const string &text) +{ + if(m_uiDebugConsole) m_uiDebugConsole->addText(text); +} + +bool UIController::PressStartPlaying(unsigned int iPad) +{ + return m_iPressStartQuadrantsMask&(1<getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showPressStart(iPad, true); +} + +void UIController::HidePressStart() +{ + ClearPressStart(); + if(m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()) m_groups[(int)eUIGroup_Fullscreen]->getPressStartToPlay()->showPressStart(0, false); +} + +void UIController::ClearPressStart() +{ + m_iPressStartQuadrantsMask = 0; +} + +// 4J Stu - For the different StringTable classes. Should really fix the libraries. +#ifndef __PS3__ +C4JStorage::EMessageResult UIController::RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA,UINT uiOptionC, DWORD dwPad, + int( *Func)(LPVOID,int,const C4JStorage::EMessageResult),LPVOID lpParam, C4JStringTable *pStringTable, WCHAR *pwchFormatString,DWORD dwFocusButton, bool bIsError) +#else +C4JStorage::EMessageResult UIController::RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA,UINT uiOptionC, DWORD dwPad, + int( *Func)(LPVOID,int,const C4JStorage::EMessageResult),LPVOID lpParam, StringTable *pStringTable, WCHAR *pwchFormatString,DWORD dwFocusButton, bool bIsError) +#endif +{ + MessageBoxInfo param; + param.uiTitle = uiTitle; + param.uiText = uiText; + param.uiOptionA = uiOptionA; + param.uiOptionC = uiOptionC; + param.dwPad = dwPad; + param.Func = Func;\ + param.lpParam = lpParam; + param.pwchFormatString = pwchFormatString; + param.dwFocusButton = dwFocusButton; + + EUILayer layer = bIsError?eUILayer_Error:eUILayer_Alert; + + bool completed = false; + if(ui.IsReloadingSkin()) + { + // Queue this message box + QueuedMessageBoxData *queuedData = new QueuedMessageBoxData(); + queuedData->info = param; + queuedData->info.uiOptionA = new UINT[param.uiOptionC]; + memcpy(queuedData->info.uiOptionA, param.uiOptionA, param.uiOptionC * sizeof(UINT)); + queuedData->iPad = dwPad; + queuedData->layer = eUILayer_Error; // Ensures that these don't get wiped out by a CloseAllScenes call + m_queuedMessageBoxData.push_back(queuedData); + } + else + { + completed = ui.NavigateToScene(dwPad, eUIScene_MessageBox, ¶m, layer, eUIGroup_Fullscreen); + } + + if( completed ) + { + // This may happen if we had to queue the message box, or there was already a message box displaying and so the NavigateToScene returned false; + return C4JStorage::EMessage_Pending; + } + else + { + return C4JStorage::EMessage_Busy; + } +} + +C4JStorage::EMessageResult UIController::RequestUGCMessageBox(UINT title/* = -1 */, UINT message/* = -1 */, int iPad/* = -1*/, int( *Func)(LPVOID,int,const C4JStorage::EMessageResult)/* = NULL*/, LPVOID lpParam/* = NULL*/) +{ + // Default title / messages + if (title == -1) + { + title = IDS_FAILED_TO_CREATE_GAME_TITLE; + } + + if (message == -1) + { + message = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE; + } + + // Default pad to primary player + if (iPad == -1) iPad = ProfileManager.GetPrimaryPad(); + +#ifdef __ORBIS__ + // Show the vague UGC system message in addition to our message + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_UGC_RESTRICTION, iPad ); + return C4JStorage::EMessage_ResultAccept; +#elif defined(__PSVITA__) + ProfileManager.ShowSystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, iPad ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + return ui.RequestMessageBox( title, IDS_CHAT_RESTRICTION_UGC, uiIDA, 1, iPad, Func, lpParam, app.GetStringTable(), NULL, 0, false); +#else + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + return ui.RequestMessageBox( title, message, uiIDA, 1, iPad, Func, lpParam, app.GetStringTable(), NULL, 0, false); +#endif +} + +C4JStorage::EMessageResult UIController::RequestContentRestrictedMessageBox(UINT title/* = -1 */, UINT message/* = -1 */, int iPad/* = -1*/, int( *Func)(LPVOID,int,const C4JStorage::EMessageResult)/* = NULL*/, LPVOID lpParam/* = NULL*/) +{ + // Default title / messages + if (title == -1) + { + title = IDS_FAILED_TO_CREATE_GAME_TITLE; + } + + if (message == -1) + { +#if defined(_XBOX_ONE) || defined(_WINDOWS64) + // IDS_CONTENT_RESTRICTION doesn't exist on XB1 + message = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE; +#else + message = IDS_CONTENT_RESTRICTION; +#endif + } + + // Default pad to primary player + if (iPad == -1) iPad = ProfileManager.GetPrimaryPad(); + +#ifdef __ORBIS__ + // Show the vague UGC system message in addition to our message + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_UGC_RESTRICTION, iPad ); + return C4JStorage::EMessage_ResultAccept; +#elif defined(__PSVITA__) + ProfileManager.ShowSystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_AGE_RESTRICTION, iPad ); + return C4JStorage::EMessage_ResultAccept; +#else + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + return ui.RequestMessageBox( title, message, uiIDA, 1, iPad, Func, lpParam, app.GetStringTable(), NULL, 0, false); +#endif +} + +void UIController::setFontCachingCalculationBuffer(int length) +{ + /* 4J-JEV: As described in an email from Sean. + If your `optional_temp_buffer` is NULL, Iggy will allocate the temp + buffer on the stack during Iggy draw calls. The size of the buffer it + will allocate is 16 bytes times `max_chars` in 32-bit, and 24 bytes + times `max_chars` in 64-bit. If the stack of the thread making the + draw call is not large enough, Iggy will crash or otherwise behave + incorrectly. + */ +#if defined __ORBIS__ || defined _DURANGO || defined _WIN64 + static const int CHAR_SIZE = 24; +#else + static const int CHAR_SIZE = 16; +#endif + + if (m_tempBuffer != NULL) delete [] m_tempBuffer; + if (length<0) + { + if (m_defaultBuffer == NULL) m_defaultBuffer = new char[CHAR_SIZE*5000]; + IggySetFontCachingCalculationBuffer(5000, m_defaultBuffer, CHAR_SIZE*5000); + } + else + { + m_tempBuffer = new char[CHAR_SIZE*length]; + IggySetFontCachingCalculationBuffer(length, m_tempBuffer, CHAR_SIZE*length); + } +} + +// Returns the first scene of given type if it exists, NULL otherwise +UIScene *UIController::FindScene(EUIScene sceneType) +{ + UIScene *pScene = NULL; + + for (int i = 0; i < eUIGroup_COUNT; i++) + { + pScene = m_groups[i]->FindScene(sceneType); +#ifdef __PS3__ + if (pScene != NULL) return pScene; +#else + if (pScene != nullptr) return pScene; +#endif + } + + return pScene; +} + +#ifdef __PSVITA__ + +void UIController::TouchBoxAdd(UIControl *pControl,UIScene *pUIScene) +{ + EUIGroup eUIGroup=pUIScene->GetParentLayerGroup(); + EUILayer eUILayer=pUIScene->GetParentLayer()->m_iLayer; + EUIScene eUIscene=pUIScene->getSceneType(); + + TouchBoxAdd(pControl,eUIGroup,eUILayer,eUIscene, pUIScene->GetMainPanel()); +} + +void UIController::TouchBoxAdd(UIControl *pControl,EUIGroup eUIGroup,EUILayer eUILayer,EUIScene eUIscene, UIControl *pMainPanelControl) +{ + UIELEMENT *puiElement = new UIELEMENT; + puiElement->pControl = pControl; + + S32 iControlWidth = pControl->getWidth(); + S32 iControlHeight = pControl->getHeight(); + S32 iMainPanelOffsetX = 0; + S32 iMainPanelOffsetY= 0; + + // 4J-TomK add main panel offset if controls do not live in the root scene + if(pMainPanelControl) + { + iMainPanelOffsetX = pMainPanelControl->getXPos(); + iMainPanelOffsetY = pMainPanelControl->getYPos(); + } + + // 4J-TomK override control width / height where needed + if(puiElement->pControl->getControlType() == UIControl::eSlider) + { + // Sliders are never scaled but masked, so we have to get the real width from AS + UIControl_Slider *pSlider = (UIControl_Slider *)puiElement->pControl; + iControlWidth = pSlider->GetRealWidth(); + } + else if(puiElement->pControl->getControlType() == UIControl::eTexturePackList) + { + // The origin of the TexturePackList is NOT in the top left corner but where the slot area starts. therefore we need the height of the slot area itself. + UIControl_TexturePackList *pTexturePackList = (UIControl_TexturePackList *)puiElement->pControl; + iControlHeight = pTexturePackList->GetRealHeight(); + } + else if(puiElement->pControl->getControlType() == UIControl::eDynamicLabel) + { + // The height and width of this control changes per how to play page + UIControl_DynamicLabel *pDynamicLabel = (UIControl_DynamicLabel *)puiElement->pControl; + iControlWidth = pDynamicLabel->GetRealWidth(); + iControlHeight = pDynamicLabel->GetRealHeight(); + } + else if(puiElement->pControl->getControlType() == UIControl::eHTMLLabel) + { + // The height and width of this control changes per how to play page + UIControl_HTMLLabel *pHtmlLabel = (UIControl_HTMLLabel *)puiElement->pControl; + iControlWidth = pHtmlLabel->GetRealWidth(); + iControlHeight = pHtmlLabel->GetRealHeight(); + } + + puiElement->x1=(S32)((float)pControl->getXPos() + (float)iMainPanelOffsetX); + puiElement->y1=(S32)((float)pControl->getYPos() + (float)iMainPanelOffsetY); + puiElement->x2=(S32)(((float)pControl->getXPos() + (float)iControlWidth + (float)iMainPanelOffsetX)); + puiElement->y2=(S32)(((float)pControl->getYPos() + (float)iControlHeight + (float)iMainPanelOffsetY)); + + if(puiElement->pControl->getControlType() == UIControl::eNoControl) + { + app.DebugPrintf("NO CONTROL!"); + } + + if(puiElement->x1 == puiElement->x2 || puiElement->y1 == puiElement->y2) + { + app.DebugPrintf("NOT adding touchbox %d,%d,%d,%d\n",puiElement->x1,puiElement->y1,puiElement->x2,puiElement->y2); + } + else + { + app.DebugPrintf("Adding touchbox %d,%d,%d,%d\n",puiElement->x1,puiElement->y1,puiElement->x2,puiElement->y2); + m_TouchBoxes[eUIGroup][eUILayer][eUIscene].push_back(puiElement); + } +} + +void UIController::TouchBoxRebuild(UIScene *pUIScene) +{ + EUIGroup eUIGroup=pUIScene->GetParentLayerGroup(); + EUILayer eUILayer=pUIScene->GetParentLayer()->m_iLayer; + EUIScene eUIscene=pUIScene->getSceneType(); + + // if we delete an element, it's possible that the scene has re-arranged all the elements, so we need to rebuild the boxes + ui.TouchBoxesClear(pUIScene); + + // rebuild boxes + AUTO_VAR(itEnd, pUIScene->GetControls()->end()); + for (AUTO_VAR(it, pUIScene->GetControls()->begin()); it != itEnd; it++) + { + UIControl *control=(UIControl *)*it; + + if(control->getControlType() == UIControl::eButton || + control->getControlType() == UIControl::eSlider || + control->getControlType() == UIControl::eCheckBox || + control->getControlType() == UIControl::eTexturePackList || + control->getControlType() == UIControl::eButtonList || + control->getControlType() == UIControl::eTextInput || + control->getControlType() == UIControl::eDynamicLabel || + control->getControlType() == UIControl::eHTMLLabel || + control->getControlType() == UIControl::eLeaderboardList || + control->getControlType() == UIControl::eTouchControl) + { + // 4J-TomK update the control (it might have been moved by flash / AS) + control->UpdateControl(); + + ui.TouchBoxAdd(control,eUIGroup,eUILayer,eUIscene, pUIScene->GetMainPanel()); + } + } +} + +void UIController::TouchBoxesClear(UIScene *pUIScene) +{ + EUIGroup eUIGroup=pUIScene->GetParentLayerGroup(); + EUILayer eUILayer=pUIScene->GetParentLayer()->m_iLayer; + EUIScene eUIscene=pUIScene->getSceneType(); + + AUTO_VAR(itEnd, m_TouchBoxes[eUIGroup][eUILayer][eUIscene].end()); + for (AUTO_VAR(it, m_TouchBoxes[eUIGroup][eUILayer][eUIscene].begin()); it != itEnd; it++) + { + UIELEMENT *element=(UIELEMENT *)*it; + delete element; + } + m_TouchBoxes[eUIGroup][eUILayer][eUIscene].clear(); +} + +bool UIController::TouchBoxHit(UIScene *pUIScene,S32 x, S32 y) +{ + EUIGroup eUIGroup=pUIScene->GetParentLayerGroup(); + EUILayer eUILayer=pUIScene->GetParentLayer()->m_iLayer; + EUIScene eUIscene=pUIScene->getSceneType(); + + // 4J-TomK let's do the transformation from touch resolution to screen resolution here, so our touchbox values always are in screen resolution! + x *= (m_fScreenWidth/1920.0f); + y *= (m_fScreenHeight/1080.0f); + + if(m_TouchBoxes[eUIGroup][eUILayer][eUIscene].size()>0) + { + AUTO_VAR(itEnd, m_TouchBoxes[eUIGroup][eUILayer][eUIscene].end()); + for (AUTO_VAR(it, m_TouchBoxes[eUIGroup][eUILayer][eUIscene].begin()); it != itEnd; it++) + { + UIELEMENT *element=(UIELEMENT *)*it; + if(element->pControl->getHidden() == false && element->pControl->getVisible()) // ignore removed controls + { + if((x>=element->x1) &&(x<=element->x2) && (y>=element->y1) && (y<=element->y2)) + { + if(!m_bTouchscreenPressed) + { + app.DebugPrintf("SET m_ActiveUIElement (Layer: %i) at x = %i y = %i\n", (int)eUILayer, (int)x, (int)y); + m_ActiveUIElement = element; + } + // remember the currently highlighted element + m_HighlightedUIElement = element; + + return true; + } + } + } + } + + //app.DebugPrintf("MISS at x = %i y = %i\n", (int)x, (int)y); + m_HighlightedUIElement = NULL; + return false; +} + +// +// Handle Touch Input +// +void UIController::HandleTouchInput(unsigned int iPad, unsigned int key, bool bPressed, bool bRepeat, bool bReleased) +{ + // no input? no handling! + if(!bPressed && !bRepeat && !bReleased) + { + // override for instand repeat without delay! + if(m_bTouchscreenPressed && m_ActiveUIElement && ( + m_ActiveUIElement->pControl->getControlType() == UIControl::eSlider || + m_ActiveUIElement->pControl->getControlType() == UIControl::eButtonList || + m_ActiveUIElement->pControl->getControlType() == UIControl::eTexturePackList || + m_ActiveUIElement->pControl->getControlType() == UIControl::eDynamicLabel || + m_ActiveUIElement->pControl->getControlType() == UIControl::eHTMLLabel || + m_ActiveUIElement->pControl->getControlType() == UIControl::eLeaderboardList || + m_ActiveUIElement->pControl->getControlType() == UIControl::eTouchControl)) + bRepeat = true; // the above controls need to be controllable without having the finger over them + else + return; + } + + SceTouchData* pTouchData = InputManager.GetTouchPadData(iPad,false); + S32 x = pTouchData->report[0].x * (m_fScreenWidth/1920.0f); + S32 y = pTouchData->report[0].y * (m_fScreenHeight/1080.0f); + + if(bPressed && !bRepeat && !bReleased) // PRESSED HANDLING + { + app.DebugPrintf("touch input pressed\n"); + switch(m_ActiveUIElement->pControl->getControlType()) + { + case UIControl::eButton: + // set focus + UIControl_Button *pButton=(UIControl_Button *)m_ActiveUIElement->pControl; + pButton->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + // override bPressed to false. we only want the button to trigger on touch release! + bPressed = false; + break; + case UIControl::eSlider: + // set focus + UIControl_Slider *pSlider=(UIControl_Slider *)m_ActiveUIElement->pControl; + pSlider->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + break; + case UIControl::eCheckBox: + // set focus + UIControl_CheckBox *pCheckbox=(UIControl_CheckBox *)m_ActiveUIElement->pControl; + pCheckbox->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + // override bPressed. we only want the checkbox to trigger on touch release! + bPressed = false; + break; + case UIControl::eButtonList: + // set focus to list + UIControl_ButtonList *pButtonList=(UIControl_ButtonList *)m_ActiveUIElement->pControl; + //pButtonList->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + // tell list where we tapped it so it can set focus to the correct button + pButtonList->SetTouchFocus((float)x, (float)y, false); + // override bPressed. we only want the ButtonList to trigger on touch release! + bPressed = false; + break; + case UIControl::eTexturePackList: + // set focus to list + UIControl_TexturePackList *pTexturePackList=(UIControl_TexturePackList *)m_ActiveUIElement->pControl; + pTexturePackList->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + // tell list where we tapped it so it can set focus to the correct texture pack + pTexturePackList->SetTouchFocus((float)x - (float)m_ActiveUIElement->x1, (float)y - (float)m_ActiveUIElement->y1, false); + // override bPressed. we only want the TexturePack List to trigger on touch release! + bPressed = false; + break; + case UIControl::eTextInput: + // set focus + UIControl_TextInput *pTextInput=(UIControl_TextInput *)m_ActiveUIElement->pControl; + pTextInput->getParentScene()->SetFocusToElement(m_ActiveUIElement->pControl->getId()); + // override bPressed to false. we only want the textinput to trigger on touch release! + bPressed = false; + break; + case UIControl::eDynamicLabel: + // handle dynamic label scrolling + UIControl_DynamicLabel *pDynamicLabel=(UIControl_DynamicLabel *)m_ActiveUIElement->pControl; + pDynamicLabel->TouchScroll(y, true); + // override bPressed to false + bPressed = false; + break; + case UIControl::eHTMLLabel: + // handle dynamic label scrolling + UIControl_HTMLLabel *pHtmlLabel=(UIControl_HTMLLabel *)m_ActiveUIElement->pControl; + pHtmlLabel->TouchScroll(y, true); + // override bPressed to false + bPressed = false; + break; + case UIControl::eLeaderboardList: + // set focus to list + UIControl_LeaderboardList *pLeaderboardList=(UIControl_LeaderboardList *)m_ActiveUIElement->pControl; + // tell list where we tapped it so it can set focus to the correct button + pLeaderboardList->SetTouchFocus((float)x, (float)y, false); + // override bPressed. we only want the ButtonList to trigger on touch release! + bPressed = false; + break; + case UIControl::eTouchControl: + // pass on touch input to relevant parent scene so we can handle it there! + m_ActiveUIElement->pControl->getParentScene()->handleTouchInput(iPad, x, y, m_ActiveUIElement->pControl->getId(), bPressed, bRepeat, bReleased); + // override bPressed to false + bPressed = false; + break; + default: + app.DebugPrintf("PRESSED - UNHANDLED UI ELEMENT\n"); + break; + } + } + else if(bRepeat) // REPEAT HANDLING + { + switch(m_ActiveUIElement->pControl->getControlType()) + { + case UIControl::eButton: + /* no action */ + break; + case UIControl::eSlider: + // handle slider movement + UIControl_Slider *pSlider=(UIControl_Slider *)m_ActiveUIElement->pControl; + float fNewSliderPos = ((float)x - (float)m_ActiveUIElement->x1) / (float)pSlider->GetRealWidth(); + pSlider->SetSliderTouchPos(fNewSliderPos); + break; + case UIControl::eCheckBox: + /* no action */ + bRepeat = false; + bPressed = false; + break; + case UIControl::eButtonList: + // handle button list scrolling + UIControl_ButtonList *pButtonList=(UIControl_ButtonList *)m_ActiveUIElement->pControl; + pButtonList->SetTouchFocus((float)x, (float)y, true); + break; + case UIControl::eTexturePackList: + // handle texturepack list scrolling + UIControl_TexturePackList *pTexturePackList=(UIControl_TexturePackList *)m_ActiveUIElement->pControl; + pTexturePackList->SetTouchFocus((float)x - (float)m_ActiveUIElement->x1, (float)y - (float)m_ActiveUIElement->y1, true); + break; + case UIControl::eTextInput: + /* no action */ + bRepeat = false; + bPressed = false; + break; + case UIControl::eDynamicLabel: + // handle dynamic label scrolling + UIControl_DynamicLabel *pDynamicLabel=(UIControl_DynamicLabel *)m_ActiveUIElement->pControl; + pDynamicLabel->TouchScroll(y, true); + // override bPressed & bRepeat to false + bPressed = false; + bRepeat = false; + break; + case UIControl::eHTMLLabel: + // handle dynamic label scrolling + UIControl_HTMLLabel *pHtmlLabel=(UIControl_HTMLLabel *)m_ActiveUIElement->pControl; + pHtmlLabel->TouchScroll(y, true); + // override bPressed & bRepeat to false + bPressed = false; + bRepeat = false; + break; + case UIControl::eLeaderboardList: + // handle button list scrolling + UIControl_LeaderboardList *pLeaderboardList=(UIControl_LeaderboardList *)m_ActiveUIElement->pControl; + pLeaderboardList->SetTouchFocus((float)x, (float)y, true); + break; + case UIControl::eTouchControl: + // override bPressed to false + bPressed = false; + // pass on touch input to relevant parent scene so we can handle it there! + m_ActiveUIElement->pControl->getParentScene()->handleTouchInput(iPad, x, y, m_ActiveUIElement->pControl->getId(), bPressed, bRepeat, bReleased); + // override bRepeat to false + bRepeat = false; + break; + default: + app.DebugPrintf("REPEAT - UNHANDLED UI ELEMENT\n"); + break; + } + } + if(bReleased) // RELEASED HANDLING + { + app.DebugPrintf("touch input released\n"); + switch(m_ActiveUIElement->pControl->getControlType()) + { + case UIControl::eButton: + // trigger button on release (ONLY if the finger is still on it!) + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + bPressed = true; + break; + case UIControl::eSlider: + /* no action */ + break; + case UIControl::eCheckBox: + // trigger checkbox on release (ONLY if the finger is still on it!) + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + { + UIControl_CheckBox *pCheckbox=(UIControl_CheckBox *)m_ActiveUIElement->pControl; + pCheckbox->TouchSetCheckbox(!pCheckbox->IsChecked()); + } + bReleased = false; + break; + case UIControl::eButtonList: + // trigger buttonlist on release (ONLY if the finger is still on it!) + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + { + UIControl_ButtonList *pButtonList=(UIControl_ButtonList *)m_ActiveUIElement->pControl; + if(pButtonList->CanTouchTrigger(x,y)) + bPressed = true; + } + break; + case UIControl::eTexturePackList: + // trigger texturepack list on release (ONLY if the finger is still on it!) + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + { + UIControl_TexturePackList *pTexturePackList=(UIControl_TexturePackList *)m_ActiveUIElement->pControl; + if(pTexturePackList->CanTouchTrigger((float)x - (float)m_ActiveUIElement->x1, (float)y - (float)m_ActiveUIElement->y1)) + bPressed = true; + } + break; + case UIControl::eTextInput: + // trigger TextInput on release (ONLY if the finger is still on it!) + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + bPressed = true; + break; + case UIControl::eDynamicLabel: + // handle dynamic label scrolling + UIControl_DynamicLabel *pDynamicLabel=(UIControl_DynamicLabel *)m_ActiveUIElement->pControl; + pDynamicLabel->TouchScroll(y, false); + break; + case UIControl::eHTMLLabel: + // handle dynamic label scrolling + UIControl_HTMLLabel *pHtmlLabel=(UIControl_HTMLLabel *)m_ActiveUIElement->pControl; + pHtmlLabel->TouchScroll(y, false); + break; + case UIControl::eLeaderboardList: + /* no action */ + break; + case UIControl::eTouchControl: + // trigger only if touch is released over the same component! + if(m_HighlightedUIElement && m_ActiveUIElement->pControl == m_HighlightedUIElement->pControl) + { + // pass on touch input to relevant parent scene so we can handle it there! + m_ActiveUIElement->pControl->getParentScene()->handleTouchInput(iPad, x, y, m_ActiveUIElement->pControl->getId(), bPressed, bRepeat, bReleased); + } + // override bReleased to false + bReleased = false; + break; + default: + app.DebugPrintf("RELEASED - UNHANDLED UI ELEMENT\n"); + break; + } + } + + // only proceed if there's input to be handled + if(bPressed || bRepeat || bReleased) + { + SendTouchInput(iPad, key, bPressed, bRepeat, bReleased); + } +} + +void UIController::SendTouchInput(unsigned int iPad, unsigned int key, bool bPressed, bool bRepeat, bool bReleased) +{ + bool handled = false; + + // Send the key to the fullscreen group first + m_groups[(int)eUIGroup_Fullscreen]->handleInput(iPad, key, bRepeat, bPressed, bReleased, handled); + if(!handled) + { + // If it's not been handled yet, then pass the event onto the players specific group + m_groups[(iPad+1)]->handleInput(iPad, key, bRepeat, bPressed, bReleased, handled); + } +} + + +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h new file mode 100644 index 0000000..ef064f8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIController.h @@ -0,0 +1,369 @@ +#pragma once +using namespace std; +#include "IUIController.h" +#include "UIEnums.h" +#include "UIGroup.h" + +class UIAbstractBitmapFont; +class UIBitmapFont; +class UITTFFont; +class UIComponent_DebugUIConsole; +class UIComponent_DebugUIMarketingGuide; +class UIControl; + +// Base class for all shared functions between UIControllers +class UIController : public IUIController +{ +public: + static __int64 iggyAllocCount; + + // MGH - added to prevent crash loading Iggy movies while the skins were being reloaded + static CRITICAL_SECTION ms_reloadSkinCS; + static bool ms_bReloadSkinCSInitialised; + +protected: + UIComponent_DebugUIConsole *m_uiDebugConsole; + UIComponent_DebugUIMarketingGuide *m_uiDebugMarketingGuide; + +private: + CRITICAL_SECTION m_navigationLock; + + static const int UI_REPEAT_KEY_DELAY_MS = 300; // How long from press until the first repeat + static const int UI_REPEAT_KEY_REPEAT_RATE_MS = 100; // How long in between repeats + DWORD m_actionRepeatTimer[XUSER_MAX_COUNT][ACTION_MAX_MENU+1]; + + float m_fScreenWidth; + float m_fScreenHeight; + bool m_bScreenWidthSetup; + + S32 m_tileOriginX, m_tileOriginY; + + UIAbstractBitmapFont *m_mcBitmapFont; + UITTFFont *m_mcTTFFont; + UIBitmapFont *m_moj7, *m_moj11; + + // 4J-PB - ui element type for PSVita touch control +#ifdef __PSVITA__ + + typedef struct + { + UIControl *pControl; + S32 x1,y1,x2,y2; + } + UIELEMENT; + // E3 - Fine for now, but we need to make this better! + vector m_TouchBoxes[eUIGroup_COUNT][eUILayer_COUNT][eUIScene_COUNT]; + bool m_bTouchscreenPressed; +#endif + // 4J Stu - These should be in the order that they reference each other (i.e. they can only reference one with a lower value in the enum) + enum ELibraries + { + eLibrary_Platform, + eLibrary_GraphicsDefault, + eLibrary_GraphicsHUD, + eLibrary_GraphicsInGame, + eLibrary_GraphicsTooltips, + eLibrary_GraphicsLabels, + eLibrary_Labels, + eLibrary_InGame, + eLibrary_HUD, + eLibrary_Tooltips, + eLibrary_Default, + +#if ( defined(_WINDOWS64) ) + // 4J Stu - Load the 720/480 skins so that we have something to fallback on during development +#ifndef _FINAL_BUILD + eLibraryFallback_Platform, + eLibraryFallback_GraphicsDefault, + eLibraryFallback_GraphicsHUD, + eLibraryFallback_GraphicsInGame, + eLibraryFallback_GraphicsTooltips, + eLibraryFallback_GraphicsLabels, + eLibraryFallback_Labels, + eLibraryFallback_InGame, + eLibraryFallback_HUD, + eLibraryFallback_Tooltips, + eLibraryFallback_Default, +#endif +#endif + + eLibrary_Count, + }; + + IggyLibrary m_iggyLibraries[eLibrary_Count]; + +protected: + GDrawFunctions *gdraw_funcs; + +private: + HIGGYEXP iggy_explorer; + HIGGYPERFMON iggy_perfmon; + bool m_iggyPerfmonEnabled; + + bool m_bMenuDisplayed[XUSER_MAX_COUNT]; // track each players menu displayed + bool m_bMenuToBeClosed[XUSER_MAX_COUNT]; // actioned at the end of the game loop + int m_iCountDown[XUSER_MAX_COUNT]; // ticks to block input + + bool m_bCloseAllScenes[eUIGroup_COUNT]; + + int m_iPressStartQuadrantsMask; + + C4JRender::eViewportType m_currentRenderViewport; + bool m_bCustomRenderPosition; + + static DWORD m_dwTrialTimerLimitSecs; + + unordered_map m_substitutionTextures; + + typedef struct _CachedMovieData + { + byteArray m_ba; + __int64 m_expiry; + } CachedMovieData; + unordered_map m_cachedMovieData; + + typedef struct _QueuedMessageBoxData + { + MessageBoxInfo info; + int iPad; + EUILayer layer; + } QueuedMessageBoxData; + vector m_queuedMessageBoxData; + + unsigned int m_winUserIndex; + //bool m_bSysUIShowing; + bool m_bSystemUIShowing; + C4JThread *m_reloadSkinThread; + bool m_navigateToHomeOnReload; + int m_accumulatedTicks; + + D3D11_RECT m_customRenderingClearRect; + + unordered_map m_registeredCallbackScenes; // A collection of scenes and unique id's that are used in async callbacks so we can safely handle when they get destroyed + CRITICAL_SECTION m_registeredCallbackScenesCS;; + +public: + UIController(); +#ifdef __PSVITA__ + void TouchBoxAdd(UIControl *pControl,UIScene *pUIScene); + bool TouchBoxHit(UIScene *pUIScene,S32 x, S32 y); + void TouchBoxesClear(UIScene *pUIScene); + void TouchBoxRebuild(UIScene *pUIScene); + + void HandleTouchInput(unsigned int iPad, unsigned int key, bool bPressed, bool bRepeat, bool bReleased); + void SendTouchInput(unsigned int iPad, unsigned int key, bool bPressed, bool bRepeat, bool bReleased); + + private: + void TouchBoxAdd(UIControl *pControl,EUIGroup eUIGroup,EUILayer eUILayer,EUIScene eUIscene, UIControl *pMainPanelControl); + UIELEMENT *m_ActiveUIElement; + UIELEMENT *m_HighlightedUIElement; +#endif + +protected: + UIGroup *m_groups[eUIGroup_COUNT]; + +public: + void showComponent(int iPad, EUIScene scene, EUILayer layer, EUIGroup group, bool show) + { + m_groups[group]->showComponent(iPad, scene, layer, show); + } + + void removeComponent(EUIScene scene, EUILayer layer, EUIGroup group) + { + m_groups[group]->removeComponent(scene, layer); + } + +protected: + // Should be called from the platforms init function + void preInit(S32 width, S32 height); + void postInit(); + + +public: + CRITICAL_SECTION m_Allocatorlock; + void SetupFont(); +public: + // TICKING + virtual void tick(); + +private: + void loadSkins(); + IggyLibrary loadSkin(const wstring &skinPath, const wstring &skinName); + +public: + void ReloadSkin(); + virtual void StartReloadSkinThread(); + virtual bool IsReloadingSkin(); + virtual bool IsExpectingOrReloadingSkin(); + virtual void CleanUpSkinReload(); + +private: + static int reloadSkinThreadProc(void* lpParam); + +public: + byteArray getMovieData(const wstring &filename); + + // INPUT +private: + void tickInput(); + void handleInput(); + void handleKeyPress(unsigned int iPad, unsigned int key); + +protected: + static rrbool RADLINK ExternalFunctionCallback( void * user_callback_data , Iggy * player , IggyExternalFunctionCallUTF16 * call ); + +public: + // RENDERING + float getScreenWidth() { return m_fScreenWidth; } + float getScreenHeight() { return m_fScreenHeight; } + + virtual void render() = 0; + void getRenderDimensions(C4JRender::eViewportType viewport, S32 &width, S32 &height); + void setupRenderPosition(C4JRender::eViewportType viewport); + void setupRenderPosition(S32 xOrigin, S32 yOrigin); + + void SetSysUIShowing(bool bVal); + static void SetSystemUIShowing(LPVOID lpParam,bool bVal); + +protected: + virtual void setTileOrigin(S32 xPos, S32 yPos) = 0; + +public: + + virtual CustomDrawData *setupCustomDraw(UIScene *scene, IggyCustomDrawCallbackRegion *region) = 0; + virtual CustomDrawData *calculateCustomDraw(IggyCustomDrawCallbackRegion *region) = 0; + virtual void endCustomDraw(IggyCustomDrawCallbackRegion *region) = 0; +protected: + // Should be called from the platforms render function + void renderScenes(); + +public: + virtual void beginIggyCustomDraw4J(IggyCustomDrawCallbackRegion *region, CustomDrawData *customDrawRegion) = 0; + void setupCustomDrawGameState(); + void endCustomDrawGameState(); + void setupCustomDrawMatrices(UIScene *scene, CustomDrawData *customDrawRegion); + void setupCustomDrawGameStateAndMatrices(UIScene *scene, CustomDrawData *customDrawRegion); + void endCustomDrawMatrices(); + void endCustomDrawGameStateAndMatrices(); + +protected: + + static void RADLINK CustomDrawCallback(void *user_callback_data, Iggy *player, IggyCustomDrawCallbackRegion *Region); + static GDrawTexture * RADLINK TextureSubstitutionCreateCallback( void * user_callback_data , IggyUTF16 * texture_name , S32 * width , S32 * height , void **destroy_callback_data ); + static void RADLINK TextureSubstitutionDestroyCallback( void * user_callback_data , void * destroy_callback_data , GDrawTexture * handle ); + + virtual GDrawTexture *getSubstitutionTexture(int textureId) { return NULL; } + virtual void destroySubstitutionTexture(void *destroyCallBackData, GDrawTexture *handle) {} + +public: + void registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength); + void unregisterSubstitutionTexture(const wstring &textureName, bool deleteData); + +public: + // NAVIGATION + bool NavigateToScene(int iPad, EUIScene scene, void *initData = NULL, EUILayer layer = eUILayer_Scene, EUIGroup group = eUIGroup_PAD); + bool NavigateBack(int iPad, bool forceUsePad = false, EUIScene eScene = eUIScene_COUNT, EUILayer eLayer = eUILayer_COUNT); + void NavigateToHomeMenu(); + UIScene *GetTopScene(int iPad, EUILayer layer = eUILayer_Scene, EUIGroup group = eUIGroup_PAD); + + size_t RegisterForCallbackId(UIScene *scene); + void UnregisterCallbackId(size_t id); + UIScene *GetSceneFromCallbackId(size_t id); + void EnterCallbackIdCriticalSection(); + void LeaveCallbackIdCriticalSection(); + +private: + void setFullscreenMenuDisplayed(bool displayed); + +public: + void CloseAllPlayersScenes(); + void CloseUIScenes(int iPad, bool forceIPad = false); + + virtual bool IsPauseMenuDisplayed(int iPad); + virtual bool IsContainerMenuDisplayed(int iPad); + virtual bool IsIgnorePlayerJoinMenuDisplayed(int iPad); + virtual bool IsIgnoreAutosaveMenuDisplayed(int iPad); + virtual void SetIgnoreAutosaveMenuDisplayed(int iPad, bool displayed); + virtual bool IsSceneInStack(int iPad, EUIScene eScene); + bool GetMenuDisplayed(int iPad); + void SetMenuDisplayed(int iPad,bool bVal); + virtual void CheckMenuDisplayed(); + void AnimateKeyPress(int iPad, int iAction, bool bRepeat, bool bPressed, bool bReleased); + void OverrideSFX(int iPad, int iAction,bool bVal); + + // TOOLTIPS + virtual void SetTooltipText( unsigned int iPad, unsigned int tooltip, int iTextID ); + virtual void SetEnableTooltips( unsigned int iPad, BOOL bVal ); + virtual void ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ); + virtual void SetTooltips( unsigned int iPad, int iA, int iB=-1, int iX=-1, int iY=-1 , int iLT=-1, int iRT=-1, int iLB=-1, int iRB=-1, int iLS=-1, bool forceUpdate = false); + virtual void EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ); + virtual void RefreshTooltips(unsigned int iPad); + + virtual void PlayUISFX(ESoundEffect eSound); + + virtual void DisplayGamertag(unsigned int iPad, bool show); + virtual void SetSelectedItem(unsigned int iPad, const wstring &name); + virtual void UpdateSelectedItemPos(unsigned int iPad); + + virtual void HandleDLCMountingComplete(); + virtual void HandleDLCInstalled(int iPad); +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + virtual void HandleTMSDLCFileRetrieved(int iPad); + virtual void HandleTMSBanFileRetrieved(int iPad); + virtual void HandleInventoryUpdated(int iPad); + virtual void HandleGameTick(); + + virtual void SetTutorial(int iPad, Tutorial *tutorial); + virtual void SetTutorialDescription(int iPad, TutorialPopupInfo *info); + virtual void RemoveInteractSceneReference(int iPad, UIScene *scene); + virtual void SetTutorialVisible(int iPad, bool visible); + virtual bool IsTutorialVisible(int iPad); + + virtual void UpdatePlayerBasePositions(); + virtual void SetEmptyQuadrantLogo(int iSection); + virtual void HideAllGameUIElements(); + virtual void ShowOtherPlayersBaseScene(unsigned int iPad, bool show); + + virtual void ShowTrialTimer(bool show); + virtual void SetTrialTimerLimitSecs(unsigned int uiSeconds); + virtual void UpdateTrialTimer(unsigned int iPad); + virtual void ReduceTrialTimerValue(); + + virtual void ShowAutosaveCountdownTimer(bool show); + virtual void UpdateAutosaveCountdownTimer(unsigned int uiSeconds); + virtual void ShowSavingMessage(unsigned int iPad, C4JStorage::ESavingMessage eVal); + + virtual void ShowPlayerDisplayname(bool show); + virtual bool PressStartPlaying(unsigned int iPad); + virtual void ShowPressStart(unsigned int iPad); + virtual void HidePressStart(); + void ClearPressStart(); + + // 4J Stu - Only because of the different StringTable type, should really fix the libraries +#ifndef __PS3__ + virtual C4JStorage::EMessageResult RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA,UINT uiOptionC, DWORD dwPad=XUSER_INDEX_ANY, + int( *Func)(LPVOID,int,const C4JStorage::EMessageResult)=NULL,LPVOID lpParam=NULL, C4JStringTable *pStringTable=NULL, WCHAR *pwchFormatString=NULL,DWORD dwFocusButton=0, bool bIsError = true); +#else + virtual C4JStorage::EMessageResult RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA,UINT uiOptionC, DWORD dwPad=XUSER_INDEX_ANY, + int( *Func)(LPVOID,int,const C4JStorage::EMessageResult)=NULL,LPVOID lpParam=NULL, StringTable *pStringTable=NULL, WCHAR *pwchFormatString=NULL,DWORD dwFocusButton=0, bool bIsError = true); +#endif + + C4JStorage::EMessageResult RequestUGCMessageBox(UINT title = -1, UINT message = -1, int iPad = -1, int( *Func)(LPVOID,int,const C4JStorage::EMessageResult) = NULL, LPVOID lpParam = NULL); + C4JStorage::EMessageResult RequestContentRestrictedMessageBox(UINT title = -1, UINT message = -1, int iPad = -1, int( *Func)(LPVOID,int,const C4JStorage::EMessageResult) = NULL, LPVOID lpParam = NULL); + + virtual void SetWinUserIndex(unsigned int iPad); + unsigned int GetWinUserIndex(); + + virtual void ShowUIDebugConsole(bool show); + virtual void ShowUIDebugMarketingGuide(bool show); + void logDebugString(const string &text); + UIScene* FindScene(EUIScene sceneType); + +public: + char *m_defaultBuffer, *m_tempBuffer; + void setFontCachingCalculationBuffer(int length); + + +}; diff --git a/Minecraft.Client/Common/UI/UIEnums.h b/Minecraft.Client/Common/UI/UIEnums.h new file mode 100644 index 0000000..c68b674 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIEnums.h @@ -0,0 +1,234 @@ +#pragma once + +// Defines the fixed groups for UI (lower numbers ticked first, rendered last (ie on top)) +enum EUIGroup +{ + eUIGroup_Fullscreen, + eUIGroup_Player1, +#ifndef __PSVITA__ + eUIGroup_Player2, + eUIGroup_Player3, + eUIGroup_Player4, +#endif + + eUIGroup_COUNT, + + eUIGroup_PAD, // Special case to determine the group from the pad (default) +}; + +// Defines the layers in a UI group (lower numbers ticked first, rendered last (ie on top)) +enum EUILayer +{ +#ifndef _CONTENT_PACKAGE + eUILayer_Debug, +#endif + eUILayer_Tooltips, + eUILayer_Error, + eUILayer_Alert, + eUILayer_Fullscreen, // Note: Fullscreen in this context doesn't necessarily mean fill the whole screen, but fill the whole viewport for this group. Enables processes that don't interefere with normal scene stack + eUILayer_Popup, + eUILayer_Scene, + //eUILayer_Chat, + eUILayer_HUD, + + eUILayer_COUNT, +}; + +// Defines the scenes and components that can be added to a layer +// If you add to the enums below, you need to add the scene name in the right place in CConsoleMinecraftApp::wchSceneA +enum EUIScene +{ + eUIScene_PartnernetPassword = 0, + eUIScene_Intro, + eUIScene_SaveMessage, + eUIScene_MainMenu, + eUIScene_FullscreenProgress, + eUIScene_PauseMenu, + eUIScene_Crafting2x2Menu, + eUIScene_Crafting3x3Menu, + eUIScene_FurnaceMenu, + eUIScene_ContainerMenu, + eUIScene_LargeContainerMenu,// for splitscreen + eUIScene_InventoryMenu, + eUIScene_DispenserMenu, + eUIScene_DebugOptions, + eUIScene_DebugTips, + eUIScene_HelpAndOptionsMenu, + eUIScene_HowToPlay, + eUIScene_HowToPlayMenu, + eUIScene_ControlsMenu, + eUIScene_SettingsOptionsMenu, + eUIScene_SettingsAudioMenu, + eUIScene_SettingsControlMenu, + eUIScene_SettingsGraphicsMenu, + eUIScene_SettingsUIMenu, + eUIScene_SettingsMenu, + eUIScene_LeaderboardsMenu, + eUIScene_Credits, + eUIScene_DeathMenu, + eUIComponent_TutorialPopup, + eUIScene_CreateWorldMenu, + eUIScene_LoadOrJoinMenu, + eUIScene_JoinMenu, + eUIScene_SignEntryMenu, + eUIScene_InGameInfoMenu, + eUIScene_ConnectingProgress, + eUIScene_DLCOffersMenu, + eUIScene_SocialPost, + eUIScene_TrialExitUpsell, + eUIScene_LoadMenu, + eUIComponent_Chat, + eUIScene_ReinstallMenu, + eUIScene_SkinSelectMenu, + eUIScene_TextEntry, + eUIScene_InGameHostOptionsMenu, + eUIScene_InGamePlayerOptionsMenu, + eUIScene_CreativeMenu, + eUIScene_LaunchMoreOptionsMenu, + eUIScene_DLCMainMenu, + eUIScene_NewUpdateMessage, + eUIScene_EnchantingMenu, + eUIScene_BrewingStandMenu, + eUIScene_EndPoem, + eUIScene_HUD, + eUIScene_TradingMenu, + eUIScene_AnvilMenu, + eUIScene_TeleportMenu, + +#ifdef _XBOX +// eUIScene_TransferToXboxOne, +#endif + + // When adding new scenes here, you must also update the switches in CConsoleMinecraftApp::NavigateToScene + // There are quite a few so you need to check them all + +#ifndef _XBOX + // Anything non-xbox should be added here. The ordering of scenes above is required for sentient reporting on xbox 360 to continue to be accurate + eUIComponent_Panorama, + eUIComponent_Logo, + eUIComponent_DebugUIConsole, + eUIComponent_DebugUIMarketingGuide, + eUIComponent_Tooltips, + eUIComponent_PressStartToPlay, + eUIComponent_MenuBackground, + eUIScene_Keyboard, + eUIScene_QuadrantSignin, + eUIScene_MessageBox, + eUIScene_Timer, + eUIScene_EULA, + eUIScene_InGameSaveManagementMenu, +#endif // ndef _XBOX + +#ifdef _DEBUG_MENUS_ENABLED + eUIScene_DebugOverlay, + eUIScene_DebugItemEditor, +#endif +#ifndef _CONTENT_PACKAGE + eUIScene_DebugCreateSchematic, + eUIScene_DebugSetCamera, +#endif + + eUIScene_COUNT, +}; + +// Used by the fullscreen progress scene to decide what to do when a thread finishes +enum ProgressionCompletionType +{ + e_ProgressCompletion_NoAction, + e_ProgressCompletion_NavigateBack, + e_ProgressCompletion_CloseUIScenes, + e_ProgressCompletion_CloseAllPlayersUIScenes, + e_ProgressCompletion_NavigateToHomeMenu, + e_ProgressCompletion_AutosaveNavigateBack, + e_ProgressCompletion_NavigateBackToScene, +}; + +enum EToolTipButton +{ + eToolTipButtonA = 0, + eToolTipButtonB, + eToolTipButtonX, + eToolTipButtonY, + eToolTipButtonLT, + eToolTipButtonRT, + eToolTipButtonLB, + eToolTipButtonRB, + eToolTipButtonLS, + eToolTipNumButtons +}; + +enum EToolTipItem +{ + eToolTipNone = -1, + eToolTipPickupPlace_OLD = 0, // To support existing menus. + eToolTipExit, + eToolTipPickUpGeneric, + eToolTipPickUpAll, + eToolTipPickUpHalf, + eToolTipPlaceGeneric, + eToolTipPlaceOne, + eToolTipPlaceAll, + eToolTipDropGeneric, + eToolTipDropOne, + eToolTipDropAll, + eToolTipSwap, + eToolTipQuickMove, + eToolTipQuickMoveIngredient, + eToolTipQuickMoveFuel, + eToolTipWhatIsThis, + eToolTipEquip, + eToolTipClearQuickSelect, + eToolTipQuickMoveTool, + eToolTipQuickMoveArmor, + eToolTipQuickMoveWeapon, + eToolTipDye, + eToolTipRepair, + eNumToolTips +}; + +enum EHowToPlayPage +{ + eHowToPlay_WhatsNew = 0, + eHowToPlay_Basics, + eHowToPlay_Multiplayer, + eHowToPlay_HUD, + eHowToPlay_Creative, + eHowToPlay_Inventory, + eHowToPlay_Chest, + eHowToPlay_LargeChest, + eHowToPlay_Enderchest, + eHowToPlay_InventoryCrafting, + eHowToPlay_CraftTable, + eHowToPlay_Furnace, + eHowToPlay_Dispenser, + + eHowToPlay_Brewing, + eHowToPlay_Enchantment, + eHowToPlay_Anvil, + eHowToPlay_FarmingAnimals, + eHowToPlay_Breeding, + eHowToPlay_Trading, + + eHowToPlay_NetherPortal, + eHowToPlay_TheEnd, +#ifdef _XBOX + eHowToPlay_SocialMedia, + eHowToPlay_BanList, +#endif + eHowToPlay_HostOptions, + eHowToPlay_NumPages +}; + +// Credits +enum ECreditTextTypes +{ + eExtraLargeText = 0, + eLargeText, + eMediumText, + eSmallText, + eNumTextTypes +}; + +#define NO_TRANSLATED_STRING ( -1 ) // String ID used to indicate that we are using non localised string. + +#define CONNECTING_PROGRESS_CHECK_TIME 500 diff --git a/Minecraft.Client/Common/UI/UIFontData.cpp b/Minecraft.Client/Common/UI/UIFontData.cpp new file mode 100644 index 0000000..c5ad46e --- /dev/null +++ b/Minecraft.Client/Common/UI/UIFontData.cpp @@ -0,0 +1,341 @@ +#include "stdafx.h" +#include "UIFontData.h" + + ///////////////////////////////////////////////////// + // --- -- --- THIS FILE IS IN UNICODE --- -- --- // + ///////////////////////////////////////////////////// + +SFontData SFontData::Mojangles_7 + = { + + /* Font Name */ "Mojangles7", + +#ifdef _XBOX + /* filename */ L"/font/Mojangles_7.png", +#else + /* Filename */ L"/TitleUpdate/res/font/Mojangles_7.png", +#endif + + /* Glyph count */ FONTSIZE, + /* Codepoints */ SFontData::Codepoints, + + /*img wdth,hght*/ 190, 264, + /*img cols,rows*/ FONTCOLS, FONTROWS, + + + /*glyph dim x,y*/ 8,13, + + /*ascent/descent*/ 7.f/13.f, 8.f/13.f, + + /*advance*/ 1.f/10.f, + + /*whitespace*/ 5, + + }; + + +SFontData SFontData::Mojangles_11 + = { + + /* Font Name */ "Mojangles11", + +#ifdef _XBOX + /* filename */ L"/font/Mojangles_11.png", +#else + /* Filename */ L"/TitleUpdate/res/font/Mojangles_11.png", +#endif + + /* Glyph count */ FONTSIZE, + /* Codepoints */ SFontData::Codepoints, + + /*img wdth,hght*/ 305, 348, + /*img cols,rows*/ FONTCOLS, FONTROWS, + + /*glyph dim x,y*/ 13,17, + + /*ascent/descent*/ 11.f/17.f, 6.f/17.f, + + /*advance*/ 1.f/13.f, + + /*whitespace*/ 7 + + }; + + + // ----------------------------------------------------------------------------- + // 4J-JEV: Glyph -> Unicode Maps, + // Unicode search tool: http://www.fileformat.info/info/unicode/char/search.htm + //------------------------------------------------------------------------------ + + +// Originally interpretted from 'Chars.txt', required many alterations to work correctly. (New Characters have been also added) +unsigned short SFontData::Codepoints[FONTSIZE] = +{ + // NOTE: When adding characters here, you may also want to add them to the ignore list 'Mojangles\Dev\Tools\Mojangles.txt' so we know not to panic when localisation uses them. + +/* ż Ż ź Ź ć Ć ń Ń */ + 0x0001, 0x017C, 0x017B, 0x017A, 0x0179, 0x0107, 0x0106, 0x0144, 0x0143, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + +/* ! " # $ % & ' ( ) * + , - */ + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0000, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, + +/* . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D */ + 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, + +/* E F G H I J K L M N O P Q R S T U V W X Y Z [ */ + 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, + +/* \ ] ^ _ ` a b c d e f g h i j k l m n o p q r */ + 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, + +/* s t u v w x y z { | } ~  */ + 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, + +/* */ + 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, + +/* ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · */ + 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + +/* ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î */ + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, + +/* Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å */ + 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, + +/* æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü */ + 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, + +/* ý þ ÿ Œ œ Š š Ÿ Ž ž ƒ ˣ ➄ – — ’ ‚ “ ” „ † ‡ • */ + 0x00FD, 0x00FE, 0x00FF, 0x0152, 0x0153, 0x0160, 0x0161, 0x0178, 0x017D, 0x017E, 0x0192, 0x02E3, 0x2784, 0x2013, 0x2014, 0x2019, 0x201A, 0x201C, 0x201D, 0x201E, 0x2020, 0x2021, 0x2022, + +/* … ‰ ‹ › € ™ ͝ Ş İ Ğ ş ı ğ ę Ę ó Ó ą Ą ś Ś ł Ł */ + 0x2026, 0x2030, 0x2039, 0x203A, 0x20AC, 0x2122, 0x035D, 0x015E, 0x0130, 0x011E, 0x015F, 0x0131, 0x011F, 0x0119, 0x0118, 0x00F3, 0x00D3, 0x0105, 0x0104, 0x015B, 0x015A, 0x0142, 0x0141, + +/* Ё А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х */ + 0x0401, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, + +/* Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м */ + 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, + +/* н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ё χ ψ ω */ + 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0451, 0x03C7, 0x03C8, 0x03C9, + +/* Č Ď Ě Ĺ Ľ Ň Ő Ř Ť Ů Ű č ď ě ĺ ľ ň ő ř ť ů ű */ + 0x010C, 0x010E, 0x011A, 0x0139, 0x013D, 0x0147, 0x0150, 0x0158, 0x0164, 0x016E, 0x0170, 0x010D, 0x010F, 0x011B, 0x013A, 0x013E, 0x0148, 0x0151, 0x0159, 0x0165, 0x016F, 0x0171, 0x0020, + +/* Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, + +/* Ω α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ τ υ φ */ + 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, + +/* Ά Έ Ή Ί Ό Ύ Ώ ΐ ά έ ή ί ϊ ό ύ ώ ŕ ΄ ‘ */ + 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, 0x038F, 0x0390, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CA, 0x03CC, 0x03CD, 0x03CE, 0x0155, 0x0384, 0x2018, 0x0000, 0x0000, 0x0000, 0x0000, +}; + + + + + /////////////////////// + // --- CFontData --- // + /////////////////////// + +CFontData::CFontData() +{ + m_unicodeMap = unordered_map(); + + m_sFontData = NULL; + m_kerningTable = NULL; + m_pbRawImage = NULL; +} + +CFontData::CFontData(SFontData &sFontData, int *pbRawImage) + : m_unicodeMap( sFontData.m_uiGlyphCount + 2 ) +{ + this->m_sFontData = &sFontData; + + // INITIALISE ALPHA CHANNEL // + + // Glyph Archive (1Byte per pixel). + unsigned int archiveSize = sFontData.m_uiGlyphMapX * sFontData.m_uiGlyphMapY; + + this->m_pbRawImage = new unsigned char[archiveSize]; + + // 4J-JEV: Take the alpha channel from each pixel. + for (unsigned int i = 0; i < archiveSize; i++) + { + this->m_pbRawImage[i] = (pbRawImage[i] & 0xFF000000) >> 24; + } + + // CREATE UNICODE MAP // + for (unsigned int i = 0; i < sFontData.m_uiGlyphCount; i++) + { + unordered_map::value_type pair(sFontData.Codepoints[i], i); + m_unicodeMap.insert( pair ); + } + + // CREATE KERNING TABLE // + m_kerningTable = new unsigned short[sFontData.m_uiGlyphCount]; + for (unsigned short glyph = 0; glyph < sFontData.m_uiGlyphCount; glyph++) + { + int row,column; + getPos(glyph,row,column); + + short xMax = 0, _x=0, _y=0; + + // Find the position of the topLeft corner. + unsigned char *topLeft = m_pbRawImage, *cursor; + moveCursor( topLeft, column * sFontData.m_uiGlyphWidth, row * sFontData.m_uiGlyphHeight); + + assert( ((column+1)*sFontData.m_uiGlyphWidth) < sFontData.m_uiGlyphMapX ); + assert( ((row+1)*sFontData.m_uiGlyphHeight) < sFontData.m_uiGlyphMapY ); + + static int XX = 79; + // Find the furthest filled pixel to the right. + for (short y = 0; y < sFontData.m_uiGlyphHeight; y++) + { + for (short x = 0; x < sFontData.m_uiGlyphWidth; x++) + { + cursor = topLeft; + moveCursor(cursor, x, y); + + assert( (cursor-m_pbRawImage) < archiveSize ); + + if ( *cursor > 0 ) + { + if (x > xMax) xMax = x; + _x = x; + _y = y; + } + } + } + +#if _DEBUG_BLOCK_CHARS + for (short y = 0; y < sFontData.m_uiGlyphHeight; y++) + { + for (short x = 0; x < sFontData.m_uiGlyphWidth; x++) + { + cursor = topLeft; + moveCursor(cursor, x, y); + + if (x==0) *cursor = 0x00; + else if (x<=xMax) *cursor = 0xFF; + else *cursor = 0x00; + } + } +#endif + + // 4J-JEV: Empty glyphs are considered to be whitespace. + if (xMax == 0) m_kerningTable[glyph] = sFontData.m_uiWhitespaceWidth; + else m_kerningTable[glyph] = xMax + 1; + } + + // CACHE GLYPH ADVANCES // + m_pfAdvanceTable = new float[sFontData.m_uiGlyphCount]; + for (unsigned short glyph = 0; glyph < sFontData.m_uiGlyphCount; glyph++) + { + m_pfAdvanceTable[glyph] = m_kerningTable[glyph] * m_sFontData->m_fAdvPerPixel; + } + + // DEBUG // +#ifndef _CONTENT_PACKAGE + for (int i = 0; i < sFontData.m_uiGlyphCount; i++) + { + int unicode = getUnicode(i), unicodeChar = 32, row, col; + if ( 32 < unicode && unicode < 127 && unicode != 0x0025 ) + { + unicodeChar = unicode; + } + + getPos(i, row, col); + + string state = "ok"; + if (i != getGlyphId(unicode)) + { + state = "MISSMATCHED!"; + + app.DebugPrintf( " %i\t%c\tU+%.4X, kerning=%i, (%2i,%2i). %s\n", + i, getGlyphId(unicode), unicodeChar, unicode, m_kerningTable[i], row, col, state.c_str() ); + } + } +#endif +} + +void CFontData::release() +{ + delete [] m_kerningTable; + delete [] m_pfAdvanceTable; + delete [] m_pbRawImage; +} + +const string CFontData::getFontName() +{ + return m_sFontData->m_strFontName; +} + +SFontData *CFontData::getFontData() +{ + return m_sFontData; +} + +unsigned short CFontData::getGlyphId(unsigned int unicodepoint) +{ + unordered_map::iterator out = m_unicodeMap.find(unicodepoint); + if (out != m_unicodeMap.end()) + return out->second; + return 0; +} + +unsigned int CFontData::getUnicode(unsigned short glyphId) +{ + return m_sFontData->Codepoints[glyphId]; +} + +unsigned char *CFontData::topLeftPixel(int row, int col) +{ + unsigned char *out = m_pbRawImage; + moveCursor(out, col * m_sFontData->m_uiGlyphWidth, row* m_sFontData->m_uiGlyphHeight); + return out; +} + +void CFontData::getPos(unsigned short glyphId, int &rowOut, int &colOut) +{ + rowOut = glyphId / m_sFontData->m_uiGlyphMapCols; + colOut = glyphId % m_sFontData->m_uiGlyphMapCols; +} + +float CFontData::getAdvance(unsigned short glyphId) +{ + return m_pfAdvanceTable[glyphId]; +} + +int CFontData::getWidth(unsigned short glyphId) +{ + return m_kerningTable[glyphId]; +} + +bool CFontData::glyphIsWhitespace(unsigned short glyphId) +{ + return unicodeIsWhitespace( getUnicode(glyphId) ); +} + +bool CFontData::unicodeIsWhitespace(unsigned int unicode) +{ + static const unsigned int MAX_WHITESPACE = 1; + static const unsigned int whitespace[MAX_WHITESPACE] = { + 0x0020 + }; + + for (int i=0; im_uiGlyphMapX) + dx; +} diff --git a/Minecraft.Client/Common/UI/UIFontData.h b/Minecraft.Client/Common/UI/UIFontData.h new file mode 100644 index 0000000..b7e38ff --- /dev/null +++ b/Minecraft.Client/Common/UI/UIFontData.h @@ -0,0 +1,133 @@ +#pragma once + +#include + +using namespace std; + +#define _DEBUG_BLOCK_CHARS 0 + +// For hardcoded font data. +struct SFontData +{ +public: + static const unsigned short FONTCOLS = 23; + static const unsigned short FONTROWS = 20; + + static const unsigned short FONTSIZE = FONTCOLS * FONTROWS; + +public: + // Font name. + string m_strFontName; + + // Filename of the glyph archive. + wstring m_wstrFilename; + + // Number of glyphs in the archive. + unsigned int m_uiGlyphCount; + + // Unicode values of each glyph. + unsigned short *m_arrCodepoints; + + // X resolution of glyph archive. + unsigned int m_uiGlyphMapX; + + // Y resolution of glyph archive. + unsigned int m_uiGlyphMapY; + + // Number of columns in the glyph archive. + unsigned int m_uiGlyphMapCols; + + // Number of rows in the glyph archive. + unsigned int m_uiGlyphMapRows; + + // Width of each glyph. + unsigned int m_uiGlyphWidth; + + // Height of each glyph. + unsigned int m_uiGlyphHeight; + + // Ascent of each glyph above the baseline (units?). + float m_fAscent; + + // Descent of each glyph below the baseline (units?). + float m_fDescent; + + // How much to advance for each pixel wide the glyph is. + float m_fAdvPerPixel; + + // How many pixels wide any whitespace characters are. + unsigned int m_uiWhitespaceWidth; + +public: + static unsigned short Codepoints[FONTSIZE]; + static SFontData Mojangles_7; + static SFontData Mojangles_11; +}; + +// Provides a common interface for dealing with font data. +class CFontData +{ +public: + CFontData(); + + // pbRawImage consumed by constructor. + CFontData(SFontData &sFontData, int *pbRawImage); + + // Release memory. + void release(); + +protected: + + // Hardcoded font data. + SFontData *m_sFontData; + + // Map Unicodepoints to glyph ids. + unordered_map m_unicodeMap; + + // Kerning value for each glyph. + unsigned short *m_kerningTable; + + // Binary blob of the archive image. + unsigned char *m_pbRawImage; + + // Total advance of each character. + float *m_pfAdvanceTable; + +public: + + // Accessor for the font name in the internal SFontData. + const string getFontName(); + + // Accessor for the hardcoded internal font data. + SFontData *getFontData(); + + // Get the glyph id corresponding to a unicode point. + unsigned short getGlyphId(unsigned int unicodepoint); + + // Get the unicodepoint corresponding to a glyph id. + unsigned int getUnicode(unsigned short glyphId); + + // Get a pointer to the top left pixel of a row/column in the raw image. + unsigned char *topLeftPixel(int row, int col); + + // Get the row and column where a glyph appears in the archive. + void getPos(unsigned short gyphId, int &row, int &col); + + // Get the advance of this character (units?). + float getAdvance(unsigned short glyphId); + + // Get the width (in pixels) of a given character. + int getWidth(unsigned short glyphId); + + // Returns true if this glyph is whitespace. + bool glyphIsWhitespace(unsigned short glyphId); + + // Returns true if this unicodepoint is whitespace + bool unicodeIsWhitespace(unsigned int unicodepoint); + +private: + + // Move a pointer in an image dx pixels right and dy pixels down, wrap around in either dimension leads to unknown behaviour. + void moveCursor(unsigned char *&cursor, unsigned int dx, unsigned int dy); +}; + diff --git a/Minecraft.Client/Common/UI/UIGroup.cpp b/Minecraft.Client/Common/UI/UIGroup.cpp new file mode 100644 index 0000000..1899d05 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIGroup.cpp @@ -0,0 +1,421 @@ +#include "stdafx.h" +#include "UIGroup.h" + +UIGroup::UIGroup(EUIGroup group, int iPad) +{ + m_group = group; + m_iPad = iPad; + m_bMenuDisplayed = false; + m_bPauseMenuDisplayed = false; + m_bContainerMenuDisplayed = false; + m_bIgnoreAutosaveMenuDisplayed = false; + m_bIgnorePlayerJoinMenuDisplayed = false; + + m_updateFocusStateCountdown = 0; + + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i] = new UILayer(this); +#ifdef __PSVITA__ + m_layers[i]->m_iLayer=(EUILayer)i; +#endif + } + + m_tooltips = (UIComponent_Tooltips *)m_layers[(int)eUILayer_Tooltips]->addComponent(0, eUIComponent_Tooltips); + + m_tutorialPopup = NULL; + m_hud = NULL; + m_pressStartToPlay = NULL; + if(m_group != eUIGroup_Fullscreen) + { + m_tutorialPopup = (UIComponent_TutorialPopup *)m_layers[(int)eUILayer_Popup]->addComponent(m_iPad, eUIComponent_TutorialPopup); + + m_hud = (UIScene_HUD *)m_layers[(int)eUILayer_HUD]->addComponent(m_iPad, eUIScene_HUD); + + //m_layers[(int)eUILayer_Chat]->addComponent(m_iPad, eUIComponent_Chat); + } + else + { + m_pressStartToPlay = (UIComponent_PressStartToPlay *)m_layers[(int)eUILayer_Tooltips]->addComponent(0, eUIComponent_PressStartToPlay); + } + + m_viewportType = C4JRender::VIEWPORT_TYPE_FULLSCREEN; + + // 4J Stu - Pre-allocate this for cached rendering in scenes. It's horribly slow to do dynamically, but we should only need one + // per group as we will only be displaying one of these types of scenes at a time + m_commandBufferList = MemoryTracker::genLists(1); +} + +void UIGroup::DestroyAll() +{ + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->DestroyAll(); + } +} + +void UIGroup::ReloadAll() +{ + // We only need to reload things when they are likely to be rendered + int highestRenderable = 0; + for(; highestRenderable < eUILayer_COUNT; ++highestRenderable) + { + if(m_layers[highestRenderable]->hidesLowerScenes()) break; + } + if(highestRenderable < eUILayer_Fullscreen) highestRenderable = eUILayer_Fullscreen; + for(; highestRenderable >= 0; --highestRenderable) + { + if(highestRenderable < eUILayer_COUNT) m_layers[highestRenderable]->ReloadAll(highestRenderable != (int)eUILayer_Fullscreen); + } +} + +void UIGroup::tick() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->tick(); + + // TODO: May wish to ignore ticking other layers here based on current layer + } + + // Handle deferred update focus + if (m_updateFocusStateCountdown > 0) + { + m_updateFocusStateCountdown--; + if (m_updateFocusStateCountdown == 0)_UpdateFocusState(); +} +} + +void UIGroup::render() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + S32 width = 0; + S32 height = 0; + ui.getRenderDimensions(m_viewportType, width, height); + int highestRenderable = 0; + for(; highestRenderable < eUILayer_COUNT; ++highestRenderable) + { + if(m_layers[highestRenderable]->hidesLowerScenes()) break; + } + for(; highestRenderable >= 0; --highestRenderable) + { + if(highestRenderable < eUILayer_COUNT) m_layers[highestRenderable]->render(width, height,m_viewportType); + } +} + +bool UIGroup::hidesLowerScenes() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return false; + bool hidesScenes = false; + for(int i = eUILayer_COUNT - 1; i >= 0; --i) + { + hidesScenes = m_layers[i]->hidesLowerScenes(); + if(hidesScenes) break; + } + return hidesScenes; +} + +void UIGroup::getRenderDimensions(S32 &width, S32 &height) +{ + ui.getRenderDimensions(m_viewportType, width, height); +} + +// NAVIGATION +bool UIGroup::NavigateToScene(int iPad, EUIScene scene, void *initData, EUILayer layer) +{ + bool succeeded = m_layers[(int)layer]->NavigateToScene(iPad, scene, initData); + updateStackStates(); + return succeeded; +} + +bool UIGroup::NavigateBack(int iPad, EUIScene eScene, EUILayer eLayer) +{ + // Keep navigating back on every layer until we hit the target scene + bool foundTarget = false; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + if(eLayer < eUILayer_COUNT && eLayer != i) continue; + foundTarget = m_layers[i]->NavigateBack(iPad, eScene); + if(foundTarget) break; + } + updateStackStates(); + return foundTarget; +} + +void UIGroup::closeAllScenes() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( m_iPad >= 0 ) + { + if(pMinecraft != NULL && pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + + // This just allows it to be shown + gameMode->getTutorial()->showTutorialPopup(true); + } + } + + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + // Ignore the error layer + if(i != (int)eUILayer_Error) m_layers[i]->closeAllScenes(); + } + updateStackStates(); +} + +UIScene *UIGroup::GetTopScene(EUILayer layer) +{ + return m_layers[(int)layer]->GetTopScene(); +} + +bool UIGroup::GetMenuDisplayed() +{ + return m_bMenuDisplayed; +} + +bool UIGroup::IsSceneInStack(EUIScene scene) +{ + bool found = false; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + found = m_layers[i]->IsSceneInStack(scene); + if(found) break; + } + return found; +} + +bool UIGroup::HasFocus(int iPad) +{ + bool hasFocus = false; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + if( m_layers[i]->m_hasFocus) + { + if(m_layers[i]->HasFocus(iPad)) + { + hasFocus = true; + } + break; + } + } + return hasFocus; +} + +#ifdef __PSVITA__ +UIScene *UIGroup::getCurrentScene() +{ + UIScene *pScene; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + pScene=m_layers[i]->getCurrentScene(); + + if(pScene!=NULL) return pScene; + } + + return NULL; +} +#endif + +// INPUT +void UIGroup::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->handleInput(iPad, key, repeat, pressed, released, handled); + if(handled) break; + } +} + +// FOCUS + +// Check that a layer may recieve focus, specifically that there is no infocus layer above +bool UIGroup::RequestFocus(UILayer* layerPtr) +{ + // Find the layer + unsigned int layerIndex = GetLayerIndex(layerPtr); + + // Top layer is always allowed focus + if (layerIndex == 0) return true; + + // Check layers above to see if any of them have focus + for (int i = layerIndex-1; i >= 0; i--) + { + if (m_layers[i]->m_hasFocus) return false; + } + + return true; +} + +void UIGroup::showComponent(int iPad, EUIScene scene, EUILayer layer, bool show) +{ + m_layers[layer]->showComponent(iPad, scene, show); +} + +UIScene *UIGroup::addComponent(int iPad, EUIScene scene, EUILayer layer) +{ + return m_layers[layer]->addComponent(iPad, scene); +} + +void UIGroup::removeComponent(EUIScene scene, EUILayer layer) +{ + m_layers[layer]->removeComponent(scene); +} + +void UIGroup::SetViewportType(C4JRender::eViewportType type) +{ + if(m_viewportType != type) + { + m_viewportType = type; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->ReloadAll(true); + } + } +} + +C4JRender::eViewportType UIGroup::GetViewportType() +{ + return m_viewportType; +} + +void UIGroup::HandleDLCMountingComplete() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + app.DebugPrintf("UIGroup::HandleDLCMountingComplete - m_layers[%d]\n",i); + m_layers[i]->HandleDLCMountingComplete(); + } +} + +void UIGroup::HandleDLCInstalled() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->HandleDLCInstalled(); + } +} + +#ifdef _XBOX_ONE +void UIGroup::HandleDLCLicenseChange() +{ + // Ignore this group if the player isn't signed in + if(m_iPad >= 0 && !ProfileManager.IsSignedIn(m_iPad)) return; + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->HandleDLCLicenseChange(); + } +} +#endif + +bool UIGroup::IsFullscreenGroup() +{ + return m_group == eUIGroup_Fullscreen; +} + + +void UIGroup::handleUnlockFullVersion() +{ + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_layers[i]->handleUnlockFullVersion(); + } +} + +void UIGroup::updateStackStates() +{ + m_bMenuDisplayed = false; + m_bPauseMenuDisplayed = false; + m_bContainerMenuDisplayed = false; + m_bIgnoreAutosaveMenuDisplayed = false; + m_bIgnorePlayerJoinMenuDisplayed = false; + + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + m_bMenuDisplayed = m_bMenuDisplayed || m_layers[i]->m_bMenuDisplayed; + m_bPauseMenuDisplayed = m_bPauseMenuDisplayed || m_layers[i]->m_bPauseMenuDisplayed; + m_bContainerMenuDisplayed = m_bContainerMenuDisplayed || m_layers[i]->m_bContainerMenuDisplayed; + m_bIgnoreAutosaveMenuDisplayed = m_bIgnoreAutosaveMenuDisplayed || m_layers[i]->m_bIgnoreAutosaveMenuDisplayed; + m_bIgnorePlayerJoinMenuDisplayed = m_bIgnorePlayerJoinMenuDisplayed || m_layers[i]->m_bIgnorePlayerJoinMenuDisplayed; + } +} + +// Defer update focus till for 10 UI ticks +void UIGroup::UpdateFocusState() +{ + m_updateFocusStateCountdown = 10; +} + +// Pass focus to uppermost layer that accepts focus +void UIGroup::_UpdateFocusState() +{ + bool groupFocusSet = false; + + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + groupFocusSet = m_layers[i]->updateFocusState(true); + if (groupFocusSet) break; + } +} + +// Get the index of the layer +unsigned int UIGroup::GetLayerIndex(UILayer* layerPtr) +{ + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + if (m_layers[i] == layerPtr) return i; + } + + // can't get here... + return 0; +} + +void UIGroup::PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic) +{ + __int64 groupStatic = 0; + __int64 groupDynamic = 0; + app.DebugPrintf(app.USER_SR, "-- BEGIN GROUP %d\n",m_group); + for(unsigned int i = 0; i < eUILayer_COUNT; ++i) + { + app.DebugPrintf(app.USER_SR, " \\- BEGIN LAYER %d\n",i); + m_layers[i]->PrintTotalMemoryUsage(groupStatic, groupDynamic); + app.DebugPrintf(app.USER_SR, " \\- END LAYER %d\n",i); + } + app.DebugPrintf(app.USER_SR, "-- Group static: %d, Group dynamic: %d\n", groupStatic, groupDynamic); + totalStatic += groupStatic; + totalDynamic += groupDynamic; + app.DebugPrintf(app.USER_SR, "-- END GROUP %d\n",m_group); +} + +int UIGroup::getCommandBufferList() +{ + return m_commandBufferList; +} + +// Returns the first scene of given type if it exists, NULL otherwise +UIScene *UIGroup::FindScene(EUIScene sceneType) +{ + UIScene *pScene = NULL; + + for (int i = 0; i < eUILayer_COUNT; i++) + { + pScene = m_layers[i]->FindScene(sceneType); +#ifdef __PS3__ + if (pScene != NULL) return pScene; +#else + if (pScene != nullptr) return pScene; +#endif + } + + return pScene; +} diff --git a/Minecraft.Client/Common/UI/UIGroup.h b/Minecraft.Client/Common/UI/UIGroup.h new file mode 100644 index 0000000..e20fbb0 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIGroup.h @@ -0,0 +1,113 @@ +#pragma once +#include "UILayer.h" +#include "UIEnums.h" + +class UIComponent_Tooltips; +class UIComponent_TutorialPopup; +class UIScene_HUD; +class UIComponent_PressStartToPlay; + +// A group contains a collection of layers for a specific context (e.g. each player has 1 group) +class UIGroup +{ +private: + UILayer *m_layers[eUILayer_COUNT]; + + UIComponent_Tooltips *m_tooltips; + UIComponent_TutorialPopup *m_tutorialPopup; + UIComponent_PressStartToPlay *m_pressStartToPlay; + UIScene_HUD *m_hud; + + C4JRender::eViewportType m_viewportType; + + EUIGroup m_group; + int m_iPad; + + bool m_bMenuDisplayed; + bool m_bPauseMenuDisplayed; + bool m_bContainerMenuDisplayed; + bool m_bIgnoreAutosaveMenuDisplayed; + bool m_bIgnorePlayerJoinMenuDisplayed; + + // Countdown in ticks to update focus state + int m_updateFocusStateCountdown; + + int m_commandBufferList; + +public: + UIGroup(EUIGroup group, int iPad); + +#ifdef __PSVITA__ + EUIGroup GetGroup() {return m_group;} +#endif + UIComponent_Tooltips *getTooltips() { return m_tooltips; } + UIComponent_TutorialPopup *getTutorialPopup() { return m_tutorialPopup; } + UIScene_HUD *getHUD() { return m_hud; } + UIComponent_PressStartToPlay *getPressStartToPlay() { return m_pressStartToPlay; } + + void DestroyAll(); + void ReloadAll(); + + void tick(); + void render(); + bool hidesLowerScenes(); + void getRenderDimensions(S32 &width, S32 &height); + + // NAVIGATION + bool NavigateToScene(int iPad, EUIScene scene, void *initData, EUILayer layer); + bool NavigateBack(int iPad, EUIScene eScene, EUILayer eLayer = eUILayer_COUNT); + void closeAllScenes(); + UIScene *GetTopScene(EUILayer layer); + + bool IsSceneInStack(EUIScene scene); + bool HasFocus(int iPad); + + bool RequestFocus(UILayer* layerPtr); + void UpdateFocusState(); + + bool GetMenuDisplayed(); + bool IsPauseMenuDisplayed() { return m_bPauseMenuDisplayed; } + bool IsContainerMenuDisplayed() { return m_bContainerMenuDisplayed; } + bool IsIgnoreAutosaveMenuDisplayed() { return m_bIgnoreAutosaveMenuDisplayed; } + bool IsIgnorePlayerJoinMenuDisplayed() { return m_bIgnorePlayerJoinMenuDisplayed; } + + // INPUT + void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +#ifdef __PSVITA__ + // Current active scene + UIScene *getCurrentScene(); +#endif + + // FOCUS + bool getFocusState(); + + // A component is an element on a layer that displays BELOW other scenes in this layer, but does not engage in any navigation + // E.g. you can keep a component active while performing navigation with other scenes on this layer + void showComponent(int iPad, EUIScene scene, EUILayer layer, bool show); + UIScene *addComponent(int iPad, EUIScene scene, EUILayer layer); + void removeComponent(EUIScene scene, EUILayer layer); + + void SetViewportType(C4JRender::eViewportType type); + C4JRender::eViewportType GetViewportType(); + + virtual void HandleDLCMountingComplete(); + virtual void HandleDLCInstalled(); +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + bool IsFullscreenGroup(); + + void handleUnlockFullVersion(); + + void PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic); + + unsigned int GetLayerIndex(UILayer* layerPtr); + + int getCommandBufferList(); + UIScene *FindScene(EUIScene sceneType); + +private: + void _UpdateFocusState(); + void updateStackStates(); +}; diff --git a/Minecraft.Client/Common/UI/UILayer.cpp b/Minecraft.Client/Common/UI/UILayer.cpp new file mode 100644 index 0000000..e6f87f8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UILayer.cpp @@ -0,0 +1,870 @@ +#include "stdafx.h" +#include "UI.h" +#include "UILayer.h" +#include "UIScene.h" + +UILayer::UILayer(UIGroup *parent) +{ + m_parentGroup = parent; + m_hasFocus = false; + m_bMenuDisplayed = false; + m_bPauseMenuDisplayed = false; + m_bContainerMenuDisplayed = false; + m_bIgnoreAutosaveMenuDisplayed = false; + m_bIgnorePlayerJoinMenuDisplayed = false; +} + +void UILayer::tick() +{ + // Delete old scenes - deleting a scene can cause a new scene to be deleted, so we need to make a copy of the scenes that we are going to try and destroy this tick + vectorscenesToDeleteCopy; + for( AUTO_VAR(it,m_scenesToDelete.begin()); it != m_scenesToDelete.end(); it++) + { + UIScene *scene = (*it); + scenesToDeleteCopy.push_back(scene); + } + m_scenesToDelete.clear(); + + // Delete the scenes in our copy if they are ready to delete, otherwise add back to the ones that are still to be deleted. Actually deleting a scene might also add something back into m_scenesToDelete. + for( AUTO_VAR(it,scenesToDeleteCopy.begin()); it != scenesToDeleteCopy.end(); it++) + { + UIScene *scene = (*it); + if( scene->isReadyToDelete()) + { + delete scene; + } + else + { + m_scenesToDelete.push_back(scene); + } + } + + while (!m_scenesToDestroy.empty()) + { + UIScene *scene = m_scenesToDestroy.back(); + m_scenesToDestroy.pop_back(); + scene->destroyMovie(); + } + m_scenesToDestroy.clear(); + + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + (*it)->tick(); + } + // Note: reverse iterator, the last element is the top of the stack + int sceneIndex = m_sceneStack.size() - 1; + //for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + while( sceneIndex >= 0 && sceneIndex < m_sceneStack.size() ) + { + //(*it)->tick(); + UIScene *scene = m_sceneStack[sceneIndex]; + scene->tick(); + --sceneIndex; + // TODO: We may wish to ignore ticking the rest of the stack based on this scene + } +} + +void UILayer::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(!ui.IsExpectingOrReloadingSkin()) + { + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + AUTO_VAR(itRef,m_componentRefCount.find((*it)->getSceneType())); + if(itRef != m_componentRefCount.end() && itRef->second.second) + { + if((*it)->isVisible() ) + { + PIXBeginNamedEvent(0, "Rendering component %d", (*it)->getSceneType() ); + (*it)->render(width, height,viewport); + PIXEndNamedEvent(); + } + } + } + } + if(!m_sceneStack.empty()) + { + int lowestRenderable = m_sceneStack.size() - 1; + for(;lowestRenderable >= 0; --lowestRenderable) + { + if(m_sceneStack[lowestRenderable]->hidesLowerScenes()) break; + } + if(lowestRenderable < 0) lowestRenderable = 0; + for(;lowestRenderable < m_sceneStack.size(); ++lowestRenderable) + { + if(m_sceneStack[lowestRenderable]->isVisible() && (!ui.IsExpectingOrReloadingSkin() || m_sceneStack[lowestRenderable]->getSceneType()==eUIScene_Timer)) + { + PIXBeginNamedEvent(0, "Rendering scene %d", m_sceneStack[lowestRenderable]->getSceneType() ); + m_sceneStack[lowestRenderable]->render(width, height,viewport); + PIXEndNamedEvent(); + } + } + } +} + +bool UILayer::IsSceneInStack(EUIScene scene) +{ + bool inStack = false; + for(int i = m_sceneStack.size() - 1;i >= 0; --i) + { + if(m_sceneStack[i]->getSceneType() == scene) + { + inStack = true; + break; + } + } + return inStack; +} + +bool UILayer::HasFocus(int iPad) +{ + bool hasFocus = false; + if(m_hasFocus) + { + for(int i = m_sceneStack.size() - 1;i >= 0; --i) + { + if(m_sceneStack[i]->stealsFocus() ) + { + if(m_sceneStack[i]->hasFocus(iPad)) + { + hasFocus = true; + } + break; + } + } + } + return hasFocus; +} + +bool UILayer::hidesLowerScenes() +{ + bool hidesScenes = false; + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + if((*it)->hidesLowerScenes()) + { + hidesScenes = true; + break; + } + } + if(!hidesScenes && !m_sceneStack.empty()) + { + for(int i = m_sceneStack.size() - 1;i >= 0; --i) + { + if(m_sceneStack[i]->hidesLowerScenes()) + { + hidesScenes = true; + break; + } + } + } + return hidesScenes; +} + +void UILayer::getRenderDimensions(S32 &width, S32 &height) +{ + m_parentGroup->getRenderDimensions(width, height); +} + +void UILayer::DestroyAll() +{ + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + (*it)->destroyMovie(); + } + for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) + { + (*it)->destroyMovie(); + } +} + +void UILayer::ReloadAll(bool force) +{ + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + (*it)->reloadMovie(force); + } + if(!m_sceneStack.empty()) + { + int lowestRenderable = 0; + for(;lowestRenderable < m_sceneStack.size(); ++lowestRenderable) + { + m_sceneStack[lowestRenderable]->reloadMovie(); + } + } +} + +bool UILayer::GetMenuDisplayed() +{ + return m_bMenuDisplayed; +} + +bool UILayer::NavigateToScene(int iPad, EUIScene scene, void *initData) +{ + UIScene *newScene = NULL; + switch(scene) + { + // Debug +#ifdef _DEBUG_MENUS_ENABLED + case eUIScene_DebugOverlay: + newScene = new UIScene_DebugOverlay(iPad, initData, this); + break; + case eUIScene_DebugSetCamera: + newScene = new UIScene_DebugSetCamera(iPad, initData, this); + break; + case eUIScene_DebugCreateSchematic: + newScene = new UIScene_DebugCreateSchematic(iPad, initData, this); + break; +#endif + case eUIScene_DebugOptions: + newScene = new UIScene_DebugOptionsMenu(iPad, initData, this); + break; + + // Containers + case eUIScene_InventoryMenu: + newScene = new UIScene_InventoryMenu(iPad, initData, this); + break; + case eUIScene_CreativeMenu: + newScene = new UIScene_CreativeMenu(iPad, initData, this); + break; + case eUIScene_ContainerMenu: + case eUIScene_LargeContainerMenu: + newScene = new UIScene_ContainerMenu(iPad, initData, this); + break; + case eUIScene_BrewingStandMenu: + newScene = new UIScene_BrewingStandMenu(iPad, initData, this); + break; + case eUIScene_DispenserMenu: + newScene = new UIScene_DispenserMenu(iPad, initData, this); + break; + case eUIScene_EnchantingMenu: + newScene = new UIScene_EnchantingMenu(iPad, initData, this); + break; + case eUIScene_FurnaceMenu: + newScene = new UIScene_FurnaceMenu(iPad, initData, this); + break; + case eUIScene_Crafting2x2Menu: + case eUIScene_Crafting3x3Menu: + newScene = new UIScene_CraftingMenu(iPad, initData, this); + break; + case eUIScene_TradingMenu: + newScene = new UIScene_TradingMenu(iPad, initData, this); + break; + case eUIScene_AnvilMenu: + newScene = new UIScene_AnvilMenu(iPad, initData, this); + break; + + // Help and Options + case eUIScene_HelpAndOptionsMenu: + newScene = new UIScene_HelpAndOptionsMenu(iPad, initData, this); + break; + case eUIScene_SettingsMenu: + newScene = new UIScene_SettingsMenu(iPad, initData, this); + break; + case eUIScene_SettingsOptionsMenu: + newScene = new UIScene_SettingsOptionsMenu(iPad, initData, this); + break; + case eUIScene_SettingsAudioMenu: + newScene = new UIScene_SettingsAudioMenu(iPad, initData, this); + break; + case eUIScene_SettingsControlMenu: + newScene = new UIScene_SettingsControlMenu(iPad, initData, this); + break; + case eUIScene_SettingsGraphicsMenu: + newScene = new UIScene_SettingsGraphicsMenu(iPad, initData, this); + break; + case eUIScene_SettingsUIMenu: + newScene = new UIScene_SettingsUIMenu(iPad, initData, this); + break; + case eUIScene_SkinSelectMenu: + newScene = new UIScene_SkinSelectMenu(iPad, initData, this); + break; + case eUIScene_HowToPlayMenu: + newScene = new UIScene_HowToPlayMenu(iPad, initData, this); + break; + case eUIScene_HowToPlay: + newScene = new UIScene_HowToPlay(iPad, initData, this); + break; + case eUIScene_ControlsMenu: + newScene = new UIScene_ControlsMenu(iPad, initData, this); + break; + case eUIScene_ReinstallMenu: + newScene = new UIScene_ReinstallMenu(iPad, initData, this); + break; + case eUIScene_Credits: + newScene = new UIScene_Credits(iPad, initData, this); + break; + + + // Other in-game + case eUIScene_PauseMenu: + newScene = new UIScene_PauseMenu(iPad, initData, this); + break; + case eUIScene_DeathMenu: + newScene = new UIScene_DeathMenu(iPad, initData, this); + break; + case eUIScene_ConnectingProgress: + newScene = new UIScene_ConnectingProgress(iPad, initData, this); + break; + case eUIScene_SignEntryMenu: + newScene = new UIScene_SignEntryMenu(iPad, initData, this); + break; + case eUIScene_InGameInfoMenu: + newScene = new UIScene_InGameInfoMenu(iPad, initData, this); + break; + case eUIScene_InGameHostOptionsMenu: + newScene = new UIScene_InGameHostOptionsMenu(iPad, initData, this); + break; + case eUIScene_InGamePlayerOptionsMenu: + newScene = new UIScene_InGamePlayerOptionsMenu(iPad, initData, this); + break; +#if defined(_XBOX_ONE) || defined(__ORBIS__) + case eUIScene_InGameSaveManagementMenu: + newScene = new UIScene_InGameSaveManagementMenu(iPad, initData, this); + break; +#endif + case eUIScene_TeleportMenu: + newScene = new UIScene_TeleportMenu(iPad, initData, this); + break; + case eUIScene_EndPoem: + if(IsSceneInStack(eUIScene_EndPoem)) + { + app.DebugPrintf("Skipped EndPoem as one was already showing\n"); + return false; + } + else + { + newScene = new UIScene_EndPoem(iPad, initData, this); + } + break; + + + // Frontend + case eUIScene_TrialExitUpsell: + newScene = new UIScene_TrialExitUpsell(iPad, initData, this); + break; + case eUIScene_Intro: + newScene = new UIScene_Intro(iPad, initData, this); + break; + case eUIScene_SaveMessage: + newScene = new UIScene_SaveMessage(iPad, initData, this); + break; + case eUIScene_MainMenu: + newScene = new UIScene_MainMenu(iPad, initData, this); + break; + case eUIScene_LoadOrJoinMenu: + newScene = new UIScene_LoadOrJoinMenu(iPad, initData, this); + break; + case eUIScene_LoadMenu: + newScene = new UIScene_LoadMenu(iPad, initData, this); + break; + case eUIScene_JoinMenu: + newScene = new UIScene_JoinMenu(iPad, initData, this); + break; + case eUIScene_CreateWorldMenu: + newScene = new UIScene_CreateWorldMenu(iPad, initData, this); + break; + case eUIScene_LaunchMoreOptionsMenu: + newScene = new UIScene_LaunchMoreOptionsMenu(iPad, initData, this); + break; + case eUIScene_FullscreenProgress: + newScene = new UIScene_FullscreenProgress(iPad, initData, this); + break; + case eUIScene_LeaderboardsMenu: + newScene = new UIScene_LeaderboardsMenu(iPad, initData, this); + break; + case eUIScene_DLCMainMenu: + newScene = new UIScene_DLCMainMenu(iPad, initData, this); + break; + case eUIScene_DLCOffersMenu: + newScene = new UIScene_DLCOffersMenu(iPad, initData, this); + break; + case eUIScene_EULA: + newScene = new UIScene_EULA(iPad, initData, this); + break; + + // Other + case eUIScene_Keyboard: + newScene = new UIScene_Keyboard(iPad, initData, this); + break; + case eUIScene_QuadrantSignin: + newScene = new UIScene_QuadrantSignin(iPad, initData, this); + break; + case eUIScene_MessageBox: + if(IsSceneInStack(eUIScene_MessageBox)) + { + app.DebugPrintf("Skipped MessageBox as one was already showing\n"); + return false; + } + else + { + newScene = new UIScene_MessageBox(iPad, initData, this); + } + break; + case eUIScene_Timer: + newScene = new UIScene_Timer(iPad, initData, this); + break; + }; + + if(newScene == NULL) + { + app.DebugPrintf("WARNING: Scene %d was not created. Add it to UILayer::NavigateToScene\n", scene); + return false; + } + + if(m_sceneStack.size() > 0) + { + newScene->setBackScene(m_sceneStack[m_sceneStack.size()-1]); + } + + m_sceneStack.push_back(newScene); + + updateFocusState(); + + newScene->tick(); + + return true; +} + +bool UILayer::NavigateBack(int iPad, EUIScene eScene) +{ + if(m_sceneStack.size() == 0) return false; + + bool navigated = false; + if(eScene < eUIScene_COUNT) + { + UIScene *scene = NULL; + do + { + scene = m_sceneStack.back(); + if(scene->getSceneType() == eScene) + { + navigated = true; + break; + } + else + { + if(scene->hasFocus(iPad)) + { + removeScene(scene); + } + else + { + // No focus on the top scene, so this use shouldn't be navigating! + break; + } + } + } while(m_sceneStack.size() > 0); + + } + else + { + UIScene *scene = m_sceneStack.back(); + if(scene->hasFocus(iPad)) + { + removeScene(scene); + navigated = true; + } + } + return navigated; +} + +void UILayer::showComponent(int iPad, EUIScene scene, bool show) +{ + AUTO_VAR(it,m_componentRefCount.find(scene)); + if(it != m_componentRefCount.end()) + { + it->second.second = show; + return; + } + if(show) addComponent(iPad,scene); +} + +bool UILayer::isComponentVisible(EUIScene scene) +{ + bool visible = false; + AUTO_VAR(it,m_componentRefCount.find(scene)); + if(it != m_componentRefCount.end()) + { + visible = it->second.second; + } + return visible; +} + +UIScene *UILayer::addComponent(int iPad, EUIScene scene, void *initData) +{ + AUTO_VAR(it,m_componentRefCount.find(scene)); + if(it != m_componentRefCount.end()) + { + ++it->second.first; + + for(AUTO_VAR(itComp,m_components.begin()); itComp != m_components.end(); ++itComp) + { + if( (*itComp)->getSceneType() == scene ) + { + return *itComp; + } + } + return NULL; + } + UIScene *newScene = NULL; + + switch(scene) + { + case eUIComponent_Panorama: + newScene = new UIComponent_Panorama(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_DebugUIConsole: + newScene = new UIComponent_DebugUIConsole(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_DebugUIMarketingGuide: + newScene = new UIComponent_DebugUIMarketingGuide(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_Logo: + newScene = new UIComponent_Logo(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_Tooltips: + newScene = new UIComponent_Tooltips(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_TutorialPopup: + newScene = new UIComponent_TutorialPopup(iPad, initData, this); + // Start hidden + m_componentRefCount[scene] = pair(1,false); + break; + case eUIScene_HUD: + newScene = new UIScene_HUD(iPad, initData, this); + // Start hidden + m_componentRefCount[scene] = pair(1,false); + break; + case eUIComponent_Chat: + newScene = new UIComponent_Chat(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_PressStartToPlay: + newScene = new UIComponent_PressStartToPlay(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + case eUIComponent_MenuBackground: + newScene = new UIComponent_MenuBackground(iPad, initData, this); + m_componentRefCount[scene] = pair(1,true); + break; + }; + + if(newScene == NULL) return NULL; + + m_components.push_back(newScene); + + return newScene; +} + +void UILayer::removeComponent(EUIScene scene) +{ + AUTO_VAR(it,m_componentRefCount.find(scene)); + if(it != m_componentRefCount.end()) + { + --it->second.first; + + if(it->second.first <= 0) + { + m_componentRefCount.erase(it); + for(AUTO_VAR(compIt, m_components.begin()) ; compIt != m_components.end(); ) + { + if( (*compIt)->getSceneType() == scene) + { +#ifdef __PSVITA__ + // remove any touchboxes + ui.TouchBoxesClear((*compIt)); +#endif + m_scenesToDelete.push_back((*compIt)); + (*compIt)->handleDestroy(); // For anything that might require the pointer be valid + compIt = m_components.erase(compIt); + } + else + { + ++compIt; + } + } + } + } +} + +void UILayer::removeScene(UIScene *scene) +{ +#ifdef __PSVITA__ + // remove any touchboxes + ui.TouchBoxesClear(scene); +#endif + + AUTO_VAR(newEnd, std::remove(m_sceneStack.begin(), m_sceneStack.end(), scene) ); + m_sceneStack.erase(newEnd, m_sceneStack.end()); + + m_scenesToDelete.push_back(scene); + + scene->handleDestroy(); // For anything that might require the pointer be valid + + bool hadFocus = m_hasFocus; + updateFocusState(); + + // If this layer has focus, pass it on + if (m_hasFocus || hadFocus) + { + m_hasFocus = false; + m_parentGroup->UpdateFocusState(); + } +} + +void UILayer::closeAllScenes() +{ + vector temp; + temp.insert(temp.end(), m_sceneStack.begin(), m_sceneStack.end()); + m_sceneStack.clear(); + for(AUTO_VAR(it, temp.begin()); it != temp.end(); ++it) + { +#ifdef __PSVITA__ + // remove any touchboxes + ui.TouchBoxesClear(*it); +#endif + m_scenesToDelete.push_back(*it); + (*it)->handleDestroy(); // For anything that might require the pointer be valid + } + + updateFocusState(); + + // If this layer has focus, pass it on + if (m_hasFocus) + { + m_hasFocus = false; + m_parentGroup->UpdateFocusState(); + } +} + +// Get top scene on stack (or NULL if stack is empty) +UIScene *UILayer::GetTopScene() +{ + if(m_sceneStack.size() == 0) + { + return NULL; + } + else + { + return m_sceneStack[m_sceneStack.size()-1]; + } +} + +// Updates layer focus state if no error message is present (unless this is the error layer) +bool UILayer::updateFocusState(bool allowedFocus /* = false */) +{ + // If haveFocus is false, request it + if (!allowedFocus) + { + // To update focus in this layer we need to request focus from group + // Focus will be denied if there's an upper layer that needs focus + allowedFocus = m_parentGroup->RequestFocus(this); + } + + m_bMenuDisplayed = false; + m_bPauseMenuDisplayed = false; + m_bContainerMenuDisplayed = false; + m_bIgnoreAutosaveMenuDisplayed = false; + m_bIgnorePlayerJoinMenuDisplayed = false; + + bool layerFocusSet = false; + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *scene = *it; + + // UPDATE FOCUS STATES + if(!layerFocusSet && allowedFocus && scene->stealsFocus()) + { + scene->gainFocus(); + layerFocusSet = true; + } + else + { + scene->loseFocus(); + if(allowedFocus && app.GetGameStarted()) + { + // 4J Stu - This is a memory optimisation so we don't keep scenes loaded in memory all the time + // This is required for PS3 (and likely Vita), but I'm removing it on XboxOne so that we can avoid + // the scene creation time (which can be >0.5s) since we have the memory to spare +#ifndef _XBOX_ONE + m_scenesToDestroy.push_back(scene); +#endif + } + } + + /// UPDATE STACK STATES + + // 4J-PB - this should just be true + m_bMenuDisplayed=true; + + EUIScene sceneType = scene->getSceneType(); + switch(sceneType) + { + case eUIScene_PauseMenu: + m_bPauseMenuDisplayed = true; + break; + case eUIScene_Crafting2x2Menu: + case eUIScene_Crafting3x3Menu: + case eUIScene_FurnaceMenu: + case eUIScene_ContainerMenu: + case eUIScene_LargeContainerMenu: + case eUIScene_InventoryMenu: + case eUIScene_CreativeMenu: + case eUIScene_DispenserMenu: + case eUIScene_BrewingStandMenu: + case eUIScene_EnchantingMenu: + m_bContainerMenuDisplayed=true; + + // Intentional fall-through + case eUIScene_DeathMenu: + case eUIScene_FullscreenProgress: + case eUIScene_SignEntryMenu: + case eUIScene_EndPoem: + m_bIgnoreAutosaveMenuDisplayed = true; + break; + } + + switch(sceneType) + { + case eUIScene_FullscreenProgress: + case eUIScene_EndPoem: + case eUIScene_Credits: + case eUIScene_LeaderboardsMenu: + m_bIgnorePlayerJoinMenuDisplayed = true; + break; + } + } + m_hasFocus = layerFocusSet; + + return m_hasFocus; +} + +#ifdef __PSVITA__ +UIScene *UILayer::getCurrentScene() +{ + // Note: reverse iterator, the last element is the top of the stack + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *scene = *it; + // 4J-PB - only used on Vita, so iPad 0 is fine + if(scene->hasFocus(0) && scene->canHandleInput()) + { + return scene; + } +} + + return NULL; +} +#endif + +void UILayer::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + // Note: reverse iterator, the last element is the top of the stack + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *scene = *it; + if(scene->hasFocus(iPad) && scene->canHandleInput()) + { + // 4J-PB - ignore repeats of action ABXY buttons + // fix for PS3 213 - [MAIN MENU] Holding down buttons will continue to activate every prompt. + // 4J Stu - Changed this slightly to add the allowRepeat function so we can allow repeats in the crafting menu + if(repeat && !scene->allowRepeat(key) ) + { + return; + } + scene->handleInput(iPad, key, repeat, pressed, released, handled); + } + + // Fix for PS3 #444 - [IN GAME] If the user keeps pressing CROSS while on the 'Save Game' screen the title will crash. + handled = handled || scene->hidesLowerScenes(); + if(handled ) break; + } + + // Components can't take input or focus +} + +void UILayer::HandleDLCMountingComplete() +{ + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *topScene = *it; + app.DebugPrintf("UILayer::HandleDLCMountingComplete - topScene\n"); + topScene->HandleDLCMountingComplete(); + } +} + +void UILayer::HandleDLCInstalled() +{ + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *topScene = *it; + topScene->HandleDLCInstalled(); + } +} + +#ifdef _XBOX_ONE +void UILayer::HandleDLCLicenseChange() +{ + for(AUTO_VAR(it,m_sceneStack.rbegin()); it != m_sceneStack.rend(); ++it) + { + UIScene *topScene = *it; + topScene->HandleDLCLicenseChange(); + } +} +#endif + +bool UILayer::IsFullscreenGroup() +{ + return m_parentGroup->IsFullscreenGroup(); +} + +C4JRender::eViewportType UILayer::getViewport() +{ + return m_parentGroup->GetViewportType(); +} + + +void UILayer::handleUnlockFullVersion() +{ + for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) + { + (*it)->handleUnlockFullVersion(); + } +} + +void UILayer::PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic) +{ + __int64 layerStatic = 0; + __int64 layerDynamic = 0; + for(AUTO_VAR(it,m_components.begin()); it != m_components.end(); ++it) + { + (*it)->PrintTotalMemoryUsage(layerStatic, layerDynamic); + } + for(AUTO_VAR(it, m_sceneStack.begin()); it != m_sceneStack.end(); ++it) + { + (*it)->PrintTotalMemoryUsage(layerStatic, layerDynamic); + } + app.DebugPrintf(app.USER_SR, " \\- Layer static: %d , Layer dynamic: %d\n", layerStatic, layerDynamic); + totalStatic += layerStatic; + totalDynamic += layerDynamic; +} + +// Returns the first scene of given type if it exists, NULL otherwise +UIScene *UILayer::FindScene(EUIScene sceneType) +{ + for (int i = 0; i < m_sceneStack.size(); i++) + { + if (m_sceneStack[i]->getSceneType() == sceneType) + { + return m_sceneStack[i]; + } + } + + return NULL; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UILayer.h b/Minecraft.Client/Common/UI/UILayer.h new file mode 100644 index 0000000..2840f23 --- /dev/null +++ b/Minecraft.Client/Common/UI/UILayer.h @@ -0,0 +1,91 @@ +#pragma once +#include "UIEnums.h" +using namespace std; +class UIScene; +class UIGroup; + +// A layer include a collection of scenes and other components +class UILayer +{ +private: + vector m_sceneStack; // Operates as a stack mainly, but we may wish to iterate over all elements + vector m_components; // Other componenents in this scene that to do not conform the the user nav stack, and cannot take focus + vector m_scenesToDelete; // A list of scenes to delete + vector m_scenesToDestroy; // A list of scenes where we want to dump the swf + +#ifdef __ORBIS__ + unordered_map,std::hash> m_componentRefCount; +#else + unordered_map > m_componentRefCount; +#endif + +public: + bool m_hasFocus; // True if the layer "has focus", should be the only layer in the group + bool m_bMenuDisplayed; + bool m_bPauseMenuDisplayed; + bool m_bContainerMenuDisplayed; + bool m_bIgnoreAutosaveMenuDisplayed; + bool m_bIgnorePlayerJoinMenuDisplayed; + +#ifdef __PSVITA__ + EUILayer m_iLayer; +#endif + + UIGroup *m_parentGroup; +public: + UILayer(UIGroup *parent); + + void tick(); + void render(S32 width, S32 height, C4JRender::eViewportType viewport); + void getRenderDimensions(S32 &width, S32 &height); + + void DestroyAll(); + void ReloadAll(bool force = false); + + // NAVIGATION + bool NavigateToScene(int iPad, EUIScene scene, void *initData); + bool NavigateBack(int iPad, EUIScene eScene); + void removeScene(UIScene *scene); + void closeAllScenes(); + UIScene *GetTopScene(); + + bool GetMenuDisplayed(); + bool IsPauseMenuDisplayed() { return m_bPauseMenuDisplayed; } + + bool IsSceneInStack(EUIScene scene); + bool HasFocus(int iPad); + + bool hidesLowerScenes(); + + // A component is an element on a layer that displays BELOW other scenes in this layer, but does not engage in any navigation + // E.g. you can keep a component active while performing navigation with other scenes on this layer + void showComponent(int iPad, EUIScene scene, bool show); + bool isComponentVisible(EUIScene scene); + UIScene *addComponent(int iPad, EUIScene scene, void *initData = NULL); + void removeComponent(EUIScene scene); + + // INPUT + void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); +#ifdef __PSVITA__ + // Current active scene + UIScene *getCurrentScene(); +#endif + // FOCUS + + bool updateFocusState(bool allowedFocus = false); + +public: + bool IsFullscreenGroup(); + C4JRender::eViewportType getViewport(); + + virtual void HandleDLCMountingComplete(); + virtual void HandleDLCInstalled(); +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + void handleUnlockFullVersion(); + UIScene *FindScene(EUIScene sceneType); + + void PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic); + +}; diff --git a/Minecraft.Client/Common/UI/UIScene.cpp b/Minecraft.Client/Common/UI/UIScene.cpp new file mode 100644 index 0000000..e7d907e --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene.cpp @@ -0,0 +1,1248 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene.h" + +#include "..\..\Lighting.h" +#include "..\..\LocalPlayer.h" +#include "..\..\ItemRenderer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +UIScene::UIScene(int iPad, UILayer *parentLayer) +{ + m_parentLayer = parentLayer; + m_iPad = iPad; + swf = NULL; + m_pItemRenderer = NULL; + + bHasFocus = false; + m_hasTickedOnce = false; + m_bFocussedOnce = false; + m_bVisible = true; + m_bCanHandleInput = false; + m_bIsReloading = false; + + m_iFocusControl = -1; + m_iFocusChild = 0; + m_lastOpacity = 1.0f; + m_bUpdateOpacity = false; + + m_backScene = NULL; + + m_cacheSlotRenders = false; + m_needsCacheRendered = true; + m_expectedCachedSlotCount = 0; + m_callbackUniqueId = 0; +} + +UIScene::~UIScene() +{ + /* Destroy the Iggy player. */ + IggyPlayerDestroy( swf ); + + for(AUTO_VAR(it,m_registeredTextures.begin()); it != m_registeredTextures.end(); ++it) + { + ui.unregisterSubstitutionTexture( it->first, it->second ); + } + + if(m_callbackUniqueId != 0) + { + ui.UnregisterCallbackId(m_callbackUniqueId); + } + + if(m_pItemRenderer != NULL) delete m_pItemRenderer; +} + +void UIScene::destroyMovie() +{ + /* Destroy the Iggy player. */ + IggyPlayerDestroy( swf ); + swf = NULL; + + // Clear out the controls collection (doesn't delete the controls, and they get re-setup later) + m_controls.clear(); + + // Clear out all the fast names for the current movie + m_fastNames.clear(); +} + +void UIScene::reloadMovie(bool force) +{ + if(!force && (stealsFocus() && (getSceneType() != eUIScene_FullscreenProgress && !bHasFocus))) return; + + m_bIsReloading = true; + if(swf) + { + /* Destroy the Iggy player. */ + IggyPlayerDestroy( swf ); + + // Clear out the controls collection (doesn't delete the controls, and they get re-setup later) + m_controls.clear(); + + // Clear out all the fast names for the current movie + m_fastNames.clear(); + } + + // Reload everything + initialiseMovie(); + + handlePreReload(); + + // Reload controls + for(AUTO_VAR(it, m_controls.begin()); it != m_controls.end(); ++it) + { + (*it)->ReInit(); + } + + handleReload(); + + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_iFocusControl; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetFocus , 1 , value ); + + m_needsCacheRendered = true; + m_bIsReloading = false; +} + +bool UIScene::needsReloaded() +{ + return !swf && (!stealsFocus() || bHasFocus); +} + +bool UIScene::hasMovie() +{ + return swf != NULL; +} + +F64 UIScene::getSafeZoneHalfHeight() +{ + float height = ui.getScreenHeight(); + + float safeHeight = 0.0f; + +#ifndef __PSVITA__ + if( !RenderManager.IsHiDef() && RenderManager.IsWidescreen() ) + { + // 90% safezone + safeHeight = height * (0.15f / 2); + } + else + { + // 90% safezone + safeHeight = height * (0.1f / 2); + } +#endif + return safeHeight; +} + +F64 UIScene::getSafeZoneHalfWidth() +{ + float width = ui.getScreenWidth(); + + float safeWidth = 0.0f; +#ifndef __PSVITA__ + if( !RenderManager.IsHiDef() && RenderManager.IsWidescreen() ) + { + // 85% safezone + safeWidth = width * (0.15f / 2); + } + else + { + // 90% safezone + safeWidth = width * (0.1f / 2); + } +#endif + return safeWidth; +} + +void UIScene::updateSafeZone() +{ + // Distance from edge + F64 safeTop = 0.0; + F64 safeBottom = 0.0; + F64 safeLeft = 0.0; + F64 safeRight = 0.0; + + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + safeTop = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + safeBottom = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + safeTop = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + safeTop = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + safeBottom = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + safeTop = getSafeZoneHalfHeight(); + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + safeRight = getSafeZoneHalfWidth(); + break; + } + setSafeZone(safeTop, safeBottom, safeLeft, safeRight); +} + +void UIScene::setSafeZone(S32 safeTop, S32 safeBottom, S32 safeLeft, S32 safeRight) +{ + IggyDataValue result; + IggyDataValue value[4]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = safeTop; + value[1].type = IGGY_DATATYPE_number; + value[1].number = safeBottom; + value[2].type = IGGY_DATATYPE_number; + value[2].number = safeLeft; + value[3].type = IGGY_DATATYPE_number; + value[3].number = safeRight; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetSafeZone , 4 , value ); +} + +void UIScene::initialiseMovie() +{ + loadMovie(); + mapElementsAndNames(); + + updateSafeZone(); + + m_bUpdateOpacity = true; +} + +#ifdef __PSVITA__ +void UIScene::SetFocusToElement(int iID) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iID; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetFocus , 1 , value ); + + // also trigger handle focus change (just in case if anything else in relation needs updating!) + _handleFocusChange(iID, 0); +} +#endif + +bool UIScene::mapElementsAndNames() +{ + m_rootPath = IggyPlayerRootPath( swf ); + + m_funcRemoveObject = registerFastName( L"RemoveObject" ); + m_funcSlideLeft = registerFastName( L"SlideLeft" ); + m_funcSlideRight = registerFastName( L"SlideRight" ); + m_funcSetSafeZone = registerFastName( L"SetSafeZone" ); + m_funcSetAlpha = registerFastName( L"SetAlpha" ); + m_funcSetFocus = registerFastName( L"SetFocus" ); + m_funcHorizontalResizeCheck = registerFastName( L"DoHorizontalResizeCheck"); + return true; +} + +extern CRITICAL_SECTION s_loadSkinCS; +void UIScene::loadMovie() +{ + EnterCriticalSection(&UIController::ms_reloadSkinCS); // MGH - added to prevent crash loading Iggy movies while the skins were being reloaded + wstring moviePath = getMoviePath(); + +#ifdef __PS3__ + if(RenderManager.IsWidescreen()) + { + moviePath.append(L"720.swf"); + m_loadedResolution = eSceneResolution_720; + } + else + { + moviePath.append(L"480.swf"); + m_loadedResolution = eSceneResolution_480; + } +#elif defined __PSVITA__ + moviePath.append(L"Vita.swf"); + m_loadedResolution = eSceneResolution_Vita; +#elif defined _WINDOWS64 + if(ui.getScreenHeight() == 720) + { + moviePath.append(L"720.swf"); + m_loadedResolution = eSceneResolution_720; + } + else if(ui.getScreenHeight() == 480) + { + moviePath.append(L"480.swf"); + m_loadedResolution = eSceneResolution_480; + } + else if(ui.getScreenHeight() < 720) + { + moviePath.append(L"Vita.swf"); + m_loadedResolution = eSceneResolution_Vita; + } + else + { + moviePath.append(L"1080.swf"); + m_loadedResolution = eSceneResolution_1080; + } +#else + moviePath.append(L"1080.swf"); + m_loadedResolution = eSceneResolution_1080; +#endif + + if(!app.hasArchiveFile(moviePath)) + { + app.DebugPrintf("WARNING: Could not find iggy movie %ls, falling back on 720\n", moviePath.c_str()); + + moviePath = getMoviePath(); + moviePath.append(L"720.swf"); + m_loadedResolution = eSceneResolution_720; + + if(!app.hasArchiveFile(moviePath)) + { + app.DebugPrintf("ERROR: Could not find any iggy movie for %ls!\n", moviePath.c_str()); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + } + } + + byteArray baFile = ui.getMovieData(moviePath.c_str()); + __int64 beforeLoad = ui.iggyAllocCount; + swf = IggyPlayerCreateFromMemory ( baFile.data , baFile.length, NULL); + __int64 afterLoad = ui.iggyAllocCount; + IggyPlayerInitializeAndTickRS ( swf ); + __int64 afterTick = ui.iggyAllocCount; + + if(!swf) + { + app.DebugPrintf("ERROR: Failed to load iggy scene!\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + } + app.DebugPrintf( app.USER_SR, "Loaded iggy movie %ls\n", moviePath.c_str() ); + IggyProperties *properties = IggyPlayerProperties ( swf ); + m_movieHeight = properties->movie_height_in_pixels; + m_movieWidth = properties->movie_width_in_pixels; + + m_renderWidth = m_movieWidth; + m_renderHeight = m_movieHeight; + + S32 width, height; + m_parentLayer->getRenderDimensions(width, height); + IggyPlayerSetDisplaySize( swf, width, height ); + + IggyPlayerSetUserdata(swf,this); + +//#ifdef _DEBUG +#if 0 + IggyMemoryUseInfo memoryInfo; + rrbool res; + int iteration = 0; + __int64 totalStatic = 0; + __int64 totalDynamic = 0; + while(res = IggyDebugGetMemoryUseInfo ( swf , + NULL , + 0 , + 0 , + iteration , + &memoryInfo )) + { + totalStatic += memoryInfo.static_allocation_bytes; + totalDynamic += memoryInfo.dynamic_allocation_bytes; + app.DebugPrintf(app.USER_SR, "%ls - %.*s static: %d ( %d ) dynamic: %d ( %d )\n", moviePath.c_str(), memoryInfo.subcategory_stringlen, memoryInfo.subcategory, + memoryInfo.static_allocation_bytes, memoryInfo.static_allocation_count, memoryInfo.dynamic_allocation_bytes, memoryInfo.dynamic_allocation_count); + ++iteration; + //if(memoryInfo.static_allocation_bytes > 0) getDebugMemoryUseRecursive(moviePath, memoryInfo); + + } + + app.DebugPrintf(app.USER_SR, "%ls - Total: %d, Expected: %d, Diff: %d\n", moviePath.c_str(), totalStatic + totalDynamic, afterTick - beforeLoad, (afterTick - beforeLoad) - (totalStatic + totalDynamic)); + +#endif + LeaveCriticalSection(&UIController::ms_reloadSkinCS); + +} + +void UIScene::getDebugMemoryUseRecursive(const wstring &moviePath, IggyMemoryUseInfo &memoryInfo) +{ + rrbool res; + IggyMemoryUseInfo internalMemoryInfo; + int internalIteration = 0; + while(res = IggyDebugGetMemoryUseInfo ( swf , + NULL , + memoryInfo.subcategory , + memoryInfo.subcategory_stringlen , + internalIteration , + &internalMemoryInfo )) + { + app.DebugPrintf(app.USER_SR, "%ls - %.*s static: %d ( %d ) dynamic: %d ( %d )\n", moviePath.c_str(), internalMemoryInfo.subcategory_stringlen, internalMemoryInfo.subcategory, + internalMemoryInfo.static_allocation_bytes, internalMemoryInfo.static_allocation_count, internalMemoryInfo.dynamic_allocation_bytes, internalMemoryInfo.dynamic_allocation_count); + ++internalIteration; + if(internalMemoryInfo.subcategory_stringlen > memoryInfo.subcategory_stringlen) getDebugMemoryUseRecursive(moviePath, internalMemoryInfo); + } +} + +void UIScene::PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic) +{ + if(!swf) return; + + IggyMemoryUseInfo memoryInfo; + rrbool res; + int iteration = 0; + __int64 sceneStatic = 0; + __int64 sceneDynamic = 0; + while(res = IggyDebugGetMemoryUseInfo ( swf , + NULL , + "" , + 0 , + iteration , + &memoryInfo )) + { + sceneStatic += memoryInfo.static_allocation_bytes; + sceneDynamic += memoryInfo.dynamic_allocation_bytes; + totalStatic += memoryInfo.static_allocation_bytes; + totalDynamic += memoryInfo.dynamic_allocation_bytes; + ++iteration; + + } + + app.DebugPrintf(app.USER_SR, " \\- Scene static: %d , Scene dynamic: %d , Total: %d - %ls\n", sceneStatic, sceneDynamic, sceneStatic + sceneDynamic, getMoviePath().c_str()); +} + +void UIScene::tick() +{ + if(m_bIsReloading) return; + if(m_hasTickedOnce) m_bCanHandleInput = true; + while(IggyPlayerReadyToTick( swf )) + { + tickTimers(); + for(AUTO_VAR(it, m_controls.begin()); it != m_controls.end(); ++it) + { + (*it)->tick(); + } + IggyPlayerTickRS( swf ); + m_hasTickedOnce = true; + } +} + +UIControl* UIScene::GetMainPanel() +{ + return NULL; +} + + +void UIScene::addTimer(int id, int ms) +{ + int currentTime = System::currentTimeMillis(); + + TimerInfo info; + info.running = true; + info.duration = ms; + info.targetTime = currentTime + ms; + m_timers[id] = info; +} + +void UIScene::killTimer(int id) +{ + AUTO_VAR(it, m_timers.find(id)); + if(it != m_timers.end()) + { + it->second.running = false; + } +} + +void UIScene::tickTimers() +{ + int currentTime = System::currentTimeMillis(); + for(AUTO_VAR(it, m_timers.begin()); it != m_timers.end();) + { + if(!it->second.running) + { + it = m_timers.erase(it); + } + else + { + if(currentTime > it->second.targetTime) + { + handleTimerComplete(it->first); + + // Auto-restart + it->second.targetTime = it->second.duration + currentTime; + } + ++it; + } + } +} + +IggyName UIScene::registerFastName(const wstring &name) +{ + IggyName var; + AUTO_VAR(it,m_fastNames.find(name)); + if(it != m_fastNames.end()) + { + var = it->second; + } + else + { + var = IggyPlayerCreateFastName ( getMovie() , (IggyUTF16 *)name.c_str() , -1 ); + m_fastNames[name] = var; + } + return var; +} + +void UIScene::removeControl( UIControl_Base *control, bool centreScene) +{ + IggyDataValue result; + IggyDataValue value[2]; + + string name = control->getControlName(); + IggyStringUTF8 stringVal; + stringVal.string = (char*)name.c_str(); + stringVal.length = name.length(); + value[0].type = IGGY_DATATYPE_string_UTF8; + value[0].string8 = stringVal; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = centreScene; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcRemoveObject , 2 , value ); + +#ifdef __PSVITA__ + // update the button positions since they may have changed + UpdateSceneControls(); + + // mark the button as removed + control->setHidden(true); + // remove it from the touchboxes + ui.TouchBoxRebuild(control->getParentScene()); +#endif + +} + +void UIScene::slideLeft() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSlideLeft , 0 , NULL ); +} + +void UIScene::slideRight() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSlideRight , 0 , NULL ); +} + +void UIScene::doHorizontalResizeCheck() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcHorizontalResizeCheck , 0 , NULL ); +} + +void UIScene::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(m_bIsReloading) return; + if(!m_hasTickedOnce || !swf) return; + ui.setupRenderPosition(viewport); + IggyPlayerSetDisplaySize( swf, width, height ); + IggyPlayerDraw( swf ); +} + +void UIScene::setOpacity(float percent) +{ + if(percent != m_lastOpacity || (m_bUpdateOpacity && getMovie())) + { + m_lastOpacity = percent; + + // 4J-TomK once a scene has been freshly loaded or re-loaded we force update opacity via initialiseMovie + if(m_bUpdateOpacity) + m_bUpdateOpacity = false; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = percent; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetAlpha , 1 , value ); + } +} + +void UIScene::setVisible(bool visible) +{ + m_bVisible = visible; +} + +void UIScene::customDraw(IggyCustomDrawCallbackRegion *region) +{ + app.DebugPrintf("Handling custom draw for scene with no override!\n"); +} + +void UIScene::customDrawSlotControl(IggyCustomDrawCallbackRegion *region, int iPad, shared_ptr item, float fAlpha, bool isFoil, bool bDecorations) +{ + if (item!= NULL) + { + if(m_cacheSlotRenders) + { + if( (m_cachedSlotDraw.size() + 1) == m_expectedCachedSlotCount) + { + //Make sure that pMinecraft->player is the correct player so that player specific rendering + // eg clock and compass, are rendered correctly + Minecraft *pMinecraft=Minecraft::GetInstance(); + shared_ptr oldPlayer = pMinecraft->player; + if( iPad >= 0 && iPad < XUSER_MAX_COUNT ) pMinecraft->player = pMinecraft->localplayers[iPad]; + + // Setup GDraw, normal game render states and matrices + //CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + PIXBeginNamedEvent(0,"Starting Iggy custom draw\n"); + CustomDrawData *customDrawRegion = ui.calculateCustomDraw(region); + ui.beginIggyCustomDraw4J(region, customDrawRegion); + ui.setupCustomDrawGameState(); + + int list = m_parentLayer->m_parentGroup->getCommandBufferList(); + + bool useCommandBuffers = false; +#ifdef _XBOX_ONE + useCommandBuffers = true; + + // 4J Stu - Temporary until we fix the glint animation which needs updated if we are just replaying a command buffer + m_needsCacheRendered = true; +#endif + + if(!useCommandBuffers || m_needsCacheRendered) + { +#if (!defined __PS3__) && (!defined __PSVITA__) + if(useCommandBuffers) RenderManager.CBuffStart(list, true); +#endif + PIXBeginNamedEvent(0,"Draw uncached"); + ui.setupCustomDrawMatrices(this, customDrawRegion); + _customDrawSlotControl(customDrawRegion, iPad, item, fAlpha, isFoil, bDecorations, useCommandBuffers); + delete customDrawRegion; + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Draw all cache"); + // Draw all the cached slots + for(AUTO_VAR(it, m_cachedSlotDraw.begin()); it != m_cachedSlotDraw.end(); ++it) + { + CachedSlotDrawData *drawData = *it; + ui.setupCustomDrawMatrices(this, drawData->customDrawRegion); + _customDrawSlotControl(drawData->customDrawRegion, iPad, drawData->item, drawData->fAlpha, drawData->isFoil, drawData->bDecorations, useCommandBuffers); + delete drawData->customDrawRegion; + delete drawData; + } + PIXEndNamedEvent(); +#ifndef __PS3__ + if(useCommandBuffers) RenderManager.CBuffEnd(); +#endif + } + m_cachedSlotDraw.clear(); + +#ifndef __PS3__ + if(useCommandBuffers) RenderManager.CBuffCall(list); +#endif + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + + pMinecraft->player = oldPlayer; + } + else + { + PIXBeginNamedEvent(0,"Caching region"); + CachedSlotDrawData *drawData = new CachedSlotDrawData(); + drawData->item = item; + drawData->fAlpha = fAlpha; + drawData->isFoil = isFoil; + drawData->bDecorations = bDecorations; + drawData->customDrawRegion = ui.calculateCustomDraw(region); + + m_cachedSlotDraw.push_back(drawData); + PIXEndNamedEvent(); + } + } + else + { + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + //Make sure that pMinecraft->player is the correct player so that player specific rendering + // eg clock and compass, are rendered correctly + shared_ptr oldPlayer = pMinecraft->player; + if( iPad >= 0 && iPad < XUSER_MAX_COUNT ) pMinecraft->player = pMinecraft->localplayers[iPad]; + + _customDrawSlotControl(customDrawRegion, iPad, item, fAlpha, isFoil, bDecorations, false); + delete customDrawRegion; + pMinecraft->player = oldPlayer; + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + } + } +} + +void UIScene::_customDrawSlotControl(CustomDrawData *region, int iPad, shared_ptr item, float fAlpha, bool isFoil, bool bDecorations, bool usingCommandBuffer) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + float bwidth,bheight; + bwidth = region->x1 - region->x0; + bheight = region->y1 - region->y0; + + float x = region->x0; + float y = region->y0; + + // Base scale on height of this control, compared to height of what the item renderer normally renders (16 pixels high). Potentially + // we might want separate x & y scales here + + float scaleX = bwidth / 16.0f; + float scaleY = bheight / 16.0f; + + glEnable(GL_RESCALE_NORMAL); + glPushMatrix(); + glRotatef(120, 1, 0, 0); + Lighting::turnOn(); + glPopMatrix(); + + float pop = item->popTime; + if (pop > 0) + { + glPushMatrix(); + float squeeze = 1 + pop / (float) Inventory::POP_TIME_DURATION; + float sx = x; + float sy = y; + float sxoffs = 8 * scaleX; + float syoffs = 12 * scaleY; + glTranslatef((float)(sx + sxoffs), (float)(sy + syoffs), 0); + glScalef(1 / squeeze, (squeeze + 1) / 2, 1); + glTranslatef((float)-(sx + sxoffs), (float)-(sy + syoffs), 0); + } + + PIXBeginNamedEvent(0,"Render and decorate"); + if(m_pItemRenderer == NULL) m_pItemRenderer = new ItemRenderer(); + m_pItemRenderer->renderAndDecorateItem(pMinecraft->font, pMinecraft->textures, item, x, y,scaleX,scaleY,fAlpha,isFoil,false, !usingCommandBuffer); + PIXEndNamedEvent(); + + if (pop > 0) + { + glPopMatrix(); + } + + if(bDecorations) + { + if((scaleX!=1.0f) ||(scaleY!=1.0f)) + { + glPushMatrix(); + glScalef(scaleX, scaleY, 1.0f); + int iX= (int)(0.5f+((float)x)/scaleX); + int iY= (int)(0.5f+((float)y)/scaleY); + + m_pItemRenderer->renderGuiItemDecorations(pMinecraft->font, pMinecraft->textures, item, iX, iY, fAlpha); + glPopMatrix(); + } + else + { + m_pItemRenderer->renderGuiItemDecorations(pMinecraft->font, pMinecraft->textures, item, (int)x, (int)y, fAlpha); + } + } + + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +} + +// 4J Stu - Not threadsafe +//void UIScene::navigateForward(int iPad, EUIScene scene, void *initData) +//{ +// if(m_parentLayer == NULL) +// { +// app.DebugPrintf("A scene is trying to navigate forwards, but it's parent layer is NULL!\n"); +//#ifndef _CONTENT_PACKAGE +// __debugbreak(); +//#endif +// } +// else +// { +// m_parentLayer->NavigateToScene(iPad,scene,initData); +// } +//} + +void UIScene::navigateBack() +{ + //CD - Added for audio + ui.PlayUISFX(eSFX_Back); + + ui.NavigateBack(m_iPad); + + if(m_parentLayer == NULL) + { +// app.DebugPrintf("A scene is trying to navigate back, but it's parent layer is NULL!\n"); +#ifndef _CONTENT_PACKAGE +// __debugbreak(); +#endif + } + else + { +// m_parentLayer->removeScene(this); + +#ifdef _DURANGO + if (ui.GetTopScene(0)) + InputManager.SetEnabledGtcButtons( ui.GetTopScene(0)->getDefaultGtcButtons() ); +#endif + } + +} + +void UIScene::gainFocus() +{ + if( !bHasFocus && stealsFocus() ) + { + // 4J Stu - Don't do this + /* + IggyEvent event; + IggyMakeEventFocusGained( &event , 0); + + IggyEventResult result; + IggyPlayerDispatchEventRS( getMovie() , &event , &result ); + + app.DebugPrintf("Sent gain focus event to scene\n"); + */ + bHasFocus = true; + if(app.GetGameStarted() && needsReloaded()) + { + reloadMovie(); + } + + updateTooltips(); + updateComponents(); + + if(!m_bFocussedOnce) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = -1; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetFocus , 1 , value ); + } + + handleGainFocus(m_bFocussedOnce); + if(bHasFocus) m_bFocussedOnce = true; + } + else if(bHasFocus && stealsFocus()) + { + updateTooltips(); + } +} + +void UIScene::loseFocus() +{ + if(bHasFocus) + { + // 4J Stu - Don't do this + /* + IggyEvent event; + IggyMakeEventFocusLost( &event ); + IggyEventResult result; + IggyPlayerDispatchEventRS ( getMovie() , &event , &result ); + */ + + app.DebugPrintf("Sent lose focus event to scene\n"); + bHasFocus = false; + handleLoseFocus(); + } +} + +void UIScene::handleGainFocus(bool navBack) +{ +#ifdef _DURANGO + InputManager.SetEnabledGtcButtons( this->getDefaultGtcButtons() ); +#endif +} + +void UIScene::updateTooltips() +{ + ui.SetTooltips(m_iPad, -1); +} + +void UIScene::sendInputToMovie(int key, bool repeat, bool pressed, bool released) +{ + if(!swf) return; + + int iggyKeyCode = convertGameActionToIggyKeycode(key); + + if(iggyKeyCode < 0) + { + app.DebugPrintf("UI WARNING: Ignoring input as game action does not translate to an Iggy keycode\n"); + return; + } + IggyEvent keyEvent; + // 4J Stu - Keyloc is always standard as we don't care about shift/alt + IggyMakeEventKey( &keyEvent, pressed?IGGY_KEYEVENT_Down:IGGY_KEYEVENT_Up, (IggyKeycode)iggyKeyCode, IGGY_KEYLOC_Standard ); + + IggyEventResult result; + IggyPlayerDispatchEventRS ( swf , &keyEvent , &result ); +} + +int UIScene::convertGameActionToIggyKeycode(int action) +{ + int keycode = -1; + switch(action) + { +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_A: + keycode = IGGY_KEYCODE_ENTER; + break; + case ACTION_MENU_B: + keycode = IGGY_KEYCODE_ESCAPE; + break; + case ACTION_MENU_X: + keycode = IGGY_KEYCODE_F1; + break; + case ACTION_MENU_Y: + keycode = IGGY_KEYCODE_F2; + break; + case ACTION_MENU_OK: + keycode = IGGY_KEYCODE_ENTER; + break; + case ACTION_MENU_CANCEL: + keycode = IGGY_KEYCODE_ESCAPE; + break; + case ACTION_MENU_UP: + keycode = IGGY_KEYCODE_UP; + break; + case ACTION_MENU_DOWN: + keycode = IGGY_KEYCODE_DOWN; + break; + case ACTION_MENU_RIGHT: + keycode = IGGY_KEYCODE_RIGHT; + break; + case ACTION_MENU_LEFT: + keycode = IGGY_KEYCODE_LEFT; + break; + case ACTION_MENU_PAGEUP: + keycode = IGGY_KEYCODE_PAGE_UP; + break; + case ACTION_MENU_PAGEDOWN: + keycode = IGGY_KEYCODE_PAGE_DOWN; + break; + case ACTION_MENU_RIGHT_SCROLL: + keycode = IGGY_KEYCODE_F3; + break; + case ACTION_MENU_LEFT_SCROLL: + keycode = IGGY_KEYCODE_F4; + break; + case ACTION_MENU_STICK_PRESS: + break; + case ACTION_MENU_OTHER_STICK_PRESS: + break; + case ACTION_MENU_OTHER_STICK_UP: + keycode = IGGY_KEYCODE_F11; + break; + case ACTION_MENU_OTHER_STICK_DOWN: + keycode = IGGY_KEYCODE_F12; + break; + case ACTION_MENU_OTHER_STICK_LEFT: + break; + case ACTION_MENU_OTHER_STICK_RIGHT: + break; + }; + + return keycode; +} + +bool UIScene::allowRepeat(int key) +{ + // 4J-PB - ignore repeats of action ABXY buttons + // fix for PS3 213 - [MAIN MENU] Holding down buttons will continue to activate every prompt. + switch(key) + { + case ACTION_MENU_OK: + case ACTION_MENU_CANCEL: + case ACTION_MENU_A: + case ACTION_MENU_B: + case ACTION_MENU_X: + case ACTION_MENU_Y: + return false; + } + return true; +} + +void UIScene::externalCallback(IggyExternalFunctionCallUTF16 * call) +{ + if(wcscmp((wchar_t *)call->function_name.string,L"handlePress")==0) + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handlePress did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_number) + { + app.DebugPrintf("Arguments for handlePress were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handlePress(call->arguments[0].number, call->arguments[1].number); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleFocusChange")==0) + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handleFocusChange did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_number) + { + app.DebugPrintf("Arguments for handleFocusChange were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + _handleFocusChange(call->arguments[0].number, call->arguments[1].number); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleInitFocus")==0) + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handleInitFocus did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_number) + { + app.DebugPrintf("Arguments for handleInitFocus were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + _handleInitFocus(call->arguments[0].number, call->arguments[1].number); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleCheckboxToggled")==0) + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handleCheckboxToggled did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_boolean) + { + app.DebugPrintf("Arguments for handleCheckboxToggled were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handleCheckboxToggled(call->arguments[0].number, call->arguments[1].boolval); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleSliderMove")==0) + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handleSliderMove did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_number) + { + app.DebugPrintf("Arguments for handleSliderMove were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handleSliderMove(call->arguments[0].number, call->arguments[1].number); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleAnimationEnd")==0) + { + if(call->num_arguments != 0) + { + app.DebugPrintf("Callback for handleAnimationEnd did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handleAnimationEnd(); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleSelectionChanged")==0) + { + if(call->num_arguments != 1) + { + app.DebugPrintf("Callback for handleSelectionChanged did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number) + { + app.DebugPrintf("Arguments for handleSelectionChanged were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handleSelectionChanged(call->arguments[0].number); + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleRequestMoreData")==0) + { + if(call->num_arguments == 0) + { + handleRequestMoreData(0,false); + } + else + { + if(call->num_arguments != 2) + { + app.DebugPrintf("Callback for handleRequestMoreData did not have the correct number of arguments\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + if(call->arguments[0].type != IGGY_DATATYPE_number || call->arguments[1].type != IGGY_DATATYPE_boolean) + { + app.DebugPrintf("Arguments for handleRequestMoreData were not of the correct type\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return; + } + handleRequestMoreData(call->arguments[0].number, call->arguments[1].boolval); + } + } + else if(wcscmp((wchar_t *)call->function_name.string,L"handleTouchBoxRebuild")==0) + { + handleTouchBoxRebuild(); + } + else + { + app.DebugPrintf("Unhandled callback: %s\n", call->function_name.string); + } +} + +void UIScene::registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength, bool deleteData) +{ + m_registeredTextures[textureName] = deleteData;; + ui.registerSubstitutionTexture(textureName, pbData, dwLength); +} + +bool UIScene::hasRegisteredSubstitutionTexture(const wstring &textureName) +{ + AUTO_VAR(it, m_registeredTextures.find( textureName ) ); + + return it != m_registeredTextures.end(); +} + +void UIScene::_handleFocusChange(F64 controlId, F64 childId) +{ + m_iFocusControl = (int)controlId; + m_iFocusChild = (int)childId; + + handleFocusChange(controlId, childId); + ui.PlayUISFX(eSFX_Focus); +} + +void UIScene::_handleInitFocus(F64 controlId, F64 childId) +{ + m_iFocusControl = (int)controlId; + m_iFocusChild = (int)childId; + + //handleInitFocus(controlId, childId); + handleFocusChange(controlId, childId); +} + +bool UIScene::controlHasFocus(int iControlId) +{ + return m_iFocusControl == iControlId; +} + +bool UIScene::controlHasFocus(UIControl_Base *control) +{ + return controlHasFocus( control->getId() ); +} + +int UIScene::getControlChildFocus() +{ + return m_iFocusChild; +} + +int UIScene::getControlFocus() +{ + return m_iFocusControl; +} + +void UIScene::setBackScene(UIScene *scene) +{ + m_backScene = scene; +} + +UIScene *UIScene::getBackScene() +{ + return m_backScene; +} +#ifdef __PSVITA__ +void UIScene::UpdateSceneControls() +{ + AUTO_VAR(itEnd, GetControls()->end()); + for (AUTO_VAR(it, GetControls()->begin()); it != itEnd; it++) + { + UIControl *control=(UIControl *)*it; + control->UpdateControl(); + } +} +#endif + +size_t UIScene::GetCallbackUniqueId() +{ + if( m_callbackUniqueId == 0) + { + m_callbackUniqueId = ui.RegisterForCallbackId(this); + } + return m_callbackUniqueId; +} + +bool UIScene::isReadyToDelete() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene.h b/Minecraft.Client/Common/UI/UIScene.h new file mode 100644 index 0000000..823c510 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene.h @@ -0,0 +1,270 @@ +#pragma once +// 4J-PB - remove the inherits via dominance warnings +#pragma warning( disable : 4250 ) +using namespace std; +// A scene map directly to an Iggy movie (or more accurately a collection of different sized movies) + +#include "UIEnums.h" +#include "UIControl_Base.h" + +class ItemRenderer; +class UILayer; + +// 4J Stu - Setup some defines for quickly mapping elements in the scene + +#define UI_BEGIN_MAP_ELEMENTS_AND_NAMES(parentClass) \ + virtual bool mapElementsAndNames() \ + { \ + parentClass::mapElementsAndNames(); \ + IggyValuePath *currentRoot = IggyPlayerRootPath ( getMovie() ); + +#define UI_END_MAP_ELEMENTS_AND_NAMES() \ + return true; \ + } + +#define UI_MAP_ELEMENT( var, name) \ + { var.setupControl(this, currentRoot , name ); m_controls.push_back(&var); } + +#define UI_BEGIN_MAP_CHILD_ELEMENTS( parent ) \ + { \ + IggyValuePath *lastRoot = currentRoot; \ + currentRoot = parent.getIggyValuePath(); + +#define UI_END_MAP_CHILD_ELEMENTS() \ + currentRoot = lastRoot; \ + } + +#define UI_MAP_NAME( var, name ) \ + { var = registerFastName(name); } + +class UIScene +{ + friend class UILayer; +public: + IggyValuePath *m_rootPath; + +private: + Iggy *swf; + IggyName m_funcRemoveObject, m_funcSlideLeft, m_funcSlideRight, m_funcSetSafeZone, m_funcSetFocus, m_funcHorizontalResizeCheck; + IggyName m_funcSetAlpha; + + ItemRenderer *m_pItemRenderer; + unordered_map m_fastNames; + unordered_map m_registeredTextures; + + typedef struct _TimerInfo + { + int duration; + int targetTime; + bool running; + } TimerInfo; + unordered_map m_timers; + + int m_iFocusControl, m_iFocusChild; + float m_lastOpacity; + bool m_bUpdateOpacity; + bool m_bVisible; + bool m_bCanHandleInput; + UIScene *m_backScene; + + size_t m_callbackUniqueId; + +public: + enum ESceneResolution + { + eSceneResolution_1080, + eSceneResolution_720, + eSceneResolution_480, + eSceneResolution_Vita, + }; + +protected: + ESceneResolution m_loadedResolution; + + bool m_bIsReloading; + bool m_bFocussedOnce; + + int m_movieWidth, m_movieHeight; + int m_renderWidth, m_renderHeight; + vector m_controls; + +protected: + UILayer *m_parentLayer; + bool bHasFocus; + int m_iPad; + bool m_hasTickedOnce; + +public: + virtual Iggy *getMovie() { return swf; } + + void destroyMovie(); + virtual void reloadMovie(bool force = false); + virtual bool needsReloaded(); + virtual bool hasMovie(); + virtual void updateSafeZone(); + + int getRenderWidth() { return m_renderWidth; } + int getRenderHeight() { return m_renderHeight; } + +#ifdef __PSVITA__ + UILayer *GetParentLayer() {return m_parentLayer;} + EUIGroup GetParentLayerGroup() {return m_parentLayer->m_parentGroup->GetGroup();} + vector *GetControls() {return &m_controls;} +#endif + +protected: + virtual F64 getSafeZoneHalfHeight(); + virtual F64 getSafeZoneHalfWidth(); + void setSafeZone(S32 top, S32 bottom, S32 left, S32 right); + void doHorizontalResizeCheck(); + virtual wstring getMoviePath() = 0; + + virtual bool mapElementsAndNames(); + void initialiseMovie(); + void loadMovie(); + +private: + void getDebugMemoryUseRecursive(const wstring &moviePath, IggyMemoryUseInfo &memoryInfo); + +public: + void PrintTotalMemoryUsage(__int64 &totalStatic, __int64 &totalDynamic); + +public: + UIScene(int iPad, UILayer *parentLayer); + virtual ~UIScene(); + + virtual EUIScene getSceneType() = 0; + ESceneResolution getSceneResolution() { return m_loadedResolution; } + + virtual void tick(); + + IggyName registerFastName(const wstring &name); +#ifdef __PSVITA__ + void SetFocusToElement(int iID); + void UpdateSceneControls(); +#endif +protected: + void addTimer(int id, int ms); + void killTimer(int id); + void tickTimers(); + TimerInfo* getTimer(int id) { return &m_timers[id]; } + virtual void handleTimerComplete(int id) {} + +public: + // FOCUS + // Returns true if this scene handles input + virtual bool stealsFocus() { return true; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return bHasFocus && iPad == m_iPad; } + + void gainFocus(); + void loseFocus(); + + virtual void updateTooltips(); + virtual void updateComponents() {} + virtual void handleGainFocus(bool navBack); + virtual void handleLoseFocus() {} + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return m_hasTickedOnce; } + + // returns main panel if controls are not living in the root + virtual UIControl* GetMainPanel(); + + void removeControl( UIControl_Base *control, bool centreScene); + void slideLeft(); + void slideRight(); + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewpBort); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + + void setOpacity(float percent); + void setVisible(bool visible); + bool isVisible() { return m_bVisible; } + +protected: + //void customDrawSlotControl(IggyCustomDrawCallbackRegion *region, int iPad, int iID, int iCount, int iAuxVal, float fAlpha, bool isFoil, bool bDecorations); + void customDrawSlotControl(IggyCustomDrawCallbackRegion *region, int iPad, shared_ptr item, float fAlpha, bool isFoil, bool bDecorations); + + bool m_cacheSlotRenders; + bool m_needsCacheRendered; + int m_expectedCachedSlotCount; +private: + typedef struct _CachedSlotDrawData + { + CustomDrawData *customDrawRegion; + shared_ptr item; + float fAlpha; + bool isFoil; + bool bDecorations; + } CachedSlotDrawData; + vector m_cachedSlotDraw; + + void _customDrawSlotControl(CustomDrawData *region, int iPad, shared_ptr item, float fAlpha, bool isFoil, bool bDecorations, bool usingCommandBuffer); + +public: + // INPUT + bool canHandleInput() { return m_bCanHandleInput; } + virtual bool allowRepeat(int key); + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) {} + void externalCallback(IggyExternalFunctionCallUTF16 * call); + + virtual void handleDestroy() {} +protected: + void sendInputToMovie(int key, bool repeat, bool pressed, bool released); + virtual void handlePreReload() {} + virtual void handleReload() {} + virtual void handlePress(F64 controlId, F64 childId) {} + virtual void handleFocusChange(F64 controlId, F64 childId) {} + virtual void handleInitFocus(F64 controlId, F64 childId) {} + virtual void handleCheckboxToggled(F64 controlId, bool selected) {} + virtual void handleSliderMove(F64 sliderId, F64 currentValue) {} + virtual void handleAnimationEnd() {} + virtual void handleSelectionChanged(F64 selectedId) {} + virtual void handleRequestMoreData(F64 startIndex, bool up) {} + virtual void handleTouchBoxRebuild() {} +private: + void _handleFocusChange(F64 controlId, F64 childId); + void _handleInitFocus(F64 controlId, F64 childId); + + int convertGameActionToIggyKeycode(int action); + +public: + bool controlHasFocus(int iControlId); + bool controlHasFocus(UIControl_Base *control); + int getControlFocus(); + int getControlChildFocus(); + + // NAVIGATION +protected: + //void navigateForward(int iPad, EUIScene scene, void *initData = NULL); + void navigateBack(); + +public: + void setBackScene(UIScene *scene); + UIScene *getBackScene(); + virtual void HandleDLCMountingComplete() {} + virtual void HandleDLCInstalled() {} +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange() {} +#endif + void registerSubstitutionTexture(const wstring &textureName, PBYTE pbData, DWORD dwLength, bool deleteData = false); + bool hasRegisteredSubstitutionTexture(const wstring &textureName); + + virtual void handleUnlockFullVersion() {} + + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) {} + + +protected: +#ifdef _DURANGO + virtual long long getDefaultGtcButtons() { return _360_GTC_BACK; } +#endif + + size_t GetCallbackUniqueId(); + + virtual bool isReadyToDelete(); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp new file mode 100644 index 0000000..a1bd827 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.cpp @@ -0,0 +1,307 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_AbstractContainerMenu.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\MultiplayerLocalPlayer.h" + +UIScene_AbstractContainerMenu::UIScene_AbstractContainerMenu(int iPad, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_focusSection = eSectionNone; + // in this scene, we override the press sound with our own for crafting success or fail + ui.OverrideSFX(m_iPad,ACTION_MENU_A,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,true); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,true); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_X,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_Y,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true); + + m_bIgnoreInput=false; +} + +UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu() +{ + app.DebugPrintf("UIScene_AbstractContainerMenu::~UIScene_AbstractContainerMenu\n"); +} + +void UIScene_AbstractContainerMenu::handleDestroy() +{ + app.DebugPrintf("UIScene_AbstractContainerMenu::handleDestroy\n"); + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(pMinecraft->localplayers[m_iPad] != NULL) pMinecraft->localplayers[m_iPad]->closeContainer(); + + ui.OverrideSFX(m_iPad,ACTION_MENU_A,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,false); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,false); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_X,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_Y,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,false); +} + +void UIScene_AbstractContainerMenu::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex) +{ +} + +void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex) +{ + + m_labelInventory.init( app.GetString(IDS_INVENTORY) ); + + if(startIndex >= 0) + { + m_slotListInventory.addSlots(startIndex, 27); + m_slotListHotbar.addSlots(startIndex + 27, 9); + } + + // Determine min and max extents for pointer, it needs to be able to move off the container to drop items. + float fPanelWidth, fPanelHeight; + float fPanelX, fPanelY; + float fPointerWidth, fPointerHeight; + + // We may have varying depths of controls here, so base off the pointers parent +#if TO_BE_IMPLEMENTED + HXUIOBJ parent; + XuiElementGetBounds( m_pointerControl->m_hObj, &fPointerWidth, &fPointerHeight ); +#else + fPointerWidth = 50; + fPointerHeight = 50; +#endif + + fPanelWidth = m_controlBackgroundPanel.getWidth(); + fPanelHeight = m_controlBackgroundPanel.getHeight(); + fPanelX = m_controlBackgroundPanel.getXPos(); + fPanelY = m_controlBackgroundPanel.getYPos(); + // Get size of pointer + m_fPointerImageOffsetX = 0; //floor(fPointerWidth/2.0f); + m_fPointerImageOffsetY = 0; //floor(fPointerHeight/2.0f); + + m_fPanelMinX = fPanelX; + m_fPanelMaxX = fPanelX + fPanelWidth; + m_fPanelMinY = fPanelY; + m_fPanelMaxY = fPanelY + fPanelHeight; + +#ifdef __ORBIS__ + // we need to map the touchpad rectangle to the UI rectangle. While it works great for the creative menu, it is much too sensitive for the smaller menus. + //X coordinate of the touch point (0 to 1919) + //Y coordinate of the touch point (0 to 941: DUALSHOCK4 wireless controllers and the CUH-ZCT1J/CAP-ZCT1J/CAP-ZCT1U controllers for the PlayStation4 development tool, + //0 to 753: JDX-1000x series controllers for the PlayStation4 development tool,) + m_fTouchPadMulX=fPanelWidth/1919.0f; + m_fTouchPadMulY=fPanelHeight/941.0f; + m_fTouchPadDeadZoneX=15.0f*m_fTouchPadMulX; + m_fTouchPadDeadZoneY=15.0f*m_fTouchPadMulY; + +#endif + + // 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 = fPanelY -fPointerHeight; + } + m_fPointerMinX = fPanelX - fPointerWidth; + m_fPointerMaxX = m_fPanelMaxX + fPointerWidth; + m_fPointerMaxY = m_fPanelMaxY + (fPointerHeight/2); + +// m_hPointerText=NULL; +// m_hPointerTextBkg=NULL; + + // Put the pointer over first item in use row to start with. + 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; + + //m_pointerControl->SetPosition( &vPointerPos ); + m_pointerPos = vPointerPos; + + IggyEvent mouseEvent; + S32 width, height; + m_parentLayer->getRenderDimensions(width, height); + S32 x = m_pointerPos.x*((float)width/m_movieWidth); + S32 y = m_pointerPos.y*((float)height/m_movieHeight); + IggyMakeEventMouseMove( &mouseEvent, x, y); + + IggyEventResult result; + IggyPlayerDispatchEventRS ( getMovie() , &mouseEvent , &result ); + +#ifdef USE_POINTER_ACCEL + m_fPointerVelX = 0.0f; + m_fPointerVelY = 0.0f; + m_fPointerAccelX = 0.0f; + m_fPointerAccelY = 0.0f; +#endif +} + +void UIScene_AbstractContainerMenu::tick() +{ + UIScene::tick(); + + onMouseTick(); + + IggyEvent mouseEvent; + S32 width, height; + m_parentLayer->getRenderDimensions(width, height); + S32 x = m_pointerPos.x*((float)width/m_movieWidth); + S32 y = m_pointerPos.y*((float)height/m_movieHeight); + IggyMakeEventMouseMove( &mouseEvent, x, y); + + // 4J Stu - This seems to be broken on Durango, so do it ourself +#ifdef _DURANGO + //mouseEvent.x = x; + //mouseEvent.y = y; +#endif + + IggyEventResult result; + IggyPlayerDispatchEventRS ( getMovie() , &mouseEvent , &result ); +} + +void UIScene_AbstractContainerMenu::render(S32 width, S32 height, C4JRender::eViewportType viewpBort) +{ + m_cacheSlotRenders = true; + + m_needsCacheRendered = m_needsCacheRendered || m_menu->needsRendered(); + + if(m_needsCacheRendered) + { + m_expectedCachedSlotCount = 0; + unsigned int count = m_menu->getSize(); + for(unsigned int i = 0; i < count; ++i) + { + if(m_menu->getSlot(i)->hasItem()) + { + ++m_expectedCachedSlotCount; + } + } + } + + UIScene::render(width, height, viewpBort); + + m_needsCacheRendered = false; +} + +void UIScene_AbstractContainerMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + shared_ptr item = nullptr; + if(wcscmp((wchar_t *)region->name,L"pointerIcon")==0) + { + m_cacheSlotRenders = false; + item = pMinecraft->localplayers[m_iPad]->inventory->getCarried(); + } + else + { + int slotId = -1; + swscanf((wchar_t*)region->name,L"slot_%d",&slotId); + if (slotId == -1) + { + app.DebugPrintf("This is not the control we are looking for\n"); + } + else + { + m_cacheSlotRenders = true; + Slot *slot = m_menu->getSlot(slotId); + item = slot->getItem(); + } + } + + if(item != NULL) customDrawSlotControl(region,m_iPad,item,1.0f,item->isFoil(),true); +} + +void UIScene_AbstractContainerMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + //app.DebugPrintf("UIScene_InventoryMenu handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + if(pressed) + { + handled = handleKeyDown(m_iPad, key, repeat); + } +} + +void UIScene_AbstractContainerMenu::SetPointerText(const wstring &description, vector &unformattedStrings, bool newSlot) +{ + //app.DebugPrintf("Setting pointer text\n"); + m_cursorPath.setLabel(description,false,newSlot); +} + +void UIScene_AbstractContainerMenu::setSectionFocus(ESceneSection eSection, int iPad) +{ + if(m_focusSection != eSectionNone) + { + UIControl *currentFocus = getSection(m_focusSection); + if(currentFocus) currentFocus->setFocus(false); + } + UIControl *newFocus = getSection(eSection); + if(newFocus) newFocus->setFocus(true); + m_focusSection = eSection; +} + +void UIScene_AbstractContainerMenu::setFocusToPointer(int iPad) +{ + if(m_focusSection != eSectionNone) + { + UIControl *currentFocus = getSection(m_focusSection); + if(currentFocus) currentFocus->setFocus(false); + } + m_focusSection = eSectionNone; +} + +shared_ptr UIScene_AbstractContainerMenu::getSlotItem(ESceneSection eSection, int iSlot) +{ + Slot *slot = m_menu->getSlot( getSectionStartOffset(eSection) + iSlot ); + if(slot) return slot->getItem(); + else return nullptr; +} + +bool UIScene_AbstractContainerMenu::isSlotEmpty(ESceneSection eSection, int iSlot) +{ + Slot *slot = m_menu->getSlot( getSectionStartOffset(eSection) + iSlot ); + if(slot) return !slot->hasItem(); + else return false; +} + +void UIScene_AbstractContainerMenu::adjustPointerForSafeZone() +{ + // Handled by AS +} diff --git a/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h new file mode 100644 index 0000000..b98b376 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_AbstractContainerMenu.h @@ -0,0 +1,63 @@ +#pragma once + +#include "UIScene.h" +#include "IUIScene_AbstractContainerMenu.h" + +class AbstractContainerMenu; + +class UIScene_AbstractContainerMenu : public UIScene, public virtual IUIScene_AbstractContainerMenu +{ +private: + ESceneSection m_focusSection; + bool m_bIgnoreInput; + +protected: + UIControl m_controlMainPanel; + UIControl_SlotList m_slotListHotbar, m_slotListInventory; + UIControl_Cursor m_cursorPath; + UIControl_Label m_labelInventory, m_labelBrewingStand; + UIControl m_controlBackgroundPanel; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_controlBackgroundPanel, "BackgroundPanel" ) + UI_MAP_ELEMENT( m_slotListHotbar, "hotbarList") + UI_MAP_ELEMENT( m_slotListInventory, "inventoryList") + UI_MAP_ELEMENT( m_cursorPath, "cursor") + UI_MAP_ELEMENT( m_labelInventory, "inventoryLabel") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_AbstractContainerMenu(int iPad, UILayer *parentLayer); + ~UIScene_AbstractContainerMenu(); + + virtual void handleDestroy(); + + int getPad() { return m_iPad; } + bool getIgnoreInput() { return m_bIgnoreInput; } + void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; } + +protected: + virtual void PlatformInitialize(int iPad, int startIndex); + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + virtual bool doesSectionTreeHaveFocus(ESceneSection eSection) { return false; } + virtual void setSectionFocus(ESceneSection eSection, int iPad); + void setFocusToPointer(int iPad); + void SetPointerText(const wstring &description, vector &unformattedStrings, bool newSlot); + virtual shared_ptr getSlotItem(ESceneSection eSection, int iSlot); + virtual bool isSlotEmpty(ESceneSection eSection, int iSlot); + virtual void adjustPointerForSafeZone(); + + virtual UIControl *getSection(ESceneSection eSection) { return NULL; } + +public: + virtual void tick(); + + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewpBort); + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp new file mode 100644 index 0000000..7b9886b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.cpp @@ -0,0 +1,400 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "MultiPlayerLocalPlayer.h" +#include "..\..\Minecraft.h" +#include "UIScene_AnvilMenu.h" + +UIScene_AnvilMenu::UIScene_AnvilMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_showingCross = false; + m_textInputAnvil.init(m_itemName,eControl_TextInput); + + m_labelAnvil.init( app.GetString(IDS_REPAIR_AND_NAME) ); + + AnvilScreenInput *initData = (AnvilScreenInput *)_initData; + m_inventory = initData->inventory; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Anvil_Menu, this); + } + + m_repairMenu = new RepairMenu( initData->inventory, initData->level, initData->x, initData->y, initData->z, pMinecraft->localplayers[iPad] ); + m_repairMenu->addSlotListener(this); + + Initialize( iPad, m_repairMenu, true, RepairMenu::INV_SLOT_START, eSectionAnvilUsing, eSectionAnvilMax ); + + m_slotListItem1.addSlots(RepairMenu::INPUT_SLOT, 1); + m_slotListItem2.addSlots(RepairMenu::ADDITIONAL_SLOT, 1); + m_slotListResult.addSlots(RepairMenu::RESULT_SLOT, 1); + + bool expensive = false; + wstring m_costString = L""; + + if(m_repairMenu->cost > 0) + { + if(m_repairMenu->cost >= 40 && !pMinecraft->localplayers[iPad]->abilities.instabuild) + { + m_costString = app.GetString(IDS_REPAIR_EXPENSIVE); + expensive = true; + } + else if(!m_repairMenu->getSlot(RepairMenu::RESULT_SLOT)->hasItem()) + { + // Do nothing + } + else + { + LPCWSTR costString = app.GetString(IDS_REPAIR_COST); + wchar_t temp[256]; + swprintf(temp, 256, costString, m_repairMenu->cost); + m_costString = temp; + if(!m_repairMenu->getSlot(RepairMenu::RESULT_SLOT)->mayPickup(dynamic_pointer_cast(m_inventory->player->shared_from_this()))) + { + expensive = true; + } + } + } + setCostLabel(m_costString, expensive); + + if(initData) delete initData; + + setIgnoreInput(false); + + app.SetRichPresenceContext(iPad, CONTEXT_GAME_STATE_ANVIL); +} + +wstring UIScene_AnvilMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"AnvilMenuSplit"; + } + else + { + return L"AnvilMenu"; + } +} + +void UIScene_AnvilMenu::handleReload() +{ + Initialize( m_iPad, m_menu, true, RepairMenu::INV_SLOT_START, eSectionAnvilUsing, eSectionAnvilMax ); + + m_slotListItem1.addSlots(RepairMenu::INPUT_SLOT, 1); + m_slotListItem2.addSlots(RepairMenu::ADDITIONAL_SLOT, 1); + m_slotListResult.addSlots(RepairMenu::RESULT_SLOT, 1); +} + +void UIScene_AnvilMenu::tick() +{ + UIScene_AbstractContainerMenu::tick(); + + handleTick(); +} + +int UIScene_AnvilMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionAnvilItem1: + cols = 1; + break; + case eSectionAnvilItem2: + cols = 1; + break; + case eSectionAnvilResult: + cols = 1; + break; + case eSectionAnvilInventory: + cols = 9; + break; + case eSectionAnvilUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_AnvilMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionAnvilItem1: + rows = 1; + break; + case eSectionAnvilItem2: + rows = 1; + break; + case eSectionAnvilResult: + rows = 1; + break; + case eSectionAnvilInventory: + rows = 3; + break; + case eSectionAnvilUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_AnvilMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionAnvilItem1: + pPosition->x = m_slotListItem1.getXPos(); + pPosition->y = m_slotListItem1.getYPos(); + break; + case eSectionAnvilItem2: + pPosition->x = m_slotListItem2.getXPos(); + pPosition->y = m_slotListItem2.getYPos(); + break; + case eSectionAnvilResult: + pPosition->x = m_slotListResult.getXPos(); + pPosition->y = m_slotListResult.getYPos(); + break; + case eSectionAnvilName: + pPosition->x = m_textInputAnvil.getXPos(); + pPosition->y = m_textInputAnvil.getYPos(); + break; + case eSectionAnvilInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionAnvilUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_AnvilMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + + switch( eSection ) + { + case eSectionAnvilItem1: + sectionSize.x = m_slotListItem1.getWidth(); + sectionSize.y = m_slotListItem1.getHeight(); + break; + case eSectionAnvilItem2: + sectionSize.x = m_slotListItem2.getWidth(); + sectionSize.y = m_slotListItem2.getHeight(); + break; + case eSectionAnvilResult: + sectionSize.x = m_slotListResult.getWidth(); + sectionSize.y = m_slotListResult.getHeight(); + break; + case eSectionAnvilName: + sectionSize.x = m_textInputAnvil.getWidth(); + sectionSize.y = m_textInputAnvil.getHeight(); + break; + case eSectionAnvilInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionAnvilUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + if(IsSectionSlotList(eSection)) + { + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; + } + else + { + GetPositionOfSection(eSection, pPosition); + pSize->x = sectionSize.x; + pSize->y = sectionSize.y; + } +} + +void UIScene_AnvilMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionAnvilItem1: + slotList = &m_slotListItem1; + break; + case eSectionAnvilItem2: + slotList = &m_slotListItem2; + break; + case eSectionAnvilResult: + slotList = &m_slotListResult; + break; + case eSectionAnvilInventory: + slotList = &m_slotListInventory; + break; + case eSectionAnvilUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_AnvilMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionAnvilItem1: + control = &m_slotListItem1; + break; + case eSectionAnvilItem2: + control = &m_slotListItem2; + break; + case eSectionAnvilResult: + control = &m_slotListResult; + break; + case eSectionAnvilName: + control = &m_textInputAnvil; + break; + case eSectionAnvilInventory: + control = &m_slotListInventory; + break; + case eSectionAnvilUsing: + control = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + return control; +} + +int UIScene_AnvilMenu::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) +{ + // 4J HEG - No reason to set value if keyboard was cancelled + UIScene_AnvilMenu *pClass=(UIScene_AnvilMenu *)lpParam; + pClass->setIgnoreInput(false); + + if (bRes) + { + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); + InputManager.GetText(pchText); + pClass->setEditNameValue((wchar_t *)pchText); + pClass->m_itemName = (wchar_t *)pchText; + pClass->updateItemName(); + } + return 0; +} + +void UIScene_AnvilMenu::handleEditNamePressed() +{ + setIgnoreInput(true); +#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ + int language = XGetLanguage(); + switch(language) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_TCHINESE: + InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); + break; + default: + // 4J Stu - Use a different keyboard for non-asian languages so we don't have prediction on + InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Alphabet_Extended); + break; + } +#else + InputManager.RequestKeyboard(app.GetString(IDS_TITLE_RENAME),m_textInputAnvil.getLabel(),(DWORD)m_iPad,30,&UIScene_AnvilMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); +#endif +} + +void UIScene_AnvilMenu::setEditNameValue(const wstring &name) +{ + m_textInputAnvil.setLabel(name); +} + +void UIScene_AnvilMenu::setEditNameEditable(bool enabled) +{ +} + +void UIScene_AnvilMenu::setCostLabel(const wstring &label, bool canAfford) +{ + IggyDataValue result; + IggyDataValue value[2]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = canAfford; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetCostLabel , 2 , value ); +} + +void UIScene_AnvilMenu::showCross(bool show) +{ + if(m_showingCross != show) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowRedCross , 1 , value ); + + m_showingCross = show; + } +} + +void UIScene_AnvilMenu::handleDestroy() +{ +#ifdef __PSVITA__ + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); +#endif + + // another player destroyed the anvil, so shut down the keyboard if it is displayed +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + InputManager.DestroyKeyboard(); +#endif + UIScene_AbstractContainerMenu::handleDestroy(); +} diff --git a/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h new file mode 100644 index 0000000..3afc633 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_AnvilMenu.h @@ -0,0 +1,66 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_AnvilMenu.h" +#include "..\Minecraft.World\MerchantMenu.h" + +class InventoryMenu; + +class UIScene_AnvilMenu : public UIScene_AbstractContainerMenu, public IUIScene_AnvilMenu +{ +private: + bool m_showingCross; + + enum EControls + { + eControl_TextInput, + }; + +public: + UIScene_AnvilMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_AnvilMenu;} + +protected: + UIControl_SlotList m_slotListItem1, m_slotListItem2, m_slotListResult; + UIControl_Label m_labelAnvil; + UIControl_TextInput m_textInputAnvil; + + IggyName m_funcShowRedCross, m_funcSetCostLabel; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListItem1, "Ingredient") + UI_MAP_ELEMENT( m_slotListItem2, "Ingredient2") + UI_MAP_ELEMENT( m_slotListResult, "Result") + UI_MAP_ELEMENT( m_labelAnvil, "AnvilText") + UI_MAP_ELEMENT( m_textInputAnvil, "AnvilTextInput") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME(m_funcShowRedCross, L"ShowRedCross") + UI_MAP_NAME(m_funcSetCostLabel, L"SetCostLabel") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual void tick(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); + + static int KeyboardCompleteCallback(LPVOID lpParam,bool bRes); + virtual void handleEditNamePressed(); + virtual void setEditNameValue(const wstring &name); + virtual void setEditNameEditable(bool enabled); + virtual void handleDestroy(); + + void setCostLabel(const wstring &label, bool canAfford); + void showCross(bool show); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.cpp b/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.cpp new file mode 100644 index 0000000..cd56bd8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.cpp @@ -0,0 +1,309 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.alchemy.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\Minecraft.h" +#include "UIScene_BrewingStandMenu.h" + +UIScene_BrewingStandMenu::UIScene_BrewingStandMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_progressBrewingArrow.init(L"",0,0,PotionBrewing::BREWING_TIME_SECONDS * SharedConstants::TICKS_PER_SECOND,0); + m_progressBrewingBubbles.init(L"",0,0,30,0); + + m_labelBrewingStand.init( app.GetString(IDS_BREWING_STAND) ); + + BrewingScreenInput *initData = (BrewingScreenInput *)_initData; + m_brewingStand = initData->brewingStand; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Brewing_Menu, this); + } + + BrewingStandMenu* menu = new BrewingStandMenu( initData->inventory, initData->brewingStand ); + + Initialize( initData->iPad, menu, true, BrewingStandMenu::INV_SLOT_START, eSectionBrewingUsing, eSectionBrewingMax ); + + m_slotListIngredient.addSlots(BrewingStandMenu::INGREDIENT_SLOT, 1); + + for(unsigned int i = 0; i < 3; ++i) + { + m_slotListBottles[i].addSlots(BrewingStandMenu::BOTTLE_SLOT_START + i, 1); + } + + if(initData) delete initData; + + app.SetRichPresenceContext(iPad, CONTEXT_GAME_STATE_BREWING); +} + +wstring UIScene_BrewingStandMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"BrewingStandMenuSplit"; + } + else + { + return L"BrewingStandMenu"; + } +} + +void UIScene_BrewingStandMenu::handleReload() +{ + Initialize( m_iPad, m_menu, true, BrewingStandMenu::INV_SLOT_START, eSectionBrewingUsing, eSectionBrewingMax ); + + m_slotListIngredient.addSlots(BrewingStandMenu::INGREDIENT_SLOT, 1); + + for(unsigned int i = 0; i < 3; ++i) + { + m_slotListBottles[i].addSlots(BrewingStandMenu::BOTTLE_SLOT_START + i, 1); + } +} + +void UIScene_BrewingStandMenu::tick() +{ + m_progressBrewingArrow.setProgress( m_brewingStand->getBrewTime() ); + + int value = 0; + int bubbleStep = (m_brewingStand->getBrewTime() / 2) % 7; + switch (bubbleStep) + { + case 0: + value = 0; + break; + case 6: + value = 5; + break; + case 5: + value = 10; + break; + case 4: + value = 15; + break; + case 3: + value = 20; + break; + case 2: + value = 25; + break; + case 1: + value = 30; + break; + } + m_progressBrewingBubbles.setProgress( value); + UIScene_AbstractContainerMenu::tick(); +} + +int UIScene_BrewingStandMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionBrewingBottle1: + cols = 1; + break; + case eSectionBrewingBottle2: + cols = 1; + break; + case eSectionBrewingBottle3: + cols = 1; + break; + case eSectionBrewingIngredient: + cols = 1; + break; + case eSectionBrewingInventory: + cols = 9; + break; + case eSectionBrewingUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_BrewingStandMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionBrewingBottle1: + rows = 1; + break; + case eSectionBrewingBottle2: + rows = 1; + break; + case eSectionBrewingBottle3: + rows = 1; + break; + case eSectionBrewingIngredient: + rows = 1; + break; + case eSectionBrewingInventory: + rows = 3; + break; + case eSectionBrewingUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_BrewingStandMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionBrewingBottle1: + pPosition->x = m_slotListBottles[0].getXPos(); + pPosition->y = m_slotListBottles[0].getYPos(); + break; + case eSectionBrewingBottle2: + pPosition->x = m_slotListBottles[1].getXPos(); + pPosition->y = m_slotListBottles[1].getYPos(); + break; + case eSectionBrewingBottle3: + pPosition->x = m_slotListBottles[2].getXPos(); + pPosition->y = m_slotListBottles[2].getYPos(); + break; + case eSectionBrewingIngredient: + pPosition->x = m_slotListIngredient.getXPos(); + pPosition->y = m_slotListIngredient.getYPos(); + break; + case eSectionBrewingInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionBrewingUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_BrewingStandMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + + switch( eSection ) + { + case eSectionBrewingBottle1: + sectionSize.x = m_slotListBottles[0].getWidth(); + sectionSize.y = m_slotListBottles[0].getHeight(); + break; + case eSectionBrewingBottle2: + sectionSize.x = m_slotListBottles[1].getWidth(); + sectionSize.y = m_slotListBottles[1].getHeight(); + break; + case eSectionBrewingBottle3: + sectionSize.x = m_slotListBottles[2].getWidth(); + sectionSize.y = m_slotListBottles[2].getHeight(); + break; + case eSectionBrewingIngredient: + sectionSize.x = m_slotListIngredient.getWidth(); + sectionSize.y = m_slotListIngredient.getHeight(); + break; + case eSectionBrewingInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionBrewingUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; +} + +void UIScene_BrewingStandMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionBrewingBottle1: + slotList = &m_slotListBottles[0]; + break; + case eSectionBrewingBottle2: + slotList = &m_slotListBottles[1]; + break; + case eSectionBrewingBottle3: + slotList = &m_slotListBottles[2]; + break; + case eSectionBrewingIngredient: + slotList = &m_slotListIngredient; + break; + case eSectionBrewingInventory: + slotList = &m_slotListInventory; + break; + case eSectionBrewingUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_BrewingStandMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionBrewingBottle1: + control = &m_slotListBottles[0]; + break; + case eSectionBrewingBottle2: + control = &m_slotListBottles[1]; + break; + case eSectionBrewingBottle3: + control = &m_slotListBottles[2]; + break; + case eSectionBrewingIngredient: + control = &m_slotListIngredient; + break; + case eSectionBrewingInventory: + control = &m_slotListInventory; + break; + case eSectionBrewingUsing: + control = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + return control; +} diff --git a/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.h b/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.h new file mode 100644 index 0000000..5441a1a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_BrewingStandMenu.h @@ -0,0 +1,49 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_BrewingMenu.h" + +class InventoryMenu; + +class UIScene_BrewingStandMenu : public UIScene_AbstractContainerMenu, public IUIScene_BrewingMenu +{ +private: + shared_ptr m_brewingStand; + +public: + UIScene_BrewingStandMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_BrewingStandMenu;} + +protected: + UIControl_SlotList m_slotListBottles[3], m_slotListIngredient; + UIControl_Label m_labelBrewingStand; + UIControl_Progress m_progressBrewingArrow, m_progressBrewingBubbles; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListBottles[0], "Bottle1") + UI_MAP_ELEMENT( m_slotListBottles[1], "Bottle2") + UI_MAP_ELEMENT( m_slotListBottles[2], "Bottle3") + UI_MAP_ELEMENT( m_slotListIngredient, "Ingredient") + UI_MAP_ELEMENT( m_labelBrewingStand, "BrewingStandText") + + UI_MAP_ELEMENT( m_progressBrewingArrow, "BrewingArrow") + UI_MAP_ELEMENT( m_progressBrewingBubbles, "BrewingBubbles") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual void tick(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp new file mode 100644 index 0000000..0ea99a3 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp @@ -0,0 +1,267 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_ConnectingProgress.h" +#include "..\..\Minecraft.h" + +UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + parentLayer->addComponent(iPad,eUIComponent_Panorama); + parentLayer->addComponent(iPad,eUIComponent_Logo); + + m_progressBar.showBar(false); + m_progressBar.setVisible( false ); + m_labelTip.setVisible( false ); + + ConnectionProgressParams *param = (ConnectionProgressParams *)_initData; + + if( param->stringId >= 0 ) + { + m_labelTitle.init( app.GetString( param->stringId ) ); + } + else + { + m_labelTitle.init( L"" ); + } + m_progressBar.init(L"",0,0,100,0); + m_buttonConfirm.init( app.GetString( IDS_CONFIRM_OK ), eControl_Confirm ); + m_buttonConfirm.setVisible(false); + +#if 0 + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + } +#endif + + m_showTooltips = param->showTooltips; + m_runFailTimer = param->setFailTimer; + m_timerTime = param->timerTime; + m_cancelFunc = param->cancelFunc; + m_cancelFuncParam = param->cancelFuncParam; + m_removeLocalPlayer = false; + m_showingButton = false; +} + +UIScene_ConnectingProgress::~UIScene_ConnectingProgress() +{ + m_parentLayer->removeComponent(eUIComponent_Panorama); + m_parentLayer->removeComponent(eUIComponent_Logo); +} + +void UIScene_ConnectingProgress::updateTooltips() +{ + // 4J-PB - removing the option of cancel join, since it didn't work anyway + //ui.SetTooltips( m_iPad, -1, m_showTooltips?IDS_TOOLTIPS_CANCEL_JOIN:-1); + ui.SetTooltips( m_iPad, -1, -1); +} + +void UIScene_ConnectingProgress::tick() +{ + UIScene::tick(); + + if( m_removeLocalPlayer ) + { + m_removeLocalPlayer = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->removeLocalPlayerIdx(m_iPad); +#ifdef _XBOX_ONE + ProfileManager.RemoveGamepadFromGame(m_iPad); +#endif + } +} + +wstring UIScene_ConnectingProgress::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1 && !m_parentLayer->IsFullscreenGroup()) + { + return L"FullscreenProgressSplit"; + } + else + { + return L"FullscreenProgress"; + } +} + +void UIScene_ConnectingProgress::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + if(!navBack && m_runFailTimer) addTimer(0,m_timerTime); +} + +void UIScene_ConnectingProgress::handleLoseFocus() +{ + int millisecsLeft = getTimer(0)->targetTime - System::currentTimeMillis(); + int millisecsTaken = getTimer(0)->duration - millisecsLeft; + app.DebugPrintf("\n"); + app.DebugPrintf("---------------------------------------------------------\n"); + app.DebugPrintf("---------------------------------------------------------\n"); + app.DebugPrintf("UIScene_ConnectingProgress time taken = %d millisecs\n", millisecsTaken); + app.DebugPrintf("---------------------------------------------------------\n"); + app.DebugPrintf("---------------------------------------------------------\n"); + app.DebugPrintf("\n"); + + + killTimer(0); +} + +void UIScene_ConnectingProgress::handleTimerComplete(int id) +{ + // Check if the connection failed + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->m_connectionFailed[m_iPad] || !g_NetworkManager.IsInSession() ) + { + +#if 0 + app.RemoveBackScene(m_iPad); +#endif + + int exitReasonStringId; + switch(pMinecraft->m_connectionFailedReason[m_iPad]) + { + case DisconnectPacket::eDisconnect_LoginTooLong: + exitReasonStringId = IDS_DISCONNECTED_LOGIN_TOO_LONG; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + break; + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + break; +#if defined(__PS3__) || defined(__ORBIS__) + case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: + exitReasonStringId = IDS_CONTENT_RESTRICTION_MULTIPLAYER; + break; + case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: + exitReasonStringId = IDS_CONTENT_RESTRICTION; + break; +#endif +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + break; +#endif + case DisconnectPacket::eDisconnect_NoFlying: + exitReasonStringId = IDS_DISCONNECTED_FLYING; + break; + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + break; +#if defined __ORBIS__ || defined __PS3__ || defined __PSVITA__ + case DisconnectPacket::eDisconnect_NATMismatch: + exitReasonStringId = IDS_DISCONNECTED_NAT_TYPE_MISMATCH; + break; +#endif + default: + exitReasonStringId = IDS_CONNECTION_LOST_SERVER; + break; + } + + if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() ) + { + m_buttonConfirm.setVisible(true); + m_showingButton = true; + + // Set text + m_labelTitle.setLabel( app.GetString( IDS_CONNECTION_FAILED ) ); + m_progressBar.setLabel( app.GetString( exitReasonStringId ) ); + m_progressBar.setVisible( true ); + m_controlTimer.setVisible( false ); + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + + //app.NavigateToHomeMenu(); + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); + } + } +} + +void UIScene_ConnectingProgress::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + if( m_showTooltips ) + { + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { +// 4J-PB - Removed the option to cancel join - it didn't work anyway +// case ACTION_MENU_CANCEL: +// { +// if(m_cancelFunc != NULL) +// { +// m_cancelFunc(m_cancelFuncParam); +// } +// else +// { +// // Cancel the join +// Minecraft *pMinecraft = Minecraft::GetInstance(); +// pMinecraft->removeLocalPlayerIdx(m_iPad); +// } +// handled = true; +// } +// break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + } + } +} + +void UIScene_ConnectingProgress::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Confirm: + if(m_showingButton) + { + if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() ) + { + // The connection failed if we see the button, so the temp player should be removed and the viewports updated again + // This is actually done in the tick as we can't pull down the scene we are currently in from here + m_removeLocalPlayer = true; + } + else + { + ui.NavigateToHomeMenu(); + //app.NavigateBack( ProfileManager.GetPrimaryPad() ); + } + } + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h new file mode 100644 index 0000000..2c52284 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h @@ -0,0 +1,61 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_ConnectingProgress : public UIScene +{ +private: + bool m_runFailTimer; + int m_timerTime; + bool m_showTooltips; + bool m_removeLocalPlayer; + bool m_showingButton; + void (*m_cancelFunc)(LPVOID param); + LPVOID m_cancelFuncParam; + + enum EControls + { + eControl_Confirm + }; + +protected: + UIControl_Progress m_progressBar; + UIControl_Label m_labelTitle, m_labelTip; + UIControl_Button m_buttonConfirm; + UIControl m_controlTimer; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_progressBar, "ProgressBar") + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_MAP_ELEMENT( m_labelTip, "Tip") + UI_MAP_ELEMENT( m_buttonConfirm, "Confirm") + UI_MAP_ELEMENT( m_controlTimer, "Timer") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_ConnectingProgress(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_ConnectingProgress(); + + virtual void tick(); + + virtual EUIScene getSceneType() { return eUIScene_ConnectingProgress;} + + virtual void updateTooltips(); + virtual void handleGainFocus(bool navBack); + virtual void handleLoseFocus(); + + void handleTimerComplete(int id); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +#ifdef _DURANGO + virtual long long getDefaultGtcButtons() { return 0; } +#endif + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_ContainerMenu.cpp b/Minecraft.Client/Common/UI/UIScene_ContainerMenu.cpp new file mode 100644 index 0000000..a0b25d1 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ContainerMenu.cpp @@ -0,0 +1,223 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_ContainerMenu.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\LocalPlayer.h" +#include "..\..\Minecraft.h" +#include "..\Tutorial\Tutorial.h" +#include "..\Tutorial\TutorialMode.h" +#include "..\Tutorial\TutorialEnum.h" + +UIScene_ContainerMenu::UIScene_ContainerMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + ContainerScreenInput *initData = (ContainerScreenInput *)_initData; + m_bLargeChest = (initData->container->getContainerSize() > 3*9)?true:false; + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelChest.init(app.GetString(initData->container->getName())); + + ContainerMenu* menu = new ContainerMenu( initData->inventory, initData->container ); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Container_Menu, this); + } + + int containerSize = menu->getSize() - (27 + 9); + + Initialize( initData->iPad, menu, true, containerSize, eSectionContainerUsing, eSectionContainerMax); + + m_slotListContainer.addSlots(0, containerSize); + + if(initData) delete initData; +} + +wstring UIScene_ContainerMenu::getMoviePath() +{ + if(m_bLargeChest) + { + if(app.GetLocalPlayerCount() > 1) + { + return L"ChestLargeMenuSplit"; + } + else + { + return L"ChestLargeMenu"; + } + } + else + { + if(app.GetLocalPlayerCount() > 1) + { + return L"ChestMenuSplit"; + } + else + { + return L"ChestMenu"; + } + } +} + +void UIScene_ContainerMenu::handleReload() +{ + int containerSize = m_menu->getSize() - (27 + 9); + + Initialize( m_iPad, m_menu, true, containerSize, eSectionContainerUsing, eSectionContainerMax ); + + m_slotListContainer.addSlots(0, containerSize); +} + +int UIScene_ContainerMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionContainerChest: + cols = 9; + break; + case eSectionContainerInventory: + cols = 9; + break; + case eSectionContainerUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_ContainerMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionContainerChest: + rows = (m_menu->getSize() - (27 + 9)) / 9; + break; + case eSectionContainerInventory: + rows = 3; + break; + case eSectionContainerUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_ContainerMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionContainerChest: + pPosition->x = m_slotListContainer.getXPos(); + pPosition->y = m_slotListContainer.getYPos(); + break; + case eSectionContainerInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionContainerUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_ContainerMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + + switch( eSection ) + { + case eSectionContainerChest: + sectionSize.x = m_slotListContainer.getWidth(); + sectionSize.y = m_slotListContainer.getHeight(); + break; + case eSectionContainerInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionContainerUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; +} + +void UIScene_ContainerMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionContainerChest: + slotList = &m_slotListContainer; + break; + case eSectionContainerInventory: + slotList = &m_slotListInventory; + break; + case eSectionContainerUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_ContainerMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionContainerChest: + control = &m_slotListContainer; + break; + case eSectionContainerInventory: + control = &m_slotListInventory; + break; + case eSectionContainerUsing: + control = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + return control; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_ContainerMenu.h b/Minecraft.Client/Common/UI/UIScene_ContainerMenu.h new file mode 100644 index 0000000..f2ad743 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ContainerMenu.h @@ -0,0 +1,40 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_ContainerMenu.h" + +class InventoryMenu; + +class UIScene_ContainerMenu : public UIScene_AbstractContainerMenu, public IUIScene_ContainerMenu +{ +private: + bool m_bLargeChest; + +public: + UIScene_ContainerMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_ContainerMenu;} + +protected: + UIControl_SlotList m_slotListContainer; + UIControl_Label m_labelChest; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListContainer, "containerList") + UI_MAP_ELEMENT( m_labelChest, "chestLabel") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_ControlsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_ControlsMenu.cpp new file mode 100644 index 0000000..c05b502 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ControlsMenu.cpp @@ -0,0 +1,336 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_ControlsMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" + +UIScene_ControlsMenu::UIScene_ControlsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; +#if defined(_XBOX) || defined(_WIN64) + value[0].number = (F64)0; +#elif defined(_DURANGO) + value[0].number = (F64)1; +#elif defined(__PS3__) + value[0].number = (F64)2; +#elif defined(__ORBIS__) + value[0].number = (F64)3; +#elif defined(__PSVITA__) + value[0].number = (F64)4; +#endif + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetPlatform , 1 , value ); + + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + if(bNotInGame) + { + LPWSTR layoutString = new wchar_t[ 128 ]; + swprintf( layoutString, 128, L"%ls", VER_PRODUCTVERSION_STR_W); + m_labelVersion.init(layoutString); + delete [] layoutString; + } + // 4J-PB - stop the label showing in the in-game controls menu + else + { + m_labelVersion.init(" "); + } + m_bCreativeMode = !bNotInGame && Minecraft::GetInstance()->localplayers[m_iPad] && Minecraft::GetInstance()->localplayers[m_iPad]->abilities.mayfly; + +#ifndef __PSVITA__ +#ifdef __ORBIS__ + // no buttons to initialise if we're running this on PS4 remote play + if(!InputManager.UsingRemoteVita()) +#endif + { + m_buttonLayouts[0].init(L"1", eControl_Button0); + m_buttonLayouts[1].init(L"2", eControl_Button1); + m_buttonLayouts[2].init(L"3", eControl_Button2); + } +#endif + + m_checkboxInvert.init(app.GetString(IDS_INVERT_LOOK), eControl_InvertLook, app.GetGameSettings(m_iPad,eGameSetting_ControlInvertLook)); + m_checkboxSouthpaw.init(app.GetString(IDS_SOUTHPAW), eControl_Southpaw, app.GetGameSettings(m_iPad,eGameSetting_ControlSouthPaw)); + + m_iSchemeTextA[0]=IDS_CONTROLS_SCHEME0; + m_iSchemeTextA[1]=IDS_CONTROLS_SCHEME1; + m_iSchemeTextA[2]=IDS_CONTROLS_SCHEME2; + + int iSelected=app.GetGameSettings(m_iPad,eGameSetting_ControlScheme); + +#ifndef __PSVITA__ + LPWSTR layoutString = new wchar_t[ 128 ]; + swprintf( layoutString, 128, L"%ls : %ls", app.GetString( IDS_CURRENT_LAYOUT ),app.GetString(m_iSchemeTextA[iSelected])); +#ifdef __ORBIS__ + if (!InputManager.UsingRemoteVita()) +#endif + { + m_labelCurrentLayout.init(layoutString); + } +#endif + + m_iCurrentNavigatedControlsLayout = iSelected; + + +#ifdef __ORBIS__ + // don't set controller layout if we're entering the PS4 remote play scene + if(!InputManager.UsingRemoteVita()) +#endif + { + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = (F64)m_iCurrentNavigatedControlsLayout; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetControllerLayout , 1 , value ); + } + +#ifdef __ORBIS__ + // Set mapping to Vita mapping + if (InputManager.UsingRemoteVita()) m_iCurrentNavigatedControlsLayout = 3; +#elif defined __PSVITA__ + // Set mapping to Vita mapping + if (InputManager.IsVitaTV()) m_iCurrentNavigatedControlsLayout = 1; +#endif + + for(unsigned int i = 0; i < e_PadCOUNT; ++i) + { + m_labelsPad[i].init(L""); + m_controlLines[i].setVisible(false); + } + m_bLayoutChanged = false; + + + PositionAllText(m_iPad); +} + +wstring UIScene_ControlsMenu::getMoviePath() +{ +#ifdef __ORBIS__ + if(InputManager.UsingRemoteVita()) + { + return L"ControlsRemotePlay"; + } + else +#endif +#ifdef __PSVITA__ + if(InputManager.IsVitaTV()) + { + return L"ControlsTV"; + } + else +#endif + if(app.GetLocalPlayerCount() > 1) + { + return L"ControlsSplit"; + } + else + { + return L"Controls"; + } +} + +void UIScene_ControlsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_ControlsMenu::tick() +{ + if(m_bLayoutChanged) PositionAllText(m_iPad); + UIScene::tick(); +} + +void UIScene_ControlsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + app.CheckGameSettingsChanged(true,iPad); + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if( pressed ) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + } + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_ControlsMenu::handleCheckboxToggled(F64 controlId, bool selected) +{ + switch((int)controlId) + { + case eControl_InvertLook: + app.SetGameSettings(m_iPad,eGameSetting_ControlInvertLook,(unsigned char)( selected ) ); + break; + case eControl_Southpaw: + app.SetGameSettings(m_iPad,eGameSetting_ControlSouthPaw,(unsigned char)( selected ) ); + PositionAllText(m_iPad); + break; + }; +} + +void UIScene_ControlsMenu::handlePress(F64 controlId, F64 childId) +{ + int control = (int)controlId; + switch(control) + { + case eControl_Button0: + case eControl_Button1: + case eControl_Button2: + app.SetGameSettings(m_iPad,eGameSetting_ControlScheme,(unsigned char)control); + LPWSTR layoutString = new wchar_t[ 128 ]; + swprintf( layoutString, 128, L"%ls : %ls", app.GetString( IDS_CURRENT_LAYOUT ),app.GetString(m_iSchemeTextA[control])); +#ifdef __ORBIS__ + if (!InputManager.UsingRemoteVita()) +#endif + { + m_labelCurrentLayout.setLabel(layoutString); + } + + break; + }; +} + +void UIScene_ControlsMenu::handleFocusChange(F64 controlId, F64 childId) +{ + int control = (int)controlId; + switch(control) + { + case eControl_Button0: + case eControl_Button1: + case eControl_Button2: + m_iCurrentNavigatedControlsLayout=control; + m_bLayoutChanged = true; + break; + }; +} + +void UIScene_ControlsMenu::PositionAllText(int iPad) +{ + for(unsigned int i = 0; i < e_PadCOUNT; ++i) + { + m_labelsPad[i].setLabel(L""); + m_controlLines[i].setVisible(false); + } + + if(m_bCreativeMode) + { + PositionText(iPad,IDS_CONTROLS_JUMPFLY,MINECRAFT_ACTION_JUMP); + } + else + { + PositionText(iPad,IDS_CONTROLS_JUMP,MINECRAFT_ACTION_JUMP); + } + PositionText(iPad,IDS_CONTROLS_INVENTORY,MINECRAFT_ACTION_INVENTORY); + PositionText(iPad,IDS_CONTROLS_PAUSE,MINECRAFT_ACTION_PAUSEMENU); + if(m_bCreativeMode) + { + PositionText(iPad,IDS_CONTROLS_SNEAKFLY,MINECRAFT_ACTION_SNEAK_TOGGLE); + } + else + { + PositionText(iPad,IDS_CONTROLS_SNEAK,MINECRAFT_ACTION_SNEAK_TOGGLE); + } + PositionText(iPad,IDS_CONTROLS_USE,MINECRAFT_ACTION_USE); + PositionText(iPad,IDS_CONTROLS_ACTION,MINECRAFT_ACTION_ACTION); + PositionText(iPad,IDS_CONTROLS_HELDITEM,MINECRAFT_ACTION_RIGHT_SCROLL); + PositionText(iPad,IDS_CONTROLS_HELDITEM,MINECRAFT_ACTION_LEFT_SCROLL); + PositionText(iPad,IDS_CONTROLS_DROP,MINECRAFT_ACTION_DROP); + PositionText(iPad,IDS_CONTROLS_CRAFTING,MINECRAFT_ACTION_CRAFTING); + PositionText(iPad,IDS_CONTROLS_THIRDPERSON,MINECRAFT_ACTION_RENDER_THIRD_PERSON); + PositionText(iPad,IDS_CONTROLS_PLAYERS,MINECRAFT_ACTION_GAME_INFO); + + // Swap for southpaw. + if ( app.GetGameSettings(m_iPad,eGameSetting_ControlSouthPaw) ) + { + // Move + PositionText(iPad,IDS_CONTROLS_LOOK,MINECRAFT_ACTION_RIGHT); + // Look + PositionText(iPad,IDS_CONTROLS_MOVE,MINECRAFT_ACTION_LOOK_RIGHT); + } + else // Normal right handed. + { + // Move + PositionText(iPad,IDS_CONTROLS_MOVE,MINECRAFT_ACTION_RIGHT); + // Look + PositionText(iPad,IDS_CONTROLS_LOOK,MINECRAFT_ACTION_LOOK_RIGHT); + } + + bool layoutHasDpadFly; +#ifdef __PSVITA__ + layoutHasDpadFly = m_iCurrentNavigatedControlsLayout == 1; +#else + layoutHasDpadFly = m_iCurrentNavigatedControlsLayout == 0; +#endif + + // If we're in controls mode 1, and creative mode show the dpad for Creative Mode + if(m_bCreativeMode && layoutHasDpadFly) + { + PositionText(iPad,IDS_CONTROLS_DPAD,MINECRAFT_ACTION_DPAD_LEFT); + } + m_bLayoutChanged = false; +} + +void UIScene_ControlsMenu::PositionText(int iPad,int iTextID, unsigned char ucAction) +{ + unsigned int uiVal = InputManager.GetGameJoypadMaps(m_iCurrentNavigatedControlsLayout, ucAction); + + if (uiVal & _360_JOY_BUTTON_A) PositionTextDirect(iPad, iTextID, e_PadA, true); + if (uiVal & _360_JOY_BUTTON_B) PositionTextDirect(iPad, iTextID, e_PadB, true); + if (uiVal & _360_JOY_BUTTON_X) PositionTextDirect(iPad, iTextID, e_PadX, true); + if (uiVal & _360_JOY_BUTTON_Y) PositionTextDirect(iPad, iTextID, e_PadY, true); + if (uiVal & _360_JOY_BUTTON_BACK) + { +#ifdef __ORBIS__ + PositionTextDirect(iPad, iTextID, (InputManager.UsingRemoteVita() ? e_PadTouch : e_PadBack), true); +#else + PositionTextDirect(iPad, iTextID, e_PadBack, true); +#endif + } + if (uiVal & _360_JOY_BUTTON_START) PositionTextDirect(iPad, iTextID, e_PadStart, true); + if (uiVal & _360_JOY_BUTTON_RB) PositionTextDirect(iPad, iTextID, e_PadRB, true); + if (uiVal & _360_JOY_BUTTON_LB) PositionTextDirect(iPad, iTextID, e_PadLB, true); + if (uiVal & _360_JOY_BUTTON_RTHUMB) PositionTextDirect(iPad, iTextID, e_PadRS_1, true); + if (uiVal & _360_JOY_BUTTON_LTHUMB) PositionTextDirect(iPad, iTextID, e_PadLS_1, true); + // Look + if (uiVal & _360_JOY_BUTTON_RSTICK_RIGHT) PositionTextDirect(iPad, iTextID, e_PadRS_2, true); + // Move + if (uiVal & _360_JOY_BUTTON_LSTICK_RIGHT) PositionTextDirect(iPad, iTextID, e_PadLS_2, true); + if (uiVal & _360_JOY_BUTTON_RT) PositionTextDirect(iPad, iTextID, e_PadRT, true); + if (uiVal & _360_JOY_BUTTON_LT) PositionTextDirect(iPad, iTextID, e_PadLT, true); + if (uiVal & _360_JOY_BUTTON_DPAD_RIGHT) PositionTextDirect(iPad, iTextID, e_PadDPadRight, true); + if (uiVal & _360_JOY_BUTTON_DPAD_LEFT) PositionTextDirect(iPad, iTextID, e_PadDPadLeft, true); + if (uiVal & _360_JOY_BUTTON_DPAD_UP) PositionTextDirect(iPad, iTextID, e_PadDPadUp, true); + if (uiVal & _360_JOY_BUTTON_DPAD_DOWN) PositionTextDirect(iPad, iTextID, e_PadDPadDown, true); + } + +void UIScene_ControlsMenu::PositionTextDirect(int iPad,int iTextID, int iControlDetailsIndex, bool bShow) +{ + LPCWSTR text = app.GetString(iTextID); + + m_labelsPad[iControlDetailsIndex].setLabel(text); + m_controlLines[iControlDetailsIndex].setVisible(bShow); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_ControlsMenu.h b/Minecraft.Client/Common/UI/UIScene_ControlsMenu.h new file mode 100644 index 0000000..538207f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ControlsMenu.h @@ -0,0 +1,141 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_ControlsMenu : public UIScene +{ +private: + enum EControl + { + // Buttons must be first three controls here + eControl_Button0, + eControl_Button1, + eControl_Button2, + eControl_InvertLook, + eControl_Southpaw, + }; + + enum EPadButtons + { + e_PadBack=0, + e_PadLT, + e_PadLB, + e_PadDPadLeft, + e_PadDPadRight, + e_PadDPadUp, + e_PadDPadDown, + e_PadLS_1, + e_PadLS_2, + e_PadStart, + e_PadRT, + e_PadRB, + e_PadY, + e_PadB, + e_PadA, + e_PadX, + e_PadRS_1, + e_PadRS_2, + e_PadTouch, + + e_PadCOUNT, + }; + + int m_iSchemeTextA[3]; + int m_iCurrentNavigatedControlsLayout; + bool m_bCreativeMode; + bool m_bLayoutChanged; + + UIControl_Label m_labelCurrentLayout; + UIControl_Label m_labelVersion; + UIControl_Label m_labelsPad[e_PadCOUNT]; + UIControl m_controlLines[e_PadCOUNT]; + UIControl_Button m_buttonLayouts[3]; + UIControl_CheckBox m_checkboxInvert, m_checkboxSouthpaw; + IggyName m_funcSetPlatform, m_funcSetControllerLayout; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + +#ifndef __PSVITA__ +#ifdef __ORBIS__ + if (!InputManager.UsingRemoteVita()) +#endif + { + UI_MAP_ELEMENT( m_labelCurrentLayout, "CurrentLayout") + + UI_MAP_ELEMENT( m_buttonLayouts[0], "Button1") + UI_MAP_ELEMENT( m_buttonLayouts[1], "Button2") + UI_MAP_ELEMENT( m_buttonLayouts[2], "Button3") + } +#endif + + UI_MAP_ELEMENT( m_labelsPad[e_PadBack], "LabelBack") + UI_MAP_ELEMENT( m_labelsPad[e_PadLT], "LabelLT") + UI_MAP_ELEMENT( m_labelsPad[e_PadLB], "LabelLB") + UI_MAP_ELEMENT( m_labelsPad[e_PadDPadLeft], "LabelDPadLeft") + UI_MAP_ELEMENT( m_labelsPad[e_PadDPadRight], "LabelDPadRight") + UI_MAP_ELEMENT( m_labelsPad[e_PadDPadUp], "LabelDPadUp") + UI_MAP_ELEMENT( m_labelsPad[e_PadDPadDown], "LabelDPadDown") + UI_MAP_ELEMENT( m_labelsPad[e_PadLS_1], "LabelLS_1") + UI_MAP_ELEMENT( m_labelsPad[e_PadLS_2], "LabelLS_2") + UI_MAP_ELEMENT( m_labelsPad[e_PadStart], "LabelStart") + UI_MAP_ELEMENT( m_labelsPad[e_PadRT], "LabelRT") + UI_MAP_ELEMENT( m_labelsPad[e_PadRB], "LabelRB") + UI_MAP_ELEMENT( m_labelsPad[e_PadY], "LabelY") + UI_MAP_ELEMENT( m_labelsPad[e_PadB], "LabelB") + UI_MAP_ELEMENT( m_labelsPad[e_PadA], "LabelA") + UI_MAP_ELEMENT( m_labelsPad[e_PadX], "LabelX") + UI_MAP_ELEMENT( m_labelsPad[e_PadRS_1], "LabelRS_1") + UI_MAP_ELEMENT( m_labelsPad[e_PadRS_2], "LabelRS_2") + UI_MAP_ELEMENT( m_labelsPad[e_PadTouch], "LabelTouch") + + UI_MAP_ELEMENT( m_controlLines[e_PadBack], "LineBack") + UI_MAP_ELEMENT( m_controlLines[e_PadLT], "LineLT") + UI_MAP_ELEMENT( m_controlLines[e_PadLB], "LineLB") + UI_MAP_ELEMENT( m_controlLines[e_PadDPadLeft], "LineDpadLeft") + UI_MAP_ELEMENT( m_controlLines[e_PadDPadRight], "LineDpadRight") + UI_MAP_ELEMENT( m_controlLines[e_PadDPadUp], "LineDpadUp") + UI_MAP_ELEMENT( m_controlLines[e_PadDPadDown], "LineDpadDown") + UI_MAP_ELEMENT( m_controlLines[e_PadLS_1], "LineL3") + UI_MAP_ELEMENT( m_controlLines[e_PadLS_2], "LineLeftStick") + UI_MAP_ELEMENT( m_controlLines[e_PadStart], "LineStart") + UI_MAP_ELEMENT( m_controlLines[e_PadRT], "LineRT") + UI_MAP_ELEMENT( m_controlLines[e_PadRB], "LineRB") + UI_MAP_ELEMENT( m_controlLines[e_PadY], "LineY") + UI_MAP_ELEMENT( m_controlLines[e_PadB], "LineB") + UI_MAP_ELEMENT( m_controlLines[e_PadA], "LineA") + UI_MAP_ELEMENT( m_controlLines[e_PadX], "LineX") + UI_MAP_ELEMENT( m_controlLines[e_PadRS_1], "LineR3") + UI_MAP_ELEMENT( m_controlLines[e_PadRS_2], "LineRightStick") + UI_MAP_ELEMENT( m_controlLines[e_PadTouch], "LineTouch") + + UI_MAP_ELEMENT( m_checkboxInvert, "InvertLook") + UI_MAP_ELEMENT( m_checkboxSouthpaw, "SouthPaw") + + UI_MAP_NAME( m_funcSetPlatform, L"SetPlatform") + UI_MAP_NAME( m_funcSetControllerLayout, L"SetControllerLayout") + UI_MAP_ELEMENT( m_labelVersion, "Version") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_ControlsMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_ControlsMenu;} + + virtual void updateTooltips(); + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleCheckboxToggled(F64 controlId, bool selected); + virtual void handlePress(F64 controlId, F64 childId); + virtual void handleFocusChange(F64 controlId, F64 childId); + +private: + void PositionText(int iPad,int iTextID, unsigned char ucAction); + void PositionTextDirect(int iPad,int iTextID, int iControlDetailsIndex, bool bShow); + void PositionAllText(int iPad); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp new file mode 100644 index 0000000..5b72906 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.cpp @@ -0,0 +1,769 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "UIScene_CraftingMenu.h" + +#ifdef __PSVITA__ +#define GAME_CRAFTING_TOUCHUPDATE_TIMER_ID 0 +#define GAME_CRAFTING_TOUCHUPDATE_TIMER_TIME 100 +#endif + +UIScene_CraftingMenu::UIScene_CraftingMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_bIgnoreKeyPresses = false; + + CraftingPanelScreenInput* initData = (CraftingPanelScreenInput*)_initData; + m_iContainerType=initData->iContainerType; + m_pPlayer=initData->player; + m_bSplitscreen=initData->bSplitscreen; + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + for(unsigned int i = 0; i < 4; ++i) m_labelIngredientsDesc[i].init(L""); + m_labelDescription.init(L""); + m_labelGroupName.init(L""); + m_labelItemName.init(L""); + m_labelInventory.init( app.GetString(IDS_INVENTORY) ); + m_labelIngredients.init( app.GetString(IDS_INGREDIENTS) ); + + if(m_iContainerType==RECIPE_TYPE_2x2) + { + m_menu = m_pPlayer->inventoryMenu; + m_iMenuInventoryStart = InventoryMenu::INV_SLOT_START; + m_iMenuHotBarStart = InventoryMenu::USE_ROW_SLOT_START; + } + else + { + CraftingMenu *menu = new CraftingMenu(m_pPlayer->inventory, m_pPlayer->level, initData->x, initData->y, initData->z); + Minecraft::GetInstance()->localplayers[m_iPad]->containerMenu = menu; + + m_menu = menu; + m_iMenuInventoryStart = CraftingMenu::INV_SLOT_START; + m_iMenuHotBarStart = CraftingMenu::USE_ROW_SLOT_START; + } + m_slotListInventory.addSlots(CRAFTING_INVENTORY_SLOT_START,CRAFTING_INVENTORY_SLOT_END - CRAFTING_INVENTORY_SLOT_START); + m_slotListHotBar.addSlots(CRAFTING_HOTBAR_SLOT_START, CRAFTING_HOTBAR_SLOT_END - CRAFTING_HOTBAR_SLOT_START); + +#if TO_BE_IMPLEMENTED + // if we are in splitscreen, then we need to figure out if we want to move this scene + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + XuiElementSetShow(m_hGrid,TRUE); + XuiElementSetShow(m_hPanel,TRUE); +#endif + + if(m_iContainerType==RECIPE_TYPE_3x3) + { + m_iIngredientsMaxSlotC = m_iIngredients3x3SlotC; + m_pGroupA=(Recipy::_eGroupType *)&m_GroupTypeMapping9GridA; + m_pGroupTabA=(_eGroupTab *)&m_GroupTabBkgMapping3x3A; + m_iCraftablesMaxHSlotC=m_iMaxHSlot3x3C; + } + else + { + m_iIngredientsMaxSlotC = m_iIngredients2x2SlotC; + m_pGroupA=(Recipy::_eGroupType *)&m_GroupTypeMapping4GridA; + m_pGroupTabA=(_eGroupTab *)&m_GroupTabBkgMapping2x2A; + m_iCraftablesMaxHSlotC=m_iMaxHSlot2x2C; + } + +#if TO_BE_IMPLEMENTED + + + // display the first group tab + m_hTabGroupA[m_iGroupIndex].SetShow(TRUE); + + // store the slot 0 position + m_pHSlotsBrushImageControl[0]->GetPosition(&m_vSlot0Pos); + m_pHSlotsBrushImageControl[1]->GetPosition(&vec); + m_fSlotSize=vec.x-m_vSlot0Pos.x; + + // store the slot 0 highlight position + m_hHighlight.GetPosition(&m_vSlot0HighlightPos); + // Store the V slot position + m_hScrollBar2.GetPosition(&m_vSlot0V2ScrollPos); + m_hScrollBar3.GetPosition(&m_vSlot0V3ScrollPos); + + // get the position of the slot from the xui, and apply any offset needed + for(int i=0;iSetShow(FALSE); + } + + XuiElementSetShow(m_hGridInventory,FALSE); + + m_hScrollBar2.SetShow(FALSE); + m_hScrollBar3.SetShow(FALSE); + +#endif + + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_CRAFTING); + setGroupText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + + // Update the tutorial state + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + if(m_iContainerType==RECIPE_TYPE_2x2) + { + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_2x2Crafting_Menu, this); + } + else + { + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_3x3Crafting_Menu, this); + } + } + +#ifdef _TO_BE_IMPLEMENTED + XuiSetTimer(m_hObj,IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); +#endif + + for(unsigned int i = 0; i < 4; ++i) + { + m_slotListIngredients[i].addSlot(CRAFTING_INGREDIENTS_DESCRIPTION_START + i); + } + m_slotListCraftingOutput.addSlot(CRAFTING_OUTPUT_SLOT_START); + m_slotListIngredientsLayout.addSlots(CRAFTING_INGREDIENTS_LAYOUT_START, m_iIngredientsMaxSlotC); + + // 3 Slot vertical scroll + m_slotListCrafting3VSlots[0].addSlot(CRAFTING_V_SLOT_START + 0); + m_slotListCrafting3VSlots[1].addSlot(CRAFTING_V_SLOT_START + 1); + m_slotListCrafting3VSlots[2].addSlot(CRAFTING_V_SLOT_START + 2); + + // 2 Slot vertical scroll + // 2 slot scroll has swapped order + m_slotListCrafting2VSlots[0].addSlot(CRAFTING_V_SLOT_START + 1); + m_slotListCrafting2VSlots[1].addSlot(CRAFTING_V_SLOT_START + 0); + + // 1 Slot scroll (for 480 mainly) + m_slotListCrafting1VSlots.addSlot(CRAFTING_V_SLOT_START); + + m_slotListCraftingHSlots.addSlots(CRAFTING_H_SLOT_START,m_iCraftablesMaxHSlotC); + + // Check which recipes are available with the resources we have + CheckRecipesAvailable(); + // reset the vertical slots + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + UpdateVerticalSlots(); + UpdateHighlight(); + + if(initData) delete initData; + + // in this scene, we override the press sound with our own for crafting success or fail + ui.OverrideSFX(m_iPad,ACTION_MENU_A,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,true); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,true); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true); + + // 4J-PB - Must be after the CanBeMade list has been set up with CheckRecipesAvailable + UpdateTooltips(); + +#ifdef __PSVITA__ + // initialise vita touch controls with ids + for(unsigned int i = 0; i < ETouchInput_Count; ++i) + { + m_TouchInput[i].init(i); + } + ui.TouchBoxRebuild(this); +#endif +} + +void UIScene_CraftingMenu::handleDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + + ui.OverrideSFX(m_iPad,ACTION_MENU_A,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,false); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,false); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,false); +} + +EUIScene UIScene_CraftingMenu::getSceneType() +{ + if(m_iContainerType==RECIPE_TYPE_3x3) + { + return eUIScene_Crafting3x3Menu; + } + else + { + return eUIScene_Crafting2x2Menu; + } +} + +wstring UIScene_CraftingMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + m_bSplitscreen = true; + if(m_iContainerType==RECIPE_TYPE_3x3) + { + return L"Crafting3x3MenuSplit"; + } + else + { + return L"Crafting2x2MenuSplit"; + } + } + else + { + if(m_iContainerType==RECIPE_TYPE_3x3) + { + return L"Crafting3x3Menu"; + } + else + { + return L"Crafting2x2Menu"; + } + } +} + +#ifdef __PSVITA__ +UIControl* UIScene_CraftingMenu::GetMainPanel() +{ + return &m_controlMainPanel; +} + +void UIScene_CraftingMenu::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) +{ + // perform action on release + if(bPressed) + { + if(iId == ETouchInput_CraftingHSlots) + { + m_iCraftingSlotTouchStartY = y; + } + } + else if(bRepeat) + { + if(iId == ETouchInput_CraftingHSlots) + { + if(y >= m_iCraftingSlotTouchStartY + m_TouchInput[ETouchInput_CraftingHSlots].getHeight()) // scroll list down + { + if(iVSlotIndexA[1]==(CanBeMadeA[m_iCurrentSlotHIndex].iCount-1)) + { + iVSlotIndexA[1]=0; + } + else + { + iVSlotIndexA[1]++; + } + ui.PlayUISFX(eSFX_Focus); + + UpdateVerticalSlots(); + UpdateHighlight(); + + m_iCraftingSlotTouchStartY = y; + } + else if(y <= m_iCraftingSlotTouchStartY - m_TouchInput[ETouchInput_CraftingHSlots].getHeight()) // scroll list up + { + if(iVSlotIndexA[1]==0) + { + iVSlotIndexA[1]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + } + else + { + iVSlotIndexA[1]--; + } + ui.PlayUISFX(eSFX_Focus); + + UpdateVerticalSlots(); + UpdateHighlight(); + + m_iCraftingSlotTouchStartY = y; + } + } + } + else if(bReleased) + { + if(iId >= ETouchInput_TouchPanel_0 && iId <= ETouchInput_TouchPanel_6) // Touch Change Group + { + m_iGroupIndex = iId; + // turn on the new group + showTabHighlight(m_iGroupIndex,true); + + m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + CheckRecipesAvailable(); + // reset the vertical slots + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + ui.PlayUISFX(eSFX_Focus); + UpdateVerticalSlots(); + UpdateHighlight(); + setGroupText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + } + else if(iId == ETouchInput_CraftingHSlots) // Touch Change Slot + { + int iMaxHSlots = 0; + if(m_iContainerType==RECIPE_TYPE_3x3) + { + iMaxHSlots = m_iMaxHSlot3x3C; + } + else + { + iMaxHSlots = m_iMaxHSlot2x2C; + } + + int iNewSlot = (x - m_TouchInput[ETouchInput_CraftingHSlots].getXPos() - m_controlMainPanel.getXPos()) / m_TouchInput[ETouchInput_CraftingHSlots].getHeight(); + + int iOldHSlot=m_iCurrentSlotHIndex; + + m_iCurrentSlotHIndex = iNewSlot; + if(m_iCurrentSlotHIndex>=m_iCraftablesMaxHSlotC) m_iCurrentSlotHIndex=0; + m_iCurrentSlotVIndex=1; + // clear the indices + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + + UpdateVerticalSlots(); + UpdateHighlight(); + // re-enable the old hslot + if(CanBeMadeA[iOldHSlot].iCount>0) + { + setShowCraftHSlot(iOldHSlot,true); + } + ui.PlayUISFX(eSFX_Focus); + } + } +} + +void UIScene_CraftingMenu::handleTouchBoxRebuild() +{ + addTimer(GAME_CRAFTING_TOUCHUPDATE_TIMER_ID,GAME_CRAFTING_TOUCHUPDATE_TIMER_TIME); +} + +void UIScene_CraftingMenu::handleTimerComplete(int id) +{ + if(id == GAME_CRAFTING_TOUCHUPDATE_TIMER_ID) + { + // we cannot rebuild touch boxes in an iggy callback because it requires further iggy calls + GetMainPanel()->UpdateControl(); + ui.TouchBoxRebuild(this); + killTimer(GAME_CRAFTING_TOUCHUPDATE_TIMER_ID); + } +} +#endif + +void UIScene_CraftingMenu::handleReload() +{ + m_slotListInventory.addSlots(CRAFTING_INVENTORY_SLOT_START,CRAFTING_INVENTORY_SLOT_END - CRAFTING_INVENTORY_SLOT_START); + m_slotListHotBar.addSlots(CRAFTING_HOTBAR_SLOT_START, CRAFTING_HOTBAR_SLOT_END - CRAFTING_HOTBAR_SLOT_START); + + for(unsigned int i = 0; i < 4; ++i) + { + m_slotListIngredients[i].addSlot(CRAFTING_INGREDIENTS_DESCRIPTION_START + i); + } + m_slotListCraftingOutput.addSlot(CRAFTING_OUTPUT_SLOT_START); + m_slotListIngredientsLayout.addSlots(CRAFTING_INGREDIENTS_LAYOUT_START, m_iIngredientsMaxSlotC); + + // 3 Slot vertical scroll + m_slotListCrafting3VSlots[0].addSlot(CRAFTING_V_SLOT_START + 0); + m_slotListCrafting3VSlots[1].addSlot(CRAFTING_V_SLOT_START + 1); + m_slotListCrafting3VSlots[2].addSlot(CRAFTING_V_SLOT_START + 2); + + // 2 Slot vertical scroll + // 2 slot scroll has swapped order + m_slotListCrafting2VSlots[0].addSlot(CRAFTING_V_SLOT_START + 1); + m_slotListCrafting2VSlots[1].addSlot(CRAFTING_V_SLOT_START + 0); + + // 1 Slot scroll (for 480 mainly) + m_slotListCrafting1VSlots.addSlot(CRAFTING_V_SLOT_START); + + m_slotListCraftingHSlots.addSlots(CRAFTING_H_SLOT_START,m_iCraftablesMaxHSlotC); + + app.DebugPrintf(app.USER_SR,"Reloading MultiPanel\n"); + int temp = m_iDisplayDescription; + m_iDisplayDescription = m_iDisplayDescription==0?1:0; + UpdateMultiPanel(); + m_iDisplayDescription = temp; + UpdateMultiPanel(); + + app.DebugPrintf(app.USER_SR,"Reloading Highlight and scroll\n"); + + // reset the vertical slots + m_iCurrentSlotHIndex = 0; + m_iCurrentSlotVIndex = 1; + iVSlotIndexA[0]=CanBeMadeA[m_iCurrentSlotHIndex].iCount-1; + iVSlotIndexA[1]=0; + iVSlotIndexA[2]=1; + UpdateVerticalSlots(); + UpdateHighlight(); + + app.DebugPrintf(app.USER_SR,"Reloading tabs\n"); + showTabHighlight(0,false); + showTabHighlight(m_iGroupIndex,true); +} + +void UIScene_CraftingMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + shared_ptr item = nullptr; + int slotId = -1; + float alpha = 1.0f; + bool decorations = true; + bool inventoryItem = false; + swscanf((wchar_t*)region->name,L"slot_%d",&slotId); + if (slotId == -1) + { + app.DebugPrintf("This is not the control we are looking for\n"); + } + else if(slotId >= CRAFTING_INVENTORY_SLOT_START && slotId < CRAFTING_INVENTORY_SLOT_END) + { + int iIndex = slotId - CRAFTING_INVENTORY_SLOT_START; + iIndex += m_iMenuInventoryStart; + Slot *slot = m_menu->getSlot(iIndex); + item = slot->getItem(); + inventoryItem = true; + } + else if(slotId >= CRAFTING_HOTBAR_SLOT_START && slotId < CRAFTING_HOTBAR_SLOT_END) + { + int iIndex = slotId - CRAFTING_HOTBAR_SLOT_START; + iIndex += m_iMenuHotBarStart; + Slot *slot = m_menu->getSlot(iIndex); + item = slot->getItem(); + inventoryItem = true; + } + else if(slotId >= CRAFTING_V_SLOT_START && slotId < CRAFTING_V_SLOT_END ) + { + decorations = false; + int iIndex = slotId - CRAFTING_V_SLOT_START; + if(m_vSlotsInfo[iIndex].show) + { + item = m_vSlotsInfo[iIndex].item; + alpha = ((float)m_vSlotsInfo[iIndex].alpha)/31.0f; + } + } + else if(slotId >= CRAFTING_H_SLOT_START && slotId < (CRAFTING_H_SLOT_START + m_iCraftablesMaxHSlotC) ) + { + decorations = false; + int iIndex = slotId - CRAFTING_H_SLOT_START; + if(m_hSlotsInfo[iIndex].show) + { + item = m_hSlotsInfo[iIndex].item; + alpha = ((float)m_hSlotsInfo[iIndex].alpha)/31.0f; + } + } + else if(slotId >= CRAFTING_INGREDIENTS_LAYOUT_START && slotId < (CRAFTING_INGREDIENTS_LAYOUT_START + m_iIngredientsMaxSlotC) ) + { + int iIndex = slotId - CRAFTING_INGREDIENTS_LAYOUT_START; + if(m_ingredientsSlotsInfo[iIndex].show) + { + item = m_ingredientsSlotsInfo[iIndex].item; + alpha = ((float)m_ingredientsSlotsInfo[iIndex].alpha)/31.0f; + } + } + else if(slotId >= CRAFTING_INGREDIENTS_DESCRIPTION_START && slotId < (CRAFTING_INGREDIENTS_DESCRIPTION_START + 4) ) + { + int iIndex = slotId - CRAFTING_INGREDIENTS_DESCRIPTION_START; + if(m_ingredientsInfo[iIndex].show) + { + item = m_ingredientsInfo[iIndex].item; + alpha = ((float)m_ingredientsInfo[iIndex].alpha)/31.0f; + } + } + else if(slotId == CRAFTING_OUTPUT_SLOT_START ) + { + if(m_craftingOutputSlotInfo.show) + { + item = m_craftingOutputSlotInfo.item; + alpha = ((float)m_craftingOutputSlotInfo.alpha)/31.0f; + } + } + + if(item != NULL) + { + if(!inventoryItem) + { + if( item->id == Item::clock_Id || item->id == Item::compass_Id ) + { + // 4J Stu - For clocks and compasses we set the aux value to a special one that signals we should use a default texture + // rather than the dynamic one for the player + item->setAuxValue(0xFF); + } + else if( (item->getAuxValue() & 0xFF) == 0xFF) + { + // 4J Stu - If the aux value is set to match any + item->setAuxValue(0); + } + } + customDrawSlotControl(region,m_iPad,item,alpha,item->isFoil(),decorations); + } +} + +int UIScene_CraftingMenu::getPad() +{ + return m_iPad; +} + +bool UIScene_CraftingMenu::allowRepeat(int key) +{ + switch(key) + { + // X is used to open this menu, so don't let it repeat + case ACTION_MENU_X: + return false; + } + return true; +} + +void UIScene_CraftingMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_InventoryMenu handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_OTHER_STICK_UP: + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(key,repeat,pressed,released); + break; + default: + if(pressed) + { + handled = handleKeyDown(m_iPad, key, repeat); + } + break; + }; +} + +void UIScene_CraftingMenu::hideAllHSlots() +{ + for(unsigned int iIndex = 0; iIndex < m_iMaxHSlotC; ++iIndex) + { + m_hSlotsInfo[iIndex].item = nullptr; + m_hSlotsInfo[iIndex].alpha = 31; + m_hSlotsInfo[iIndex].show = false; + } +} + +void UIScene_CraftingMenu::hideAllVSlots() +{ + for(unsigned int iIndex = 0; iIndex < m_iMaxDisplayedVSlotC; ++iIndex) + { + m_vSlotsInfo[iIndex].item = nullptr; + m_vSlotsInfo[iIndex].alpha = 31; + m_vSlotsInfo[iIndex].show = false; + } +} + +void UIScene_CraftingMenu::hideAllIngredientsSlots() +{ + for(int i=0;i item, unsigned int uiAlpha) +{ + m_hSlotsInfo[iIndex].item = item; + m_hSlotsInfo[iIndex].alpha = uiAlpha; + m_hSlotsInfo[iIndex].show = true; +} + +void UIScene_CraftingMenu::setCraftVSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha) +{ + m_vSlotsInfo[iIndex].item = item; + m_vSlotsInfo[iIndex].alpha = uiAlpha; + m_vSlotsInfo[iIndex].show = true; +} + +void UIScene_CraftingMenu::setCraftingOutputSlotItem(int iPad, shared_ptr item) +{ + m_craftingOutputSlotInfo.item = item; + m_craftingOutputSlotInfo.alpha = 31; + m_craftingOutputSlotInfo.show = item != NULL; +} + +void UIScene_CraftingMenu::setCraftingOutputSlotRedBox(bool show) +{ + m_slotListCraftingOutput.showSlotRedBox(0,show); +} + +void UIScene_CraftingMenu::setIngredientSlotItem(int iPad, int index, shared_ptr item) +{ + m_ingredientsSlotsInfo[index].item = item; + m_ingredientsSlotsInfo[index].alpha = 31; + m_ingredientsSlotsInfo[index].show = item != NULL; +} + +void UIScene_CraftingMenu::setIngredientSlotRedBox(int index, bool show) +{ + m_slotListIngredientsLayout.showSlotRedBox(index,show); +} + +void UIScene_CraftingMenu::setIngredientDescriptionItem(int iPad, int index, shared_ptr item) +{ + m_ingredientsInfo[index].item = item; + m_ingredientsInfo[index].alpha = 31; + m_ingredientsInfo[index].show = item != NULL; + + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = index; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = m_ingredientsInfo[index].show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcShowIngredientSlot , 2 , value ); +} + +void UIScene_CraftingMenu::setIngredientDescriptionRedBox(int index, bool show) +{ + m_slotListIngredients[index].showSlotRedBox(0,show); +} + +void UIScene_CraftingMenu::setIngredientDescriptionText(int index, LPCWSTR text) +{ + m_labelIngredientsDesc[index].setLabel(text); +} + + +void UIScene_CraftingMenu::setShowCraftHSlot(int iIndex, bool show) +{ + m_hSlotsInfo[iIndex].show = show; +} + +void UIScene_CraftingMenu::showTabHighlight(int iIndex, bool show) +{ + if(show) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = iIndex; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcSetActiveTab , 1 , value ); + } +} + +void UIScene_CraftingMenu::setGroupText(LPCWSTR text) +{ + m_labelGroupName.setLabel(text); +} + +void UIScene_CraftingMenu::setDescriptionText(LPCWSTR text) +{ + m_labelDescription.setLabel(text); +} + +void UIScene_CraftingMenu::setItemText(LPCWSTR text) +{ + m_labelItemName.setLabel(text); +} + +void UIScene_CraftingMenu::UpdateMultiPanel() +{ + // Call Iggy function to show the current panel + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_iDisplayDescription; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcShowPanelDisplay , 1 , value ); +} + +void UIScene_CraftingMenu::scrollDescriptionUp() +{ + // handled differently +} + +void UIScene_CraftingMenu::scrollDescriptionDown() +{ + // handled differently +} + +void UIScene_CraftingMenu::updateHighlightAndScrollPositions() +{ + { + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_iCurrentSlotHIndex; + + int selectorType = 0; + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount == 2) + { + selectorType = 1; + } + else if( CanBeMadeA[m_iCurrentSlotHIndex].iCount > 2) + { + selectorType = 2; + } + + value[1].type = IGGY_DATATYPE_number; + value[1].number = selectorType; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcMoveSelector , 2 , value ); + } + + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_iCurrentSlotVIndex; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcSelectVerticalItem , 1 , value ); + } +} + +void UIScene_CraftingMenu::updateVSlotPositions(int iSlots, int i) +{ + // Not needed +} diff --git a/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h new file mode 100644 index 0000000..44a39d6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CraftingMenu.h @@ -0,0 +1,205 @@ +#pragma once + +#include "UIScene.h" +#include "UIControl_SlotList.h" +#include "UIControl_Label.h" +#include "IUIScene_CraftingMenu.h" + +#define CRAFTING_INVENTORY_SLOT_START 0 +#define CRAFTING_INVENTORY_SLOT_END (CRAFTING_INVENTORY_SLOT_START + 27) + +#define CRAFTING_HOTBAR_SLOT_START CRAFTING_INVENTORY_SLOT_END +#define CRAFTING_HOTBAR_SLOT_END (CRAFTING_HOTBAR_SLOT_START + 9) + +// Ingredients etc should go here +#define CRAFTING_INGREDIENTS_DESCRIPTION_START CRAFTING_HOTBAR_SLOT_END +#define CRAFTING_INGREDEINTS_DESCRIPTION_END (CRAFTING_INGREDIENTS_DESCRIPTION_START + 4) + +#define CRAFTING_OUTPUT_SLOT_START CRAFTING_INGREDEINTS_DESCRIPTION_END +#define CRAFTING_OUTPUT_SLOT_END (CRAFTING_OUTPUT_SLOT_START + 1) + +#define CRAFTING_INGREDIENTS_LAYOUT_START CRAFTING_OUTPUT_SLOT_END +#define CRAFTING_INGREDIENTS_LAYOUT_END (CRAFTING_INGREDIENTS_LAYOUT_START+9) + +#define CRAFTING_V_SLOT_START CRAFTING_INGREDIENTS_LAYOUT_END +#define CRAFTING_V_SLOT_END (CRAFTING_V_SLOT_START+3) + +// H slots should go last in the count as it's dependent on which size of crafting panel we have +#define CRAFTING_H_SLOT_START CRAFTING_V_SLOT_END + +class UIScene_CraftingMenu : public UIScene, public IUIScene_CraftingMenu +{ +private: + typedef struct _SlotInfo + { + shared_ptr item; + unsigned int alpha; + bool show; + + _SlotInfo() + { + item = nullptr; + alpha = 31; + show = true; + } + } SlotInfo; + + SlotInfo m_hSlotsInfo[m_iMaxHSlotC]; + SlotInfo m_vSlotsInfo[m_iMaxDisplayedVSlotC]; + SlotInfo m_ingredientsSlotsInfo[m_iIngredients3x3SlotC]; + SlotInfo m_craftingOutputSlotInfo; + SlotInfo m_ingredientsInfo[4]; + + AbstractContainerMenu *m_menu; + + int m_iMenuInventoryStart; + int m_iMenuHotBarStart; + +public: + UIScene_CraftingMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual void handleDestroy(); + + virtual EUIScene getSceneType(); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + +#ifdef __PSVITA__ + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); + virtual UIControl* GetMainPanel(); + virtual void handleTouchBoxRebuild(); + virtual void handleTimerComplete(int id); +#endif + +protected: + UIControl m_controlMainPanel; + UIControl m_control1Selector, m_control2Selector, m_control3Selector; + UIControl_SlotList m_slotListCraftingHSlots; + UIControl_SlotList m_slotListCrafting1VSlots, m_slotListCrafting2VSlots[2], m_slotListCrafting3VSlots[3]; + UIControl_SlotList m_slotListIngredientsLayout, m_slotListCraftingOutput; + UIControl_SlotList m_slotListIngredients[4]; + UIControl_SlotList m_slotListInventory, m_slotListHotBar; + UIControl_Label m_labelIngredientsDesc[4]; + UIControl_HTMLLabel m_labelDescription; + UIControl_Label m_labelGroupName, m_labelItemName, m_labelInventory, m_labelIngredients; + + IggyName m_funcMoveSelector, m_funcSelectVerticalItem, m_funcSetActiveTab; + IggyName m_funcShowPanelDisplay, m_funcShowIngredientSlot; + +#ifdef __PSVITA__ + enum ETouchInput + { + ETouchInput_TouchPanel_0, + ETouchInput_TouchPanel_1, + ETouchInput_TouchPanel_2, + ETouchInput_TouchPanel_3, + ETouchInput_TouchPanel_4, + ETouchInput_TouchPanel_5, + ETouchInput_TouchPanel_6, + ETouchInput_CraftingHSlots, + + ETouchInput_Count, + }; + UIControl_Touch m_TouchInput[ETouchInput_Count]; + S32 m_iCraftingSlotTouchStartY; +#endif + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListCraftingHSlots, "CraftingHSlots") + + UI_MAP_ELEMENT( m_control3Selector, "SlotSelector3" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_control3Selector) + UI_MAP_ELEMENT( m_slotListCrafting3VSlots[0], "Crafting3VSlot1") + UI_MAP_ELEMENT( m_slotListCrafting3VSlots[1], "Crafting3VSlot2") + UI_MAP_ELEMENT( m_slotListCrafting3VSlots[2], "Crafting3VSlot3") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_control2Selector, "SlotSelector2" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_control2Selector) + UI_MAP_ELEMENT( m_slotListCrafting2VSlots[0], "Crafting2VSlot1") + UI_MAP_ELEMENT( m_slotListCrafting2VSlots[1], "Crafting2VSlot2") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_control1Selector, "CraftingSelector" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_control1Selector) + UI_MAP_ELEMENT( m_slotListCrafting1VSlots, "Crafting1VSlot1") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_slotListIngredientsLayout, "IngredientsLayout") + UI_MAP_ELEMENT( m_slotListCraftingOutput, "CraftingOutput") + + UI_MAP_ELEMENT( m_slotListIngredients[0], "Ingredient1") + UI_MAP_ELEMENT( m_slotListIngredients[1], "Ingredient2") + UI_MAP_ELEMENT( m_slotListIngredients[2], "Ingredient3") + UI_MAP_ELEMENT( m_slotListIngredients[3], "Ingredient4") + + UI_MAP_ELEMENT( m_labelIngredientsDesc[0], "Ingredient1Desc") + UI_MAP_ELEMENT( m_labelIngredientsDesc[1], "Ingredient2Desc") + UI_MAP_ELEMENT( m_labelIngredientsDesc[2], "Ingredient3Desc") + UI_MAP_ELEMENT( m_labelIngredientsDesc[3], "Ingredient4Desc") + + UI_MAP_ELEMENT( m_labelIngredients, "IngredientsLabel") + + UI_MAP_ELEMENT( m_labelDescription, "DescriptionText") + + UI_MAP_ELEMENT( m_slotListInventory, "Inventory") + UI_MAP_ELEMENT( m_slotListHotBar, "HotBar") + + UI_MAP_ELEMENT( m_labelGroupName, "GroupName") + UI_MAP_ELEMENT( m_labelItemName, "ItemName") + UI_MAP_ELEMENT( m_labelInventory, "InventoryLabel") + + UI_MAP_NAME( m_funcMoveSelector, L"MoveSelector") + UI_MAP_NAME( m_funcSelectVerticalItem, L"SelectVerticalItem") + UI_MAP_NAME( m_funcSetActiveTab, L"SetActiveTab") + UI_MAP_NAME( m_funcShowPanelDisplay, L"showPanelDisplay") + UI_MAP_NAME( m_funcShowIngredientSlot, L"ShowIngredient") + +#ifdef __PSVITA__ + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_0], "TouchPanel_0" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_1], "TouchPanel_1" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_2], "TouchPanel_2" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_3], "TouchPanel_3" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_4], "TouchPanel_4" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_5], "TouchPanel_5" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_6], "TouchPanel_6" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_CraftingHSlots], "TouchPanel_CraftingHSlots" ) +#endif + + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual bool allowRepeat(int key); + void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + virtual int getPad(); + virtual void hideAllHSlots(); + virtual void hideAllVSlots(); + virtual void hideAllIngredientsSlots(); + virtual void setCraftHSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha); + virtual void setCraftVSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha); + virtual void setCraftingOutputSlotItem(int iPad, shared_ptr item); + virtual void setCraftingOutputSlotRedBox(bool show); + virtual void setIngredientSlotItem(int iPad, int index, shared_ptr item); + virtual void setIngredientSlotRedBox(int index, bool show); + virtual void setIngredientDescriptionItem(int iPad, int index, shared_ptr item); + virtual void setIngredientDescriptionRedBox(int index, bool show); + virtual void setIngredientDescriptionText(int index, LPCWSTR text); + virtual void setShowCraftHSlot(int iIndex, bool show); + virtual void showTabHighlight(int iIndex, bool show); + virtual void setGroupText(LPCWSTR text); + virtual void setDescriptionText(LPCWSTR text); + virtual void setItemText(LPCWSTR text); + virtual void scrollDescriptionUp(); + virtual void scrollDescriptionDown(); + virtual void updateHighlightAndScrollPositions(); + virtual void updateVSlotPositions(int iSlots, int i); + + virtual void UpdateMultiPanel(); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp new file mode 100644 index 0000000..fa41909 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -0,0 +1,1457 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_CreateWorldMenu.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\BiomeSource.h" +#include "..\..\..\Minecraft.World\IntCache.h" +#include "..\..\..\Minecraft.World\LevelType.h" +#include "..\..\DLCTexturePack.h" + +#ifdef __PSVITA__ +#include "PSVita\Network\SQRNetworkManager_AdHoc_Vita.h" +#endif + +#ifdef _WINDOWS64 + +#include +#include "Xbox\Resource.h" +#endif + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 + +int UIScene_CreateWorldMenu::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + +UIScene_CreateWorldMenu::UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer) : IUIScene_StartGame(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_worldName = app.GetString(IDS_DEFAULT_WORLD_NAME); + m_seed = L""; + + m_iPad=iPad; + + m_labelWorldName.init(app.GetString(IDS_WORLD_NAME)); + m_labelSeed.init(app.GetString(IDS_CREATE_NEW_WORLD_SEED)); + m_labelRandomSeed.init(app.GetString(IDS_CREATE_NEW_WORLD_RANDOM_SEED)); + + m_editWorldName.init(m_worldName, eControl_EditWorldName); + m_editSeed.init(L"", eControl_EditSeed); + + m_buttonGamemode.init(app.GetString(IDS_GAMEMODE_SURVIVAL),eControl_GameModeToggle); + m_buttonMoreOptions.init(app.GetString(IDS_MORE_OPTIONS),eControl_MoreOptions); + m_buttonCreateWorld.init(app.GetString(IDS_CREATE_NEW_WORLD),eControl_NewWorld); + + m_texturePackList.init(app.GetString(IDS_DLC_MENU_TEXTUREPACKS), eControl_TexturePackList); + + m_labelTexturePackName.init(L""); + m_labelTexturePackDescription.init(L""); + + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)])); + m_sliderDifficulty.init(TempString,eControl_Difficulty,0,3,app.GetGameSettings(m_iPad,eGameSetting_Difficulty)); + + m_MoreOptionsParams.bGenerateOptions=TRUE; + m_MoreOptionsParams.bStructures=TRUE; + m_MoreOptionsParams.bFlatWorld=FALSE; + m_MoreOptionsParams.bBonusChest=FALSE; + m_MoreOptionsParams.bPVP = TRUE; + m_MoreOptionsParams.bTrust = TRUE; + m_MoreOptionsParams.bFireSpreads = TRUE; + m_MoreOptionsParams.bHostPrivileges = FALSE; + m_MoreOptionsParams.bTNT = TRUE; + m_MoreOptionsParams.iPad = iPad; + + m_bGameModeSurvival=true; + m_pDLCPack = NULL; + m_bRebuildTouchBoxes = false; + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineSettingChangedBySystem=false; + + // 4J-PB - Removing this so that we can attempt to create an online game on PS3 when we are a restricted child account + // It'll fail when we choose create, but this matches the behaviour of load game, and lets the player know why they can't play online, + // instead of just greying out the online setting in the More Options + // #ifdef __PS3__ + // if(ProfileManager.IsSignedInLive( m_iPad )) + // { + // ProfileManager.GetChatAndContentRestrictions(m_iPad,true,&bChatRestricted,&bContentRestricted,NULL); + // } + // #endif + + // Set the text for friends of friends, and default to on + if( m_bMultiplayerAllowed ) + { + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + if(bGameSetting_Online) + { + // The profile settings say Online, but either the player is offline, or they are not allowed to play online + m_MoreOptionsParams.bOnlineSettingChangedBySystem=true; + } + } + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + // Set up online game checkbox + bool bOnlineGame = m_MoreOptionsParams.bOnlineGame; + m_checkboxOnline.SetEnable(true); + + // 4J-PB - to stop an offline game being able to select the online flag + if(ProfileManager.IsSignedInLive(m_iPad) == false) + { + m_checkboxOnline.SetEnable(false); + } + + if(m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + m_checkboxOnline.SetEnable(false); + bOnlineGame = false; + } + + m_checkboxOnline.init(app.GetString(IDS_ONLINE_GAME), eControl_OnlineGame, bOnlineGame); + } +#endif + + addTimer( GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME ); +#if TO_BE_IMPLEMENTED + XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); +#endif + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_CreateWorldMenu, 0); + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true) + { + // not doing a mount, so enable input + m_bIgnoreInput=true; + } + else + { + m_bIgnoreInput = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + wchar_t imageName[64]; + swprintf(imageName,64,L"tpack%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + m_texturePackList.addPack(i,imageName); + } + } + +#if TO_BE_IMPLEMENTED + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } +#endif + + UpdateTexturePackDescription(m_currentTexturePackIndex); + + + m_texturePackList.selectSlot(m_currentTexturePackIndex); + } +} + +UIScene_CreateWorldMenu::~UIScene_CreateWorldMenu() +{ +} + +void UIScene_CreateWorldMenu::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_CreateWorldMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +} + +wstring UIScene_CreateWorldMenu::getMoviePath() +{ + return L"CreateWorldMenu"; +} + +UIControl* UIScene_CreateWorldMenu::GetMainPanel() +{ + return &m_controlMainPanel; +} + +void UIScene_CreateWorldMenu::handleDestroy() +{ +#ifdef __PSVITA__ + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); +#endif + + // shut down the keyboard if it is displayed +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + InputManager.DestroyKeyboard(); +#endif +} + +void UIScene_CreateWorldMenu::tick() +{ + UIScene::tick(); + + if(m_iSetTexturePackDescription >= 0 ) + { + UpdateTexturePackDescription( m_iSetTexturePackDescription ); + m_iSetTexturePackDescription = -1; + } + if(m_bShowTexturePackDescription) + { + slideLeft(); + m_texturePackDescDisplayed = true; + + m_bShowTexturePackDescription = false; + } + +#ifdef __ORBIS__ + // check the status of the PSPlus common dialog + switch (sceNpCommerceDialogUpdateStatus()) + { + case SCE_COMMON_DIALOG_STATUS_FINISHED: + { + SceNpCommerceDialogResult Result; + sceNpCommerceDialogGetResult(&Result); + sceNpCommerceDialogTerminate(); + + if(Result.authorized) + { + ProfileManager.PsPlusUpdate(ProfileManager.GetPrimaryPad(), &Result); + // they just became a PSPlus member + checkStateAndStartGame(); + } + else + { + // continue offline? + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_DECLINE; + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_PLAY_OFFLINE,IDS_NO_PLAYSTATIONPLUS, uiIDA, 1, ProfileManager.GetPrimaryPad(),&UIScene_CreateWorldMenu::ContinueOffline,dynamic_cast(this),app.GetStringTable(), 0, 0, false); + } + } + break; + default: + break; + } +#endif +} + +#ifdef __ORBIS__ +int UIScene_CreateWorldMenu::ContinueOffline(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_CreateWorldMenu* pClass = (UIScene_CreateWorldMenu*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultAccept) + { + pClass->m_MoreOptionsParams.bOnlineGame=false; + pClass->checkStateAndStartGame(); + } + return 0; +} + +#endif + +void UIScene_CreateWorldMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + + // 4J-JEV: Inform user why their game must be offline. +#if defined _XBOX_ONE + if ( pressed && controlHasFocus(m_checkboxOnline.getId()) && !m_checkboxOnline.IsEnabled() ) + { + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } +#endif + + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_OTHER_STICK_UP: + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(key, repeat, pressed, released); + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + bool bOnlineGame = m_checkboxOnline.IsChecked(); + if (m_MoreOptionsParams.bOnlineGame != bOnlineGame) + { + m_MoreOptionsParams.bOnlineGame = bOnlineGame; + + if (!m_MoreOptionsParams.bOnlineGame) + { + m_MoreOptionsParams.bInviteOnly = false; + m_MoreOptionsParams.bAllowFriendsOfFriends = false; + } + } + } +#endif + + handled = true; + break; + } +} + +void UIScene_CreateWorldMenu::handlePress(F64 controlId, F64 childId) +{ + if(m_bIgnoreInput) return; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + switch((int)controlId) + { + case eControl_EditWorldName: + { + m_bIgnoreInput=true; + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD),m_editWorldName.getLabel(),(DWORD)0,25,&UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback,this,C_4JInput::EKeyboardMode_Default); + } + break; + case eControl_EditSeed: + { + m_bIgnoreInput=true; +#ifdef __PS3__ + int language = XGetLanguage(); + switch(language) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_TCHINESE: + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_CreateWorldMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); + break; + default: + // 4J Stu - Use a different keyboard for non-asian languages so we don't have prediction on + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_CreateWorldMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Alphabet_Extended); + break; + } +#else + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_CreateWorldMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); +#endif + } + break; + case eControl_GameModeToggle: + if(m_bGameModeSurvival) + { + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + else + { + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + } + break; + case eControl_MoreOptions: + ui.NavigateToScene(m_iPad, eUIScene_LaunchMoreOptionsMenu, &m_MoreOptionsParams); + break; + case eControl_TexturePackList: + { + UpdateCurrentTexturePack((int)childId); + } + break; + case eControl_NewWorld: + { +#ifdef _DURANGO + if(m_MoreOptionsParams.bOnlineGame) + { + m_bIgnoreInput = true; + ProfileManager.CheckMultiplayerPrivileges(m_iPad, true, &checkPrivilegeCallback, this); + } + else +#endif + { + StartSharedLaunchFlow(); + } + break; + } + } +} + +#ifdef _DURANGO +void UIScene_CreateWorldMenu::checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad) +{ + UIScene_CreateWorldMenu* pClass = (UIScene_CreateWorldMenu*)lpParam; + + if(hasPrivilege) + { + pClass->StartSharedLaunchFlow(); + } + else + { + pClass->m_bIgnoreInput = false; + } +} +#endif + +void UIScene_CreateWorldMenu::StartSharedLaunchFlow() +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + // Check if we need to upsell the texture pack + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { +#if TO_BE_IMPLEMENTED + // They've selected a texture pack they don't have yet + // upsell + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(m_MoreOptionsParams.dwTexturePack,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + //uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&TexturePackDialogReturned,this,app.GetStringTable(),NULL,0,false); + return; + } + } + m_bIgnoreInput = true; + + // if the profile data has been changed, then force a profile write (we save the online/invite/friends of friends settings) + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + // check the checkboxes + + // Only save the online setting if the user changed it - we may change it because we're offline, but don't want that saved + if(!m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + app.SetGameSettings(m_iPad,eGameSetting_Online,m_MoreOptionsParams.bOnlineGame?1:0); + } + app.SetGameSettings(m_iPad,eGameSetting_InviteOnly,m_MoreOptionsParams.bInviteOnly?1:0); + app.SetGameSettings(m_iPad,eGameSetting_FriendsOfFriends,m_MoreOptionsParams.bAllowFriendsOfFriends?1:0); + + app.CheckGameSettingsChanged(true,m_iPad); + + // Check that we have the rights to use a texture pack we have selected. + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)pTexturePack; + m_pDLCPack=pDLCTexPack->getDLCInfoParentPack(); + + // do we have a license? + if(m_pDLCPack && !m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // no + + // We need to allow people to use a trial texture pack if they are offline - we only need them online if they want to buy it. + + /* + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + if(!ProfileManager.IsSignedInLive(m_iPad)) + { + // need to be signed in to live + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + m_bIgnoreInput = false; + return; + } + else */ + { + // upsell +#ifdef _XBOX + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_pDLCPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=pTexturePack->getDLCPack()->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the texture pack + TelemetryManager->RecordUpsellPresented(m_iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + +#if defined(_DURANGO) || defined(_WINDOWS64) + // trial pack warning + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_USING_TRIAL_TEXUREPACK_WARNING, uiIDA, 1, m_iPad,&TrialTexturePackWarningReturned,this,app.GetStringTable(),NULL,0,false); +#elif defined __PS3__ || defined __ORBIS__ || defined(__PSVITA__) + // trial pack warning + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_USING_TRIAL_TEXUREPACK_WARNING, uiIDA, 2, m_iPad,&TrialTexturePackWarningReturned,this,app.GetStringTable(),NULL,0,false); +#endif + +#if defined _XBOX_ONE || defined __ORBIS__ + StorageManager.SetSaveDisabled(true); +#endif + return; + } + } + } +#if defined _XBOX_ONE || defined __ORBIS__ + app.SetGameHostOption(eGameHostOption_DisableSaving, m_MoreOptionsParams.bDisableSaving?1:0); + StorageManager.SetSaveDisabled(m_MoreOptionsParams.bDisableSaving); +#endif + checkStateAndStartGame(); +} + +void UIScene_CreateWorldMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_Difficulty: + m_sliderDifficulty.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value])); + m_sliderDifficulty.setLabel(TempString); + break; + } +} + +void UIScene_CreateWorldMenu::handleTimerComplete(int id) +{ +#ifdef __PSVITA__ + // we cannot rebuild touch boxes in an iggy callback because it requires further iggy calls + if(m_bRebuildTouchBoxes) + { + GetMainPanel()->UpdateControl(); + ui.TouchBoxRebuild(this); + m_bRebuildTouchBoxes = false; + } +#endif + + switch(id) + { + case GAME_CREATE_ONLINE_TIMER_ID: + { + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + m_checkboxOnline.SetEnable(bMultiplayerAllowed); + m_checkboxOnline.setChecked(m_MoreOptionsParams.bOnlineGame); + } +#endif + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + // 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + // also check for any new texture packs info being available + // for each item in the mem list, check it's in the data list + + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + // for each iConfig, check if the data is available, and add it to the List, then remove it from the viConfig + for(int i=0;i 0 && pbData) + { + DWORD dwImageBytes=0; + PBYTE pbImageData=NULL; + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes ); + ListInfo.fEnabled = TRUE; + ListInfo.iData = m_iConfigA[i]; + HRESULT hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + app.DebugPrintf("Adding texturepack %d from TPD\n",m_iConfigA[i]); + + m_pTexturePacksList->AddData(ListInfo); + + m_iConfigA[i]=-1; + } + } + } + } + break; +#endif + }; +} + +void UIScene_CreateWorldMenu::handleGainFocus(bool navBack) +{ + if(navBack) + { +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + m_checkboxOnline.setChecked(m_MoreOptionsParams.bOnlineGame); + } + m_editSeed.setLabel(m_MoreOptionsParams.seed); +#endif + } +} + +int UIScene_CreateWorldMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes) +{ + UIScene_CreateWorldMenu *pClass=(UIScene_CreateWorldMenu *)lpParam; + pClass->m_bIgnoreInput=false; + // 4J HEG - No reason to set value if keyboard was cancelled + if (bRes) + { + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); + InputManager.GetText(pchText); + + if(pchText[0]!=0) + { + pClass->m_editWorldName.setLabel((wchar_t *)pchText); + pClass->m_worldName = (wchar_t *)pchText; + } + + pClass->m_buttonCreateWorld.setEnable( !pClass->m_worldName.empty() ); + } + return 0; +} + +int UIScene_CreateWorldMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,bool bRes) +{ + UIScene_CreateWorldMenu *pClass=(UIScene_CreateWorldMenu *)lpParam; + pClass->m_bIgnoreInput=false; + // 4J HEG - No reason to set value if keyboard was cancelled + if (bRes) + { +#ifdef __PSVITA__ + //CD - Changed to 2048 [SCE_IME_MAX_TEXT_LENGTH] + uint16_t pchText[2048]; + ZeroMemory(pchText, 2048 * sizeof(uint16_t) ); +#else + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); +#endif + InputManager.GetText(pchText); + pClass->m_editSeed.setLabel((wchar_t *)pchText); + pClass->m_MoreOptionsParams.seed = (wchar_t *)pchText; + } + return 0; +} + +void UIScene_CreateWorldMenu::checkStateAndStartGame() +{ + int primaryPad = ProfileManager.GetPrimaryPad(); + bool isSignedInLive = true; + bool isOnlineGame = m_MoreOptionsParams.bOnlineGame; + int iPadNotSignedInLive = -1; + bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable(); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (ProfileManager.IsSignedIn(i) && (i == primaryPad || isLocalMultiplayerAvailable)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + } + } + + // If this is an online game but not all players are signed in to Live, stop! + if (isOnlineGame && !isSignedInLive) + { +#ifdef __ORBIS__ + assert(iPadNotSignedInLive != -1); + + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + m_bIgnoreInput = true; + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_CANCEL; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPadNotSignedInLive, &UIScene_CreateWorldMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + } + return; +/* 4J-PB - Add this after release +#elif defined __PSVITA__ + m_bIgnoreInput=false; + // Determine why they're not "signed in live" + if (ProfileManager.IsSignedInPSN(ProfileManager.GetPrimaryPad())) + { + // Signed in to PSN but not connected (no internet access) + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_PRO_CURRENTLY_NOT_ONLINE_TITLE, IDS_PRO_PSNOFFLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), NULL,NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), NULL,NULL, app.GetStringTable()); + return; + }*/ +#else + m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + return; +#endif + } + +#ifdef __ORBIS__ + + bool bPlayStationPlus = true; + int iPadWithNoPlaystationPlus=0; + if(isOnlineGame && isSignedInLive) + { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(ProfileManager.IsSignedIn(i) && (i == primaryPad || isLocalMultiplayerAvailable)) + { + if(ProfileManager.HasPlayStationPlus(i)==false) + { + bPlayStationPlus=false; + iPadWithNoPlaystationPlus=i; + break; + } + } + } + + if(bPlayStationPlus==false) + { + m_bIgnoreInput=false; + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); + +// UINT uiIDA[2]; +// uiIDA[0]=IDS_PLAY_OFFLINE; +// uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; +// ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_CreateWorldMenu::PSPlusReturned,this, app.GetStringTable(),NULL,0,false); + return; + } + } +#endif + + if(m_bGameModeSurvival != true || m_MoreOptionsParams.bHostPrivileges == TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + if(m_bGameModeSurvival != true) + { + ui.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_CREATIVE, uiIDA, 2, m_iPad,&UIScene_CreateWorldMenu::ConfirmCreateReturned,this,app.GetStringTable(),NULL,0,false); + } + else + { + ui.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_HOST_PRIVILEGES, uiIDA, 2, m_iPad,&UIScene_CreateWorldMenu::ConfirmCreateReturned,this,app.GetStringTable(),NULL,0,false); + } + } + else + { + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + //bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && m_MoreOptionsParams.bOnlineGame; + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + bool bContentRestricted = false; + + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); +#if defined(__PS3__) || defined(__PSVITA__) + if(isOnlineGame && isSignedInLive) + { + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,NULL,&bContentRestricted,NULL); + } +#endif + + noUGC = !pccAllowed && !pccFriendsAllowed; + + if(isOnlineGame && isSignedInLive && app.IsLocalMultiplayerAvailable()) + { + // 4J-PB not sure why we aren't checking the content restriction for the main player here when multiple controllers are connected - adding now + if(noUGC ) + { + m_bIgnoreInput=false; + ui.RequestUGCMessageBox(); + } + else if(bContentRestricted ) + { + m_bIgnoreInput=false; + ui.RequestContentRestrictedMessageBox(); + } +#ifdef __ORBIS__ + else if(bPlayStationPlus==false) + { + m_bIgnoreInput=false; + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); +// UINT uiIDA[2]; +// uiIDA[0]=IDS_PLAY_OFFLINE; +// uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; +// ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_CreateWorldMenu::PSPlusReturned,this, app.GetStringTable(),NULL,0,false); + } + +#endif + else + { + //ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameCreate::StartGame_SignInReturned, this,ProfileManager.GetPrimaryPad()); + SignInInfo info; + info.Func = &UIScene_CreateWorldMenu::StartGame_SignInReturned; + info.lpParam = this; + info.requireOnline = m_MoreOptionsParams.bOnlineGame; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); + } + } + else + { + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isOnlineGame && isSignedInLive && noUGC ) + { + m_bIgnoreInput=false; + ui.RequestUGCMessageBox(); + } + else if(isOnlineGame && isSignedInLive && bContentRestricted ) + { + m_bIgnoreInput=false; + ui.RequestContentRestrictedMessageBox(); + } +#ifdef __ORBIS__ + else if(isOnlineGame && isSignedInLive && (bPlayStationPlus==false)) + { + m_bIgnoreInput=false; + setVisible( true ); + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); + +// UINT uiIDA[2]; +// uiIDA[0]=IDS_PLAY_OFFLINE; +// uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; +// ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_CreateWorldMenu::PSPlusReturned,this, app.GetStringTable(),NULL,0,false); + } + +#endif + else + { +#if defined(__ORBIS__) || defined(__PSVITA__) + if(isOnlineGame) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() ); + } + } +#endif + CreateGame(this, 0); + } + } + } + +} + +// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not +void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD dwLocalUsersMask) +{ +#if TO_BE_IMPLEMENTED + // stop the timer running that causes a check for new texture packs in TMS but not installed, since this will run all through the create game, and will crash if it tries to create an hbrush + XuiKillTimer(pClass->m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID); +#endif + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode()) + { + if(SQRNetworkManager_AdHoc_Vita::GetAdhocStatus())// && pClass->m_MoreOptionsParams.bOnlineGame) + isClientSide = true; + } +#endif // __PSVITA__ + + bool isPrivate = pClass->m_MoreOptionsParams.bInviteOnly?true:false; + + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + // create the world and launch + wstring wWorldName = pClass->m_worldName; + + StorageManager.ResetSaveData(); + // Make our next save default to the name of the level + StorageManager.SetSaveTitle((wchar_t *)wWorldName.c_str()); + + wstring wSeed; + if(!pClass->m_MoreOptionsParams.seed.empty() ) + { + wSeed=pClass->m_MoreOptionsParams.seed; + } + else + { + // random + wSeed=L""; + } + + // start the game + bool isFlat = (pClass->m_MoreOptionsParams.bFlatWorld==TRUE); + __int64 seedValue = 0; + + NetworkGameInitData *param = new NetworkGameInitData(); + + if (wSeed.length() != 0) + { + __int64 value = 0; + unsigned int len = (unsigned int)wSeed.length(); + + //Check if the input string contains a numerical value + bool isNumber = true; + for( unsigned int i = 0 ; i < len ; ++i ) + { + if( wSeed.at(i) < L'0' || wSeed.at(i) > L'9' ) + { + if( !(i==0 && wSeed.at(i) == L'-' ) ) + { + isNumber = false; + break; + } + } + } + + //If the input string is a numerical value, convert it to a number + if( isNumber ) + value = _fromString<__int64>(wSeed); + + //If the value is not 0 use it, otherwise use the algorithm from the java String.hashCode() function to hash it + if( value != 0 ) + seedValue = value; + else + { + int hashValue = 0; + for( unsigned int i = 0 ; i < len ; ++i ) + hashValue = 31 * hashValue + wSeed.at(i); + seedValue = hashValue; + } + } + else + { + param->findSeed = true; // 4J - java code sets the seed to was (new Random())->nextLong() here - we used to at this point find a suitable seed, but now just set a flag so this is performed in Minecraft::Server::initServer. + } + + + param->seed = seedValue; + param->saveData = NULL; + param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack); + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,pClass->m_MoreOptionsParams.bAllowFriendsOfFriends); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(pClass->m_iPad,eGameSetting_GamertagsVisible)?1:0); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(pClass->m_iPad,eGameSetting_BedrockFog)?1:0); + + app.SetGameHostOption(eGameHostOption_GameType,pClass->m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId() ); + app.SetGameHostOption(eGameHostOption_LevelType,pClass->m_MoreOptionsParams.bFlatWorld ); + app.SetGameHostOption(eGameHostOption_Structures,pClass->m_MoreOptionsParams.bStructures ); + app.SetGameHostOption(eGameHostOption_BonusChest,pClass->m_MoreOptionsParams.bBonusChest ); + + app.SetGameHostOption(eGameHostOption_PvP,pClass->m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,pClass->m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,pClass->m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,pClass->m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,pClass->m_MoreOptionsParams.bHostPrivileges ); + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + +#ifdef _LARGE_WORLDS + switch(pClass->m_MoreOptionsParams.worldSize) + { + case 0: + // Classic + param->xzSize = 1 * 54; + param->hellScale = 3; + break; + case 1: + // Small + param->xzSize = 1 * 64; + param->hellScale = 3; + break; + case 2: + // Medium + param->xzSize = 3 * 64; + param->hellScale = 6; + break; + case 3: + //param->xzSize = 5 * 64; + //param->hellScale = 8; + + // Large + param->xzSize = LEVEL_MAX_WIDTH; + param->hellScale = HELL_LEVEL_MAX_SCALE; + break; + }; +#else + param->xzSize = LEVEL_MAX_WIDTH; + param->hellScale = HELL_LEVEL_MAX_SCALE; +#endif + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave time + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(pClass->m_iPad,eUIScene_FullscreenProgress, loadingParams); +} + + +int UIScene_CreateWorldMenu::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_CreateWorldMenu* pClass = (UIScene_CreateWorldMenu*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(pClass->m_iPad)) + { + bool isOnlineGame = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + // bool isOnlineGame = pClass->m_MoreOptionsParams.bOnlineGame; + int primaryPad = ProfileManager.GetPrimaryPad(); + bool noPrivileges = false; + DWORD dwLocalUsersMask = 0; + bool isSignedInLive = ProfileManager.IsSignedInLive(primaryPad); + int iPadNotSignedInLive = -1; + bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable(); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if (ProfileManager.IsSignedIn(i) && ((i == primaryPad) || isLocalMultiplayerAvailable)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + if( !ProfileManager.AllowedToPlayMultiplayer(i) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(i); + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + } + } + + // If this is an online game but not all players are signed in to Live, stop! + if (isOnlineGame && !isSignedInLive) + { +#ifdef __ORBIS__ + assert(iPadNotSignedInLive != -1); + + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + pClass->m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + pClass->m_bIgnoreInput=true; + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_CANCEL; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPadNotSignedInLive, &UIScene_CreateWorldMenu::MustSignInReturnedPSN, pClass, app.GetStringTable(), NULL, 0, false); + } + return 0; +#else + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + return 0; +#endif + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isOnlineGame && (noPrivileges || noUGC) ) + { + if( noUGC ) + { + pClass->m_bIgnoreInput = false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + } + else + { + pClass->m_bIgnoreInput = false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + } + } + else + { + // This is NOT called from a storage manager thread, and is in fact called from the main thread in the Profile library tick. Therefore we use the main threads IntCache. + CreateGame(pClass, dwLocalUsersMask); + } + } + } + else + { + pClass->m_bIgnoreInput = false; + } + return 0; +} + + +int UIScene_CreateWorldMenu::ConfirmCreateReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_CreateWorldMenu* pClass = (UIScene_CreateWorldMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(isClientSide && app.IsLocalMultiplayerAvailable()) + { + //ProfileManager.RequestSignInUI(false, false, false, true, false,&UIScene_CreateWorldMenu::StartGame_SignInReturned, pClass,ProfileManager.GetPrimaryPad()); + SignInInfo info; + info.Func = &UIScene_CreateWorldMenu::StartGame_SignInReturned; + info.lpParam = pClass; + info.requireOnline = pClass->m_MoreOptionsParams.bOnlineGame; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); + } + else + { + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + pClass->m_bIgnoreInput = false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + } + else + { +#if defined( __ORBIS__) || defined(__PSVITA__) + bool isOnlineGame = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + if(isOnlineGame) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() ); + } + } +#endif + CreateGame(pClass, 0); + } + } + } + else + { + pClass->m_bIgnoreInput = false; + } + return 0; +} + +#ifdef __ORBIS__ +int UIScene_CreateWorldMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_CreateWorldMenu* pClass = (UIScene_CreateWorldMenu *)pParam; + pClass->m_bIgnoreInput = false; + + if(result==C4JStorage::EMessage_ResultAccept) + { + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_CreateWorldMenu::StartGame_SignInReturned, pClass, false, iPad); + } + + return 0; +} + +// int UIScene_CreateWorldMenu::PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +// { +// int32_t iResult; +// UIScene_CreateWorldMenu *pClass = (UIScene_CreateWorldMenu *)pParam; +// +// // continue offline, or upsell PS Plus? +// if(result==C4JStorage::EMessage_ResultDecline) +// { +// // upsell psplus +// int32_t iResult=sceNpCommerceDialogInitialize(); +// +// SceNpCommerceDialogParam param; +// sceNpCommerceDialogParamInitialize(¶m); +// param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; +// param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; +// param.userId = ProfileManager.getUserID(pClass->m_iPad); +// +// iResult=sceNpCommerceDialogOpen(¶m); +// } +// else if(result==C4JStorage::EMessage_ResultAccept) +// { +// // continue offline +// pClass->m_MoreOptionsParams.bOnlineGame=false; +// pClass->checkStateAndStartGame(); +// } +// +// pClass->m_bIgnoreInput=false; +// return 0; +// } +#endif + + +void UIScene_CreateWorldMenu::handleTouchBoxRebuild() +{ + m_bRebuildTouchBoxes = true; +} diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h new file mode 100644 index 0000000..ebb383e --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.h @@ -0,0 +1,115 @@ +#pragma once + +#include "IUIScene_StartGame.h" + +class UIScene_CreateWorldMenu : public IUIScene_StartGame +{ +private: + enum EControls + { + eControl_EditWorldName, + eControl_EditSeed, + eControl_TexturePackList, + eControl_GameModeToggle, + eControl_Difficulty, + eControl_MoreOptions, + eControl_NewWorld, + eControl_OnlineGame, + }; + + static int m_iDifficultyTitleSettingA[4]; + + + wstring m_worldName; + wstring m_seed; + + UIControl m_controlMainPanel; + UIControl_Label m_labelWorldName, m_labelSeed, m_labelRandomSeed; + UIControl_Button m_buttonGamemode, m_buttonMoreOptions, m_buttonCreateWorld; + UIControl_TextInput m_editWorldName, m_editSeed; + UIControl_Slider m_sliderDifficulty; + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + UIControl_CheckBox m_checkboxOnline; +#endif + + UIControl_BitmapIcon m_bitmapIcon, m_bitmapComparison; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(IUIScene_StartGame) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_labelWorldName, "WorldName") + UI_MAP_ELEMENT( m_editWorldName, "EditWorldName") + UI_MAP_ELEMENT( m_labelSeed, "Seed") + UI_MAP_ELEMENT( m_editSeed, "EditSeed") + UI_MAP_ELEMENT( m_labelRandomSeed, "RandomSeed") + UI_MAP_ELEMENT( m_texturePackList, "TexturePackSelector") + UI_MAP_ELEMENT( m_buttonGamemode, "GameModeToggle") + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + UI_MAP_ELEMENT( m_checkboxOnline, "CheckboxOnline") +#endif + UI_MAP_ELEMENT( m_buttonMoreOptions, "MoreOptions") + UI_MAP_ELEMENT( m_buttonCreateWorld, "NewWorld") + UI_MAP_ELEMENT( m_sliderDifficulty, "Difficulty") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bGameModeSurvival; + bool m_bMultiplayerAllowed; + DLCPack * m_pDLCPack; + bool m_bRebuildTouchBoxes; + +public: + UIScene_CreateWorldMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_CreateWorldMenu(); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_CreateWorldMenu;} + + virtual void handleDestroy(); + virtual void tick(); + + virtual UIControl* GetMainPanel(); + + virtual void handleTouchBoxRebuild(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + + virtual void handleTimerComplete(int id); + virtual void handleGainFocus(bool navBack); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +private: + void StartSharedLaunchFlow(); + bool IsLocalMultiplayerAvailable(); + +#ifdef _DURANGO + static void checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad); +#endif + +protected: + static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,const bool bRes); + static int KeyboardCompleteSeedCallback(LPVOID lpParam,const bool bRes); + void handlePress(F64 controlId, F64 childId); + void handleSliderMove(F64 sliderId, F64 currentValue); + + static void CreateGame(UIScene_CreateWorldMenu* pClass, DWORD dwLocalUsersMask); + static int ConfirmCreateReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int StartGame_SignInReturned(void *pParam,bool bContinue, int iPad); + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); + +#ifdef __ORBIS__ + //static int PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ContinueOffline(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + + virtual void checkStateAndStartGame(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreativeMenu.cpp new file mode 100644 index 0000000..569cc8b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CreativeMenu.cpp @@ -0,0 +1,493 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_CreativeMenu.h" + +#include "..\Minecraft.World\JavaMath.h" +#include "..\..\LocalPlayer.h" +#include "..\Tutorial\Tutorial.h" +#include "..\Tutorial\TutorialMode.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +#ifdef __PSVITA__ +#define GAME_CREATIVE_TOUCHUPDATE_TIMER_ID 0 +#define GAME_CREATIVE_TOUCHUPDATE_TIMER_TIME 100 +#endif + +UIScene_CreativeMenu::UIScene_CreativeMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + InventoryScreenInput *initData = (InventoryScreenInput *)_initData; + + shared_ptr creativeContainer = shared_ptr(new SimpleContainer( 0, TabSpec::MAX_SIZE )); + itemPickerMenu = new ItemPickerMenu(creativeContainer, initData->player->inventory); + + Initialize( initData->iPad, itemPickerMenu, false, -1, eSectionInventoryCreativeUsing, eSectionInventoryCreativeMax, initData->bNavigateBack); + + m_labelInventory.setLabel( L"" ); + m_bFirstCall=true; + + //m_slotListContainer.addSlots(0,TabSpec::MAX_SIZE); + //m_slotListHotbar.addSlots(TabSpec::MAX_SIZE,TabSpec::MAX_SIZE + 9); + for(unsigned int i = 0; i < TabSpec::MAX_SIZE; ++i) + { + m_slotListContainer.addSlot(i); + } + + for(unsigned int i = TabSpec::MAX_SIZE; i < TabSpec::MAX_SIZE + 9; ++i) + { + m_slotListHotbar.addSlot(i); + } + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Creative_Inventory_Menu, this); + } + + if(initData) delete initData; + + m_curTab = eCreativeInventoryTab_COUNT; + switchTab(eCreativeInventoryTab_BuildingBlocks); + +#ifdef __PSVITA__ + // initialise vita touch controls with ids + for(unsigned int i = 0; i < ETouchInput_Count; ++i) + { + m_TouchInput[i].init(i); + } +#endif +} + +wstring UIScene_CreativeMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"CreativeMenuSplit"; + } + else + { + return L"CreativeMenu"; + } +} + +#ifdef __PSVITA__ +UIControl* UIScene_CreativeMenu::GetMainPanel() +{ + return &m_controlMainPanel; +} + +void UIScene_CreativeMenu::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) +{ + // perform action on release + if(bReleased) + { + if(iId >= eCreativeInventoryTab_BuildingBlocks && iId <= eCreativeInventoryTab_Misc) + { + switchTab((ECreativeInventoryTabs)iId); + ui.PlayUISFX(eSFX_Focus); + } + } + if(bRepeat && iId == ETouchInput_TouchSlider && specs[m_curTab]->getPageCount() > 1) + { + // calculate relative touch position on slider + float fPosition = ((float)y - (float)m_TouchInput[ETouchInput_TouchSlider].getYPos() - m_controlMainPanel.getYPos()) / (float)m_TouchInput[ETouchInput_TouchSlider].getHeight(); + + // clamp + if(fPosition > 1) + fPosition = 1.0f; + else if(fPosition < 0) + fPosition = 0.0f; + + // calculate page position according to page count + int iCurrentPage = Math::round(fPosition * (specs[m_curTab]->getPageCount() - 1)); + + // set tab page + m_tabPage[m_curTab] = iCurrentPage; + + // update tab + switchTab(m_curTab); + } +} + +void UIScene_CreativeMenu::handleTouchBoxRebuild() +{ + addTimer(GAME_CREATIVE_TOUCHUPDATE_TIMER_ID,GAME_CREATIVE_TOUCHUPDATE_TIMER_TIME); +} + +void UIScene_CreativeMenu::handleTimerComplete(int id) +{ + if(id == GAME_CREATIVE_TOUCHUPDATE_TIMER_ID) + { + // we cannot rebuild touch boxes in an iggy callback because it requires further iggy calls + GetMainPanel()->UpdateControl(); + ui.TouchBoxRebuild(this); + killTimer(GAME_CREATIVE_TOUCHUPDATE_TIMER_ID); + } +} +#endif + +void UIScene_CreativeMenu::handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey) +{ + switch(eSection) + { + case eSectionInventoryCreativeTab_0: + case eSectionInventoryCreativeTab_1: + case eSectionInventoryCreativeTab_2: + case eSectionInventoryCreativeTab_3: + case eSectionInventoryCreativeTab_4: + case eSectionInventoryCreativeTab_5: + case eSectionInventoryCreativeTab_6: + case eSectionInventoryCreativeTab_7: + { + ECreativeInventoryTabs tab = (ECreativeInventoryTabs)((int)eCreativeInventoryTab_BuildingBlocks + (int)eSection - (int)eSectionInventoryCreativeTab_0); + if(tab != m_curTab) + { + switchTab(tab); + ui.PlayUISFX(eSFX_Focus); + } + } + break; + case eSectionInventoryCreativeSlider: + ScrollBar(this->m_pointerPos); + break; + } +} + +void UIScene_CreativeMenu::ScrollBar(UIVec2D pointerPos) +{ + float fPosition = ((float)pointerPos.y - (float)m_TouchInput[ETouchInput_TouchSlider].getYPos()) / (float)m_TouchInput[ETouchInput_TouchSlider].getHeight(); + + // clamp + if(fPosition > 1) + fPosition = 1.0f; + else if(fPosition < 0) + fPosition = 0.0f; + + // calculate page position according to page count + int iCurrentPage = Math::round(fPosition * (specs[m_curTab]->getPageCount() - 1)); + + // set tab page + m_tabPage[m_curTab] = iCurrentPage; + + // update tab + switchTab(m_curTab); +} + +void UIScene_CreativeMenu::handleReload() +{ + Initialize( m_iPad, m_menu, false, -1, eSectionInventoryCreativeUsing, eSectionInventoryCreativeMax, m_bNavigateBack ); + + for(unsigned int i = 0; i < TabSpec::MAX_SIZE; ++i) + { + m_slotListContainer.addSlot(i); + } + + for(unsigned int i = TabSpec::MAX_SIZE; i < TabSpec::MAX_SIZE + 9; ++i) + { + m_slotListHotbar.addSlot(i); + } +} + +void UIScene_CreativeMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + // 4J-PB - going to ignore repeats on this scene + if(repeat) return; + + //app.DebugPrintf("UIScene_CreativeMenu handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + int dir = 1; + switch(key) + { + case VK_PAD_LSHOULDER: + dir = -1; + // Fall through intentional + case VK_PAD_RSHOULDER: + { + ECreativeInventoryTabs tab = (ECreativeInventoryTabs)(m_curTab + dir); + if (tab < 0) tab = (ECreativeInventoryTabs)(eCreativeInventoryTab_COUNT - 1); + if (tab >= eCreativeInventoryTab_COUNT) tab = eCreativeInventoryTab_BuildingBlocks; + switchTab(tab); + ui.PlayUISFX(eSFX_Focus); + } + break; + case VK_PAD_LTRIGGER: + // change the potion strength + { + ++m_tabDynamicPos[m_curTab]; + if(m_tabDynamicPos[m_curTab] >= specs[m_curTab]->m_dynamicGroupsCount) m_tabDynamicPos[m_curTab] = 0; + switchTab(m_curTab); + } + break; + default: + UIScene_AbstractContainerMenu::handleInput(iPad,key,repeat,pressed,released,handled); + break; + } +} + +void UIScene_CreativeMenu::updateTabHighlightAndText(ECreativeInventoryTabs tab) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = (F64)tab; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcSetActiveTab , 1 , value ); + + m_labelInventory.setLabel(app.GetString(specs[tab]->m_descriptionId)); +} + +int UIScene_CreativeMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + cols = 10; + break; + case eSectionInventoryCreativeUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_CreativeMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + rows = 5; + break; + case eSectionInventoryCreativeUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_CreativeMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + pPosition->x = m_slotListContainer.getXPos(); + pPosition->y = m_slotListContainer.getYPos(); + break; + case eSectionInventoryCreativeUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + case eSectionInventoryCreativeTab_0: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_0].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_0].getYPos(); + break; + case eSectionInventoryCreativeTab_1: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_1].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_1].getYPos(); + break; + case eSectionInventoryCreativeTab_2: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_2].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_2].getYPos(); + break; + case eSectionInventoryCreativeTab_3: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_3].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_3].getYPos(); + break; + case eSectionInventoryCreativeTab_4: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_4].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_4].getYPos(); + break; + case eSectionInventoryCreativeTab_5: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_5].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_5].getYPos(); + break; + case eSectionInventoryCreativeTab_6: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_6].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_6].getYPos(); + break; + case eSectionInventoryCreativeTab_7: + pPosition->x = m_TouchInput[ETouchInput_TouchPanel_7].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchPanel_7].getYPos(); + break; + case eSectionInventoryCreativeSlider: + pPosition->x = m_TouchInput[ETouchInput_TouchSlider].getXPos(); + pPosition->y = m_TouchInput[ETouchInput_TouchSlider].getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_CreativeMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + sectionSize.x = m_slotListContainer.getWidth(); + sectionSize.y = m_slotListContainer.getHeight(); + break; + case eSectionInventoryCreativeUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + case eSectionInventoryCreativeTab_0: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_0].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_0].getHeight(); + break; + case eSectionInventoryCreativeTab_1: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_1].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_1].getHeight(); + break; + case eSectionInventoryCreativeTab_2: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_2].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_2].getHeight(); + break; + case eSectionInventoryCreativeTab_3: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_3].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_3].getHeight(); + break; + case eSectionInventoryCreativeTab_4: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_4].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_4].getHeight(); + break; + case eSectionInventoryCreativeTab_5: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_5].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_5].getHeight(); + break; + case eSectionInventoryCreativeTab_6: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_6].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_6].getHeight(); + break; + case eSectionInventoryCreativeTab_7: + sectionSize.x = m_TouchInput[ETouchInput_TouchPanel_7].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchPanel_7].getHeight(); + break; + case eSectionInventoryCreativeSlider: + sectionSize.x = m_TouchInput[ETouchInput_TouchSlider].getWidth(); + sectionSize.y = m_TouchInput[ETouchInput_TouchSlider].getHeight(); + break; + default: + assert( false ); + break; + } + + if(IsSectionSlotList(eSection)) + { + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; + } + else + { + GetPositionOfSection(eSection, pPosition); + pSize->x = sectionSize.x; + pSize->y = sectionSize.y; + } +} + +void UIScene_CreativeMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + slotList = &m_slotListContainer; + break; + case eSectionInventoryCreativeUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_CreativeMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionInventoryCreativeSelector: + control = &m_slotListContainer; + break; + case eSectionInventoryCreativeUsing: + control = &m_slotListHotbar; + break; + case eSectionInventoryCreativeTab_0: + control = &m_TouchInput[ETouchInput_TouchPanel_0]; + break; + case eSectionInventoryCreativeTab_1: + control = &m_TouchInput[ETouchInput_TouchPanel_1]; + break; + case eSectionInventoryCreativeTab_2: + control = &m_TouchInput[ETouchInput_TouchPanel_2]; + break; + case eSectionInventoryCreativeTab_3: + control = &m_TouchInput[ETouchInput_TouchPanel_3]; + break; + case eSectionInventoryCreativeTab_4: + control = &m_TouchInput[ETouchInput_TouchPanel_4]; + break; + case eSectionInventoryCreativeTab_5: + control = &m_TouchInput[ETouchInput_TouchPanel_5]; + break; + case eSectionInventoryCreativeTab_6: + control = &m_TouchInput[ETouchInput_TouchPanel_6]; + break; + case eSectionInventoryCreativeTab_7: + control = &m_TouchInput[ETouchInput_TouchPanel_7]; + break; + case eSectionInventoryCreativeSlider: + control = &m_TouchInput[ETouchInput_TouchSlider]; + break; + default: + assert( false ); + break; + } + return control; +} + +void UIScene_CreativeMenu::updateScrollCurrentPage(int currentPage, int pageCount) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = (F64)pageCount; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (F64)currentPage - 1; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ) , m_funcSetScrollBar , 2 , value ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_CreativeMenu.h b/Minecraft.Client/Common/UI/UIScene_CreativeMenu.h new file mode 100644 index 0000000..6fbec2b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_CreativeMenu.h @@ -0,0 +1,90 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_CreativeMenu.h" + +class UIScene_CreativeMenu : public UIScene_AbstractContainerMenu, public IUIScene_CreativeMenu +{ +public: + UIScene_CreativeMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_CreativeMenu;} + +protected: + UIControl_SlotList m_slotListContainer; + IggyName m_funcSetActiveTab, m_funcSetScrollBar; + + enum ETouchInput + { + ETouchInput_TouchPanel_0, + ETouchInput_TouchPanel_1, + ETouchInput_TouchPanel_2, + ETouchInput_TouchPanel_3, + ETouchInput_TouchPanel_4, + ETouchInput_TouchPanel_5, + ETouchInput_TouchPanel_6, + ETouchInput_TouchPanel_7, + ETouchInput_TouchSlider, + + ETouchInput_Count, + }; + +#ifdef __PSVITA__ + // 4J - TomK - this only needs to be a touch component on vita! + UIControl_Touch m_TouchInput[ETouchInput_Count]; +#else + UIControl_Base m_TouchInput[ETouchInput_Count]; +#endif + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_0], "TouchPanel_0" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_1], "TouchPanel_1" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_2], "TouchPanel_2" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_3], "TouchPanel_3" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_4], "TouchPanel_4" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_5], "TouchPanel_5" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_6], "TouchPanel_6" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchPanel_7], "TouchPanel_7" ) + UI_MAP_ELEMENT( m_TouchInput[ETouchInput_TouchSlider], "TouchPanel_Slider" ) + + UI_MAP_ELEMENT( m_slotListContainer, "containerList") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME(m_funcSetActiveTab, L"SetActiveTab") + UI_MAP_NAME(m_funcSetScrollBar, L"SetScrollBar") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); + + virtual void handleOtherClicked(int iPad, ESceneSection eSection, int buttonNum, bool quickKey); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +#ifdef __PSVITA__ + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); + virtual UIControl* GetMainPanel(); + virtual void handleTouchBoxRebuild(); + virtual void handleTimerComplete(int id); +#endif + virtual void ScrollBar(UIVec2D pointerPos); + +private: + // IUIScene_CreativeMenu + void updateTabHighlightAndText(ECreativeInventoryTabs tab); + void updateScrollCurrentPage(int currentPage, int pageCount); + bool m_bFirstCall; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_Credits.cpp b/Minecraft.Client/Common/UI/UIScene_Credits.cpp new file mode 100644 index 0000000..1155859 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Credits.cpp @@ -0,0 +1,677 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_Credits.h" + +#define CREDIT_ICON -2 + +SCreditTextItemDef UIScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] = +{ + { L"MOJANG", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_ORIGINALDESIGN, NO_TRANSLATED_STRING,eLargeText }, + { L"Markus Persson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_PMPROD, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Kaplan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_RESTOFMOJANG, NO_TRANSLATED_STRING,eMediumText }, + { L"%ls", IDS_CREDITS_LEADPC, NO_TRANSLATED_STRING,eLargeText }, + { L"Jens Bergensten", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_JON_KAGSTROM, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_CEO, NO_TRANSLATED_STRING,eLargeText }, + { L"Carl Manneh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_DOF, NO_TRANSLATED_STRING,eLargeText }, + { L"Lydia Winters", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_WCW, NO_TRANSLATED_STRING,eLargeText }, + { L"Karin Severinsson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_CUSTOMERSUPPORT, NO_TRANSLATED_STRING,eLargeText }, + { L"Marc Watson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_DESPROG, NO_TRANSLATED_STRING,eLargeText }, + { L"Aron Nieminen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_CHIEFARCHITECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Frisk", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_CODENINJA, NO_TRANSLATED_STRING,eLargeText }, + { L"%ls", IDS_CREDITS_TOBIAS_MOLLSTAM, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_OFFICEDJ, NO_TRANSLATED_STRING,eLargeText }, + { L"Kristoffer Jelbring", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_DEVELOPER, NO_TRANSLATED_STRING,eLargeText }, + { L"Leonard Axelsson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_BULLYCOORD, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakob Porser", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_ARTDEVELOPER, NO_TRANSLATED_STRING,eLargeText }, + { L"Junkboy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_EXPLODANIM, NO_TRANSLATED_STRING,eLargeText }, + { L"Mattis Grahm", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_CONCEPTART, NO_TRANSLATED_STRING,eLargeText }, + { L"Henrik Petterson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_CRUNCHER, NO_TRANSLATED_STRING,eLargeText }, + { L"Patrick Geuder", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%ls", IDS_CREDITS_MUSICANDSOUNDS, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Rosenfeld (C418)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"4J Studios", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"%ls", IDS_CREDITS_PROGRAMMING, NO_TRANSLATED_STRING,eLargeText }, + { L"Paddy Burns", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Richard Reavy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Stuart Ross", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"James Vaughan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mark Hughes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Harry Gordon", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Thomas Kronberg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + { L"Ian le Bruce", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Andy West", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gordon McLean", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + +#ifdef __PSVITA__ +// 4J-PB - Aaron didn't want to be in the credits { L"Aaron Puzey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Dawson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + + { L"%ls", IDS_CREDITS_ART, NO_TRANSLATED_STRING,eLargeText }, + { L"David Keningale", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + { L"Pat McGovern", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + { L"Alan Redmond", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + { L"Julian Laing", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Caitlin Goodale", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Sutherland", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + { L"Chris Reeves", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kate Wright", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Hansen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + { L"Kate Flavell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + { L"Donald Robertson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jamie Keddie", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Thomas Naylor", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brian Lindsay", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hannah Watts", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rebecca O'Neil", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"%ls", IDS_CREDITS_QA, NO_TRANSLATED_STRING,eLargeText }, + { L"Steven Gary Woodward", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + { L"Richard Black", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#endif + { L"George Vaughan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris van der Kuyl", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Roni Percy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Anne Clarke", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Anthony Kent", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, +#ifdef _XBOX + // credits are in the XUI file +#elif defined(__PS3__) +// font credits + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_DYNAFONT, NO_TRANSLATED_STRING,eLargeText }, + +#elif defined(__ORBIS__) +// font credits + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_DYNAFONT, NO_TRANSLATED_STRING,eLargeText }, + +#elif defined(_DURANGO) + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"Xbox LIVE Arcade Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_LEADPRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Roger Carpenter", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Stuart Platt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Riccardo Lenzi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_LEADTESTER, NO_TRANSLATED_STRING,eLargeText }, + { L"Bill Brown (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brandon McCurry (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hakim Ronaque, Joe Dunavant", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paul Loynd, Jeffery Stephens", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rial Lerum (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DESIGNTEAM, NO_TRANSLATED_STRING,eLargeText }, + { L"Craig Leigh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DEVELOPMENTTEAM, NO_TRANSLATED_STRING,eLargeText }, + { L"Scott Guest", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jeff \"Dextor\" Blazier", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yukie Yamaguchi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Hewitt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_RELEASEMANAGEMENT, NO_TRANSLATED_STRING,eLargeText }, + { L"Isaac Aubrey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jordan Forbes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Josh Mulanax", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shogo Ishii (TekSystems)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Keenan (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Joshua Bullard (TekSystems)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"GTO-E Compliance", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Dominic Gara", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"James Small", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"%s", IDS_CREDITS_EXECPRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Mark Coates", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Avi Ben-Menahem", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Earnest Yuen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + + { L"%s", IDS_CREDITS_XBLADIRECTOR, NO_TRANSLATED_STRING,eLargeText }, + { L"Ted Woolsey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_BIZDEV, NO_TRANSLATED_STRING,eLargeText }, + { L"Cherie Lutz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Peter Zetterberg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PORTFOLIODIRECTOR, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris Charla", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PRODUCTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel McConnell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_MARKETING, NO_TRANSLATED_STRING,eLargeText }, + { L"Brandon Wells", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Wolf", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Dongelmans", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_COMMUNITYMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Alex Hebert", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_REDMONDLOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Zeb Wedell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gabriella Mittiga (Pactera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Fielding (Global Studio Consulting)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yong Zhao (Hisoft Envisage Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shogo Ishii (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_EUROPELOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Gerard Dunne", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ricardo Cordoba", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Magali Lucchini", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Malika Kherfi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lizzy Untermann", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ian Walsh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alfonsina Mossello (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marika Mauri (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nobuhiro Izumisawa (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Sebastien Faucon (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jose Manuel Martinez (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Montse Garcia (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ASIALOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Takashi Sasaki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Changseon Ha", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shinya Muto (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hiroshi Hosoda (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Natsuko Kudo (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yong-Hong Park (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yuko Yoshida (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_USERRESEARCH, NO_TRANSLATED_STRING,eLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"User Research Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Tim Nichols", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"User Research Engineer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Michael Medlock", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kristie Fisher", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_MGSCENTRAL, NO_TRANSLATED_STRING,eLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"Test Team Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Dan Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_MILESTONEACCEPT, NO_TRANSLATED_STRING,eLargeText }, + { L"Justin Davis (VMC)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Microsoft Studios Sentient Development Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Ellery Charlson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Frank Klier", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Ronald", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Cullen Waters", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Steve Jackson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Barath Vasudevan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Derek Mantey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Henry Sterchi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Fintel", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Soren Hannibal Nielsen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Meetali Goel (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Uladzimir Sadouski (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + + { L"Allan Murphy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Allison Bokone", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alvin Chen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arthur Yung", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brian Tyler", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Daniel Taylor", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dave Reed", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Duoc Nguyen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Eric Voreis", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Evelyn Thomas", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jeff Braunstein", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jolynn Carpenter", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Justin Brown", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kareem Choudhry", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin Cogger", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin La Chapelle", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Luc Rancourt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Matt Bronder", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Siebert", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mike Harsh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mike Sterling", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nick Rapp", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Orr Keshet", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paul Hellyar", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Peter Giffin", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Richard Moe", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Selfon", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Stephane St-Michel", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Steve Spiller", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Steven Trombetta", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Theo Michel", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tina Lemire", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tom Miller", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Travis St. Onge", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Brianna Witherspoon (Nytec Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jim Pekola (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Greg Hjertager", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Masha Reutovski (Nytec Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Henry", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Matt Golz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Gaffney (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jared Barnhill (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Laura Hawkins", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"2nd Cavalry", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"GTO Bug Bash Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Oliver Miyashita", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin Salcedo", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nick Bodenham", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Giggins", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ben Board", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Peter Choi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Andy Su (CompuCom Systems Inc.)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Boker ", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Josh Bliggenstorfer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paul Amer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Louise Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karin Behland (Aquent LLC)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Bruno", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Phil Spencer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Christi Davisson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacob Farley (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chad Stringer (Collabera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rick Rispoli (Collabera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Test by Experis", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"%s", IDS_CREDITS_TESTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Matt Brown", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gavin Kennedy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SRTESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Lloyd Bell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tim Attuquayefio", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Byron R. Monzon", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marta Alombro", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SDET, NO_TRANSLATED_STRING,eLargeText }, + { L"Valeriy Novytskyy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PROJECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Allyson Burk", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Scott", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Shearer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ADDITIONALSTE, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris Merritt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kimberlee Lyles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Eric Ranz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Russ Allen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTASSOCIATES, NO_TRANSLATED_STRING,eLargeText }, + { L"Michael Arvat", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Josh Breese", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"April Culberson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Fox", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Clayton K. Hopper", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Matthew Howells", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alan Hume", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacob Martin", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin Lourigan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Lovemark", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_RISE_LUGO, NO_TRANSLATED_STRING,eSmallText }, + { L"Ryan Naegeli", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Isaac Price", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Masha Reutovski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brad Shockey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jonathan Tote", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marc Williams", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gillian Williams", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jeffrey Woito", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Young", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jae Yslas", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Amanda Swalling", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ben Dienes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Kent", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dustin Lukas", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Emily Lovering", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nick Fowler", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + // EVEN MORE CREDITS + { L"Test by Lionbridge", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_TESTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Blazej Zawadzki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakub Garwacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Lahti", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mariusz Gelnicki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karol Falak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Watroba", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"%s", IDS_CREDITS_PROJECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Artur Grochowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Grzegorz Kohorewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Derewonko", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Celej", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Senior Test Engineers", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakub Rybacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mateusz Szymanski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Szczytowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rafal Rawski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"%s", IDS_CREDITS_TESTASSOCIATES, NO_TRANSLATED_STRING,eLargeText }, + { L"Adrian Klepacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Aleksander Pietraszak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Arkadiusz Kala", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Sykula", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Bartlomiej Kmita", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Malinowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jan Prejs", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jedrzej Kucharek", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Dabrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Maciej Urlo", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Maciej Wygoda", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Piasecki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Piotrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marek Latacz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Biernat", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Krupinski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Warchal", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Wascinski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Zbrzezniak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Milosz Maciejewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Pawel Kumanowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Przemyslaw Malinowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Dabrowicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Trzebiatowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Wojciech Kujawa", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Blazej Kohorewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Damian Mielnik", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dariusz Nowakowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dominik Rzeznicki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacek Piotrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Rybacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Wozniakowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jaroslaw Radzio", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Kaczor", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karolina Szymanska", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Konrad Mady", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Krzysztof Galazka", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ludwik Miszta", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Kwiatkowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Krzysiak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mateusz Szymanski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Maslany", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Nyszka", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Norbert Jankowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Piotr Daszewski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Radoslaw Kozlowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Kalowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + { L"David Hickey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Sean Kellogg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Adam Keating", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jerzy Tyminski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paulina Sliwinska", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + + { L"Test by Shield", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"GTO Shared Service Test Manager", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Natahri Felton", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shield Test Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Matt Giddings", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shield IT Support", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"David Grant (Compucom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Primary Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Alex Chen (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alex Hunte (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brian Boye (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Bridgette Cummins (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Carleson (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Christopher Hermey (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Hendrickson (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ioana Preda (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jessica Jenkins (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Johnathan Ochs (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Upham (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nicholas Johansson (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nicholas Starner (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Torr Vickers (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Victoria Bruder (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + +#elif defined(_WIN64) +#elif defined(__PSVITA__) +// font credits + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%ls", IDS_DYNAFONT, NO_TRANSLATED_STRING,eLargeText }, + +#endif + +#ifndef _XBOX +// Miles & Iggy credits + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"", CREDIT_ICON, eCreditIcon_Iggy,eSmallText }, // extra blank line + { L"Uses Iggy.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#ifdef __PS3__ + { L"Copyright (C) 2009-2013 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#else + { L"Copyright (C) 2009-2014 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#endif + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"", CREDIT_ICON, eCreditIcon_Miles,eSmallText }, // extra blank line + { L"Uses Miles Sound System.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#ifdef __PS3__ + { L"Copyright (C) 1991-2013 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#else + { L"Copyright (C) 1991-2014 by RAD Game Tools, Inc.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#endif +#ifdef __PS3__ + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"", CREDIT_ICON, eCreditIcon_Dolby,eSmallText }, // extra blank line + { L"Dolby and the double-D symbol", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"are trademarks of Dolby Laboratories.", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line +#endif +#endif +}; + +UIScene_Credits::UIScene_Credits(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bAddNextLabel = false; + + // How many lines of text are in the credits? + m_iNumTextDefs = MAX_CREDIT_STRINGS; + + // Are there any additional lines needed for the DLC credits? + m_iNumTextDefs+=app.GetDLCCreditsCount(); + + m_iCurrDefIndex = -1; + + // Add the first 20 Flash can cope with + for(unsigned int i = 0; i < 20; ++i) + { + ++m_iCurrDefIndex; + + // Set up the new text element. + if ( gs_aCreditDefs[i].m_iStringID[0] == NO_TRANSLATED_STRING ) + { + setNextLabel(gs_aCreditDefs[i].m_Text,gs_aCreditDefs[i].m_eType); + } + else // using additional translated string. + { + LPWSTR creditsString = new wchar_t[ 128 ]; + if(gs_aCreditDefs[i].m_iStringID[1]!=NO_TRANSLATED_STRING) + { + swprintf( creditsString, 128, gs_aCreditDefs[i].m_Text, app.GetString( gs_aCreditDefs[i].m_iStringID[0] ), app.GetString( gs_aCreditDefs[i].m_iStringID[1] ) ); + } + else + { + swprintf( creditsString, 128, gs_aCreditDefs[i].m_Text, app.GetString( gs_aCreditDefs[i].m_iStringID[0] ) ); + } + setNextLabel(creditsString,gs_aCreditDefs[i].m_eType); + delete [] creditsString; + } + } +} + +wstring UIScene_Credits::getMoviePath() +{ + return L"Credits"; +} + +void UIScene_Credits::updateTooltips() +{ + ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_BACK); +} + +void UIScene_Credits::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); +} + +void UIScene_Credits::handleReload() +{ + // We don't allow this in splitscreen, so just go back + navigateBack(); +} + +void UIScene_Credits::tick() +{ + UIScene::tick(); + + if(m_bAddNextLabel) + { + m_bAddNextLabel = false; + + const SCreditTextItemDef* pDef; + + // Time to create next text item. + ++m_iCurrDefIndex; + + // Wrap back to start. + if ( m_iCurrDefIndex >= m_iNumTextDefs ) + { + m_iCurrDefIndex = 0; + } + + if(m_iCurrDefIndex >= MAX_CREDIT_STRINGS) + { + app.DebugPrintf("DLC credit %d\n",m_iCurrDefIndex-MAX_CREDIT_STRINGS); + // DLC credit + pDef = app.GetDLCCredits(m_iCurrDefIndex-MAX_CREDIT_STRINGS); + } + else + { + // Get text def for this item. + pDef = &( gs_aCreditDefs[ m_iCurrDefIndex ] ); + } + + // Set up the new text element. + if(pDef->m_Text!=NULL) // 4J-PB - think the RAD logo ones aren't set up yet and are coming is as null + { + + if ( pDef->m_iStringID[0] == CREDIT_ICON ) + { + addImage((ECreditIcons)pDef->m_iStringID[1]); + } + else if ( pDef->m_iStringID[0] == NO_TRANSLATED_STRING ) + { + setNextLabel(pDef->m_Text,pDef->m_eType); + } + else // using additional translated string. + { + LPWSTR creditsString = new wchar_t[ 128 ]; + if(pDef->m_iStringID[1]!=NO_TRANSLATED_STRING) + { + swprintf( creditsString, 128, pDef->m_Text, app.GetString( pDef->m_iStringID[0] ), app.GetString( pDef->m_iStringID[1] ) ); + } + else + { + swprintf( creditsString, 128, pDef->m_Text, app.GetString( pDef->m_iStringID[0] ) ); + } + setNextLabel(creditsString,pDef->m_eType); + delete [] creditsString; + } + } + } +} + +void UIScene_Credits::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %ls, pressed- %ls, released- %ls\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed && !repeat) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_Credits::setNextLabel(const wstring &label, ECreditTextTypes size) +{ + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (int)size; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = (m_iCurrDefIndex == (m_iNumTextDefs - 1)); + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetNextLabel , 3 , value ); +} + +void UIScene_Credits::addImage(ECreditIcons icon) +{ + IggyDataValue result; + IggyDataValue value[2]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = (int)icon; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = (m_iCurrDefIndex == (m_iNumTextDefs - 1)); + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAddImage , 2 , value ); +} + +void UIScene_Credits::handleRequestMoreData(F64 startIndex, bool up) +{ + m_bAddNextLabel = true; +} diff --git a/Minecraft.Client/Common/UI/UIScene_Credits.h b/Minecraft.Client/Common/UI/UIScene_Credits.h new file mode 100644 index 0000000..4116628 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Credits.h @@ -0,0 +1,71 @@ +#pragma once + +#include "UIScene.h" + +#define PS3_CREDITS_COUNT 75 +#define PSVITA_CREDITS_COUNT 77 +#define PS4_CREDITS_COUNT 75 +#define XBOXONE_CREDITS_COUNT (75+318) +#define MILES_AND_IGGY_CREDITS_COUNT 8 +#define DYNAMODE_FONT_CREDITS_COUNT 2 +#define PS3_DOLBY_CREDIT 4 + + +#ifdef __PS3__ +#define MAX_CREDIT_STRINGS (PS3_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT + PS3_DOLBY_CREDIT) +#elif defined(__ORBIS__) +#define MAX_CREDIT_STRINGS (PS4_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT) +#elif defined(_DURANGO) || defined _WIN64 +#define MAX_CREDIT_STRINGS (XBOXONE_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT) +#elif defined(__PSVITA__) +#define MAX_CREDIT_STRINGS (PSVITA_CREDITS_COUNT + MILES_AND_IGGY_CREDITS_COUNT + DYNAMODE_FONT_CREDITS_COUNT) +#endif + +class UIScene_Credits : public UIScene +{ +private: + enum ECreditIcons + { + eCreditIcon_Iggy, + eCreditIcon_Miles, + eCreditIcon_Dolby, + }; + + static SCreditTextItemDef gs_aCreditDefs[MAX_CREDIT_STRINGS]; + + int m_iCurrDefIndex; // Index of last created text def. + int m_iNumTextDefs; // Total number of text defs in the credits. + + bool m_bAddNextLabel; + + IggyName m_funcSetNextLabel, m_funcAddImage; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_NAME(m_funcSetNextLabel, L"SetNextLabel") + UI_MAP_NAME(m_funcAddImage, L"AddImage") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_Credits(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_Credits;} + + virtual void updateTooltips(); + virtual void updateComponents(); + + void handleReload(); + + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleRequestMoreData(F64 startIndex, bool up); + +private: + void setNextLabel(const wstring &label, ECreditTextTypes size); + void addImage(ECreditIcons icon); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.cpp b/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.cpp new file mode 100644 index 0000000..0d36dcd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.cpp @@ -0,0 +1,243 @@ +#include "stdafx.h" +#include "UI.h" +#if defined(__PS3__) || defined(__ORBIS__) +#include "Common\Network\Sony\SonyCommerce.h" +#endif +#include "UIScene_DLCMainMenu.h" + +#define PLAYER_ONLINE_TIMER_ID 0 +#define PLAYER_ONLINE_TIMER_TIME 100 + +UIScene_DLCMainMenu::UIScene_DLCMainMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + m_labelOffers.init(app.GetString(IDS_DOWNLOADABLE_CONTENT_OFFERS)); + m_buttonListOffers.init(eControl_OffersList); + +#if defined _XBOX_ONE || defined __ORBIS__ + // load any local DLC images + app.LoadLocalDLCImages(); +#endif + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + // show a timer on this menu + m_Timer.setVisible(true); + + m_bCategoriesShown=false; +#endif + + if(m_loadedResolution == eSceneResolution_1080) + { +#ifdef _DURANGO + m_labelXboxStore.init( app.GetString(IDS_XBOX_STORE) ); +#else + m_labelXboxStore.init( L"" ); +#endif + } + +#if defined(_DURANGO) + m_Timer.setVisible(false); + + m_buttonListOffers.addItem(app.GetString(IDS_DLC_MENU_SKINPACKS),e_DLC_SkinPack); + m_buttonListOffers.addItem(app.GetString(IDS_DLC_MENU_TEXTUREPACKS),e_DLC_TexturePacks); + m_buttonListOffers.addItem(app.GetString(IDS_DLC_MENU_MASHUPPACKS),e_DLC_MashupPacks); + + app.AddDLCRequest(e_Marketplace_Content); // content is skin packs, texture packs and mash-up packs + // we also need to mount the local DLC so we can tell what's been purchased + app.StartInstallDLCProcess(iPad); +#endif + + TelemetryManager->RecordMenuShown(iPad, eUIScene_DLCMainMenu, 0); + +#ifdef __ORBIS__ + sceNpCommerceShowPsStoreIcon(SCE_NP_COMMERCE_PS_STORE_ICON_RIGHT); +#elif defined __PSVITA__ + sceNpCommerce2ShowPsStoreIcon(SCE_NP_COMMERCE2_ICON_DISP_RIGHT); +#endif + +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + addTimer( PLAYER_ONLINE_TIMER_ID, PLAYER_ONLINE_TIMER_TIME ); +#endif +} + +UIScene_DLCMainMenu::~UIScene_DLCMainMenu() +{ + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); +#if defined _XBOX_ONE || defined __ORBIS__ + app.FreeLocalDLCImages(); +#endif +} + +wstring UIScene_DLCMainMenu::getMoviePath() +{ + return L"DLCMainMenu"; +} + +void UIScene_DLCMainMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); +} + +void UIScene_DLCMainMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { +#ifdef __ORBIS__ + sceNpCommerceHidePsStoreIcon(); +#elif defined __PSVITA__ + sceNpCommerce2HidePsStoreIcon(); +#endif + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_DLCMainMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_OffersList: + { + int iIndex = (int)childId; + DLCOffersParam *param = new DLCOffersParam(); + param->iPad = m_iPad; + + param->iType = iIndex; + // promote the DLC content request type + + // Xbox One will have requested the marketplace content - there is only that type +#ifndef _XBOX_ONE + app.AddDLCRequest((eDLCMarketplaceType)iIndex, true); +#endif + killTimer(PLAYER_ONLINE_TIMER_ID); + ui.NavigateToScene(m_iPad, eUIScene_DLCOffersMenu, param); + break; + } + }; +} + +void UIScene_DLCMainMenu::handleTimerComplete(int id) +{ +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) + switch(id) + { + case PLAYER_ONLINE_TIMER_ID: +#ifndef _WINDOWS64 + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())==false) + { + // check the player hasn't gone offline + // If they have, bring up the PSN warning and exit from the leaderboards + unsigned int uiIDA[1]; + uiIDA[0]=IDS_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CONNECTION_LOST, g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT), uiIDA,1,ProfileManager.GetPrimaryPad(),UIScene_DLCMainMenu::ExitDLCMainMenu,this, app.GetStringTable()); + } +#endif + break; + } +#endif +} + +int UIScene_DLCMainMenu::ExitDLCMainMenu(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_DLCMainMenu* pClass = (UIScene_DLCMainMenu*)pParam; + +#ifdef __ORBIS__ + sceNpCommerceHidePsStoreIcon(); +#elif defined __PSVITA__ + sceNpCommerce2HidePsStoreIcon(); +#endif + pClass->navigateBack(); + + return 0; +} + +void UIScene_DLCMainMenu::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + + updateTooltips(); + + if(navBack) + { + // add the timer back in +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + addTimer( PLAYER_ONLINE_TIMER_ID, PLAYER_ONLINE_TIMER_TIME ); +#endif + } +} + +void UIScene_DLCMainMenu::tick() +{ + UIScene::tick(); + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + if((m_bCategoriesShown==false) && (app.GetCommerceCategoriesRetrieved())) + { + // disable the timer display on this menu + m_Timer.setVisible(false); + m_bCategoriesShown=true; + + // add the categories to the list box + SonyCommerce::CategoryInfo *pCategories=app.GetCategoryInfo(); + std::list::iterator iter = pCategories->subCategories.begin(); + SonyCommerce::CategoryInfoSub category; + for(int i=0;icountOfSubCategories;i++) + { + // add a button in with the subcategory + category = (SonyCommerce::CategoryInfoSub)(*iter); + + string teststring=category.categoryName; + m_buttonListOffers.addItem(teststring,i); + + iter++; + } + + // set the focus to the first thing in the categories if there are any + if(pCategories->countOfSubCategories>0) + { + m_buttonListOffers.setFocus(true); + } + else + { +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + app.CheckForEmptyStore(ProfileManager.GetPrimaryPad()); +#endif + // need to display text to say no downloadable content available yet + m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES)); + +#ifdef __ORBIS__ + // 4J-JEV: TRC Requirement (R4055), need to display this system message. + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_EMPTY_STORE, ProfileManager.GetPrimaryPad() ); +#endif + } + + } +#endif +} + diff --git a/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.h b/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.h new file mode 100644 index 0000000..15272fe --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DLCMainMenu.h @@ -0,0 +1,50 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_DLCMainMenu : public UIScene +{ +private: + enum EControls + { + eControl_OffersList, + }; + + UIControl_ButtonList m_buttonListOffers; + UIControl_Label m_labelOffers, m_labelXboxStore; + UIControl m_Timer; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListOffers, "OffersList") + UI_MAP_ELEMENT( m_labelOffers, "OffersList_Title") + UI_MAP_ELEMENT( m_Timer, "Timer") + if(m_loadedResolution == eSceneResolution_1080) + { + UI_MAP_ELEMENT( m_labelXboxStore, "XboxLabel" ) + } + UI_END_MAP_ELEMENTS_AND_NAMES() + + static int ExitDLCMainMenu(void *pParam,int iPad,C4JStorage::EMessageResult result); + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + bool m_bCategoriesShown; +#endif + +public: + UIScene_DLCMainMenu(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_DLCMainMenu(); + virtual void handleTimerComplete(int id); + virtual void handleGainFocus(bool navBack); + + virtual EUIScene getSceneType() { return eUIScene_DLCMainMenu;} + virtual void tick(); + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handlePress(F64 controlId, F64 childId); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.cpp b/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.cpp new file mode 100644 index 0000000..9dbdc3d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.cpp @@ -0,0 +1,923 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_DLCOffersMenu.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) +#include "Common\Network\Sony\SonyHttp.h" +#endif + +#ifdef __PSVITA__ +#include "PSVita\Network\SonyCommerce_Vita.h" +#endif + +#define PLAYER_ONLINE_TIMER_ID 0 +#define PLAYER_ONLINE_TIMER_TIME 100 + +UIScene_DLCOffersMenu::UIScene_DLCOffersMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_bProductInfoShown=false; + DLCOffersParam *param=(DLCOffersParam *)initData; + m_iProductInfoIndex=param->iType; + m_iCurrentDLC=0; + m_iTotalDLC=0; +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + m_pvProductInfo=NULL; +#endif + m_bAddAllDLCButtons=true; + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + m_bIsSD=!RenderManager.IsHiDef() && !RenderManager.IsWidescreen(); + + m_labelOffers.init(app.GetString(IDS_DOWNLOADABLE_CONTENT_OFFERS)); + m_buttonListOffers.init(eControl_OffersList); + m_labelHTMLSellText.init(" "); + m_labelPriceTag.init(" "); + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_DLCOffersMenu, 0); + + m_bHasPurchased = false; + m_bIsSelected = false; + + if(m_loadedResolution == eSceneResolution_1080) + { +#ifdef _DURANGO + m_labelXboxStore.init( app.GetString(IDS_XBOX_STORE) ); +#else + m_labelXboxStore.init( L"" ); +#endif + } + +#ifdef _DURANGO + m_pNoImageFor_DLC = NULL; + // If we don't yet have this DLC, we need to display a timer + m_bDLCRequiredIsRetrieved=false; + m_bIgnorePress=true; + m_bSelectionChanged=true; + // display a timer + m_Timer.setVisible(true); + +#endif + +#ifdef __ORBIS__ + //sceNpCommerceShowPsStoreIcon(SCE_NP_COMMERCE_PS_STORE_ICON_CENTER); +#endif + +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + addTimer( PLAYER_ONLINE_TIMER_ID, PLAYER_ONLINE_TIMER_TIME ); +#endif + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} + +UIScene_DLCOffersMenu::~UIScene_DLCOffersMenu() +{ + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); +} + +void UIScene_DLCOffersMenu::handleTimerComplete(int id) +{ +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) + switch(id) + { + case PLAYER_ONLINE_TIMER_ID: +#ifndef _WINDOWS64 + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())==false) + { + // check the player hasn't gone offline + // If they have, bring up the PSN warning and exit from the DLC menu + unsigned int uiIDA[1]; + uiIDA[0]=IDS_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CONNECTION_LOST, g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT), uiIDA,1,ProfileManager.GetPrimaryPad(),UIScene_DLCOffersMenu::ExitDLCOffersMenu,this, app.GetStringTable()); + } +#endif + break; + } +#endif +} + +int UIScene_DLCOffersMenu::ExitDLCOffersMenu(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_DLCOffersMenu* pClass = (UIScene_DLCOffersMenu*)pParam; + +#ifdef __ORBIS__ + sceNpCommerceHidePsStoreIcon(); +#elif defined __PSVITA__ + sceNpCommerce2HidePsStoreIcon(); +#endif + ui.NavigateToHomeMenu();//iPad,eUIScene_MainMenu); + + return 0; +} + +wstring UIScene_DLCOffersMenu::getMoviePath() +{ + return L"DLCOffersMenu"; +} + +void UIScene_DLCOffersMenu::updateTooltips() +{ + int iA = -1; + if(m_bIsSelected) + { + if( !m_bHasPurchased ) + { + iA = IDS_TOOLTIPS_INSTALL; + } + else + { + iA = IDS_TOOLTIPS_REINSTALL; + } + } + ui.SetTooltips( m_iPad, iA,IDS_TOOLTIPS_BACK); +} + +void UIScene_DLCOffersMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + if(pressed) + { + // 4J - TomK don't proceed if there is no DLC to navigate through + if(m_iTotalDLC > 0) + { + if(m_iCurrentDLC > 0) + m_iCurrentDLC--; + + m_bProductInfoShown = false; + } + } + sendInputToMovie(key, repeat, pressed, released); + break; + + case ACTION_MENU_DOWN: + if(pressed) + { + // 4J - TomK don't proceed if there is no DLC to navigate through + if(m_iTotalDLC > 0) + { + if(m_iCurrentDLC < (m_iTotalDLC - 1)) + m_iCurrentDLC++; + + m_bProductInfoShown = false; + } + } + sendInputToMovie(key, repeat, pressed, released); + break; + + case ACTION_MENU_LEFT: + /* +#ifdef _DEBUG + static int iTextC=0; + switch(iTextC) + { + case 0: + m_labelHTMLSellText.init("Voici un fantastique mini-pack de 24 apparences pour personnaliser votre personnage Minecraft et vous mettre dans l'ambiance des ftes de fin d'anne.

1-4 joueurs
2-8 joueurs en rseau

Cet article fait lobjet dune licence ou dune sous-licence de Sony Computer Entertainment America, et est soumis aux conditions gnrales du service du rseau, au contrat dutilisateur, aux restrictions dutilisation de cet article et aux autres conditions applicables, disponibles sur le site www.us.playstation.com/support/useragreements. Si vous ne souhaitez pas accepter ces conditions, ne tlchargez pas ce produit. Cet article peut tre utilis avec un maximum de deux systmes PlayStation3 activs associs ce compte Sony Entertainment Network.

'Minecraft' est une marque commerciale de Notch Development AB."); + break; + case 1: + m_labelHTMLSellText.init("Un fabuloso minipack de 24 aspectos para personalizar tu personaje de Minecraft y ponerte a tono con las fiestas.

1-4 jugadores
2-8 jugadores en red

Sony Computer Entertainment America le concede la licencia o sublicencia de este artculo, que est sujeto a los trminos de servicio y al acuerdo de usuario de la red. Las restricciones de uso de este artculo, as como otros trminos aplicables, se encuentran en www.us.playstation.com/support/useragreements. Si no desea aceptar todos estos trminos, no descargue este artculo. Este artculo puede usarse en hasta dos sistemas PlayStation3 activados asociados con esta cuenta de Sony Entertainment Network.

'Minecraft' es una marca comercial de Notch Development AB."); + break; + case 2: + m_labelHTMLSellText.init("Este um incrvel pacote com 24 capas para personalizar seu personagem no Minecraft e entrar no clima de final de ano.

1-4 Jogadores
Jogadores em rede 2-8

Este item est sendo licenciado ou sublicenciado para voc pela Sony Computer Entertainment America e est sujeito aos Termos de Servio da Rede e Acordo do Usurio, as restries de uso deste item e outros termos aplicveis esto localizados em www.us.playstation.com/support/useragreements. Caso no queira aceitar todos esses termos, no baixe este item. Este item pode ser usado com at 2 sistemas PlayStation3 ativados associados a esta Conta de Rede Sony Entertainment.

'Minecraft' uma marca registrada da Notch Development AB"); + break; + } + iTextC++; + if(iTextC>2) iTextC=0; +#endif + */ + case ACTION_MENU_RIGHT: + case ACTION_MENU_OTHER_STICK_DOWN: + case ACTION_MENU_OTHER_STICK_UP: + // don't pass down PageUp or PageDown because this will cause conflicts between the buttonlist and scrollable html text component + //case ACTION_MENU_PAGEUP: + //case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_DLCOffersMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_OffersList: + { +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + // buy the DLC + + vector::iterator it = m_pvProductInfo->begin(); + string teststring; + for(int i=0;i-1)) + { + int iIndex = (int)childId; + MARKETPLACE_CONTENTOFFER_INFO xOffer = StorageManager.GetOffer(iIndex); + UpdateDisplay(xOffer); + }*/ +#endif + +#if defined __PSVITA__ || defined __ORBIS__ + if(m_pvProductInfo) + { + m_bIsSelected = true; + vector::iterator it = m_pvProductInfo->begin(); + string teststring; + for(int i=0;isize(); + } + + vector::iterator it = m_pvProductInfo->begin(); + string teststring; + bool bFirstItemSet=false; + for(int i=0;idwImageBytes!=0) + { + pbImageData=pSONYDLCInfo->pbImageData; + iImageDataBytes=pSONYDLCInfo->dwImageBytes; + bDeleteData=false; // we'll clean up the local LDC images + } + else +#endif + if(info.imageUrl[0]!=0) + { + SonyHttp::getDataFromURL(info.imageUrl,(void **)&pbImageData,&iImageDataBytes); + bDeleteData=true; + } + + if(iImageDataBytes!=0) + { + // set the image + registerSubstitutionTexture(textureName,pbImageData,iImageDataBytes,bDeleteData); + m_bitmapIconOfferImage.setTextureName(textureName); + // 4J Stu - Don't delete this + //delete [] pbImageData; + } + else + { + m_bitmapIconOfferImage.setTextureName(L""); + } + } + else + { + m_bitmapIconOfferImage.setTextureName(textureName); + } + } + it++; + } + + if(bFirstItemSet==false) + { + // we were not able to add any items to the list + m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES)); + } + else + { + // set the focus to the first thing in the categories if there are any + if(m_pvProductInfo->size()>0) + { + m_buttonListOffers.setFocus(true); + } + else + { + // need to display text to say no downloadable content available yet + m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES)); + } + } + + m_Timer.setVisible(false); + m_bProductInfoShown=true; + } + } + else + { +#ifdef __PSVITA__ + // MGH - fixes bug 5768 on Vita - should be extended properly to work for other platforms + if((SonyCommerce_Vita::getPurchasabilityUpdated()) && app.GetCommerceProductListRetrieved()&& app.GetCommerceProductListInfoRetrieved() && m_iTotalDLC > 0) + { + + { + vector::iterator it = m_pvProductInfo->begin(); + for(int i=0;i 0) + { + + + vector::iterator it = m_pvProductInfo->begin(); + string teststring; + for(int i=0;idwImageBytes!=0) + { + pbImageData=pSONYDLCInfo->pbImageData; + iImageDataBytes=pSONYDLCInfo->dwImageBytes; + bDeleteData=false; // we'll clean up the local LDC images + } + else +#endif + { + SonyHttp::getDataFromURL(info.imageUrl,(void **)&pbImageData,&iImageDataBytes); + bDeleteData=true; + } + + if(iImageDataBytes!=0) + { + // set the image + registerSubstitutionTexture(textureName,pbImageData,iImageDataBytes, bDeleteData); + m_bitmapIconOfferImage.setTextureName(textureName); + + // 4J Stu - Don't delete this + //delete [] pbImageData; + } + else + { + m_bitmapIconOfferImage.setTextureName(L""); + } + } + else + { + m_bitmapIconOfferImage.setTextureName(textureName); + } + m_bProductInfoShown=true; + m_Timer.setVisible(false); + } + + } +#elif defined _XBOX_ONE + if(m_bAddAllDLCButtons) + { + // Is the DLC we're looking for available? + if(!m_bDLCRequiredIsRetrieved) + { + // DLCContentRetrieved is to see if the type of content has been retrieved - and on Durango there is only type 0 - XMARKETPLACE_OFFERING_TYPE_CONTENT + if(app.DLCContentRetrieved(e_Marketplace_Content)) + { + m_bDLCRequiredIsRetrieved=true; + + // Retrieve the info + GetDLCInfo(app.GetDLCOffersCount(), false); + m_bIgnorePress=false; + m_bAddAllDLCButtons=false; + + // hide the timer + m_Timer.setVisible(false); + } + } + } + + // have to wait until we have the offers + if(m_bSelectionChanged && m_bDLCRequiredIsRetrieved) + { + // need to update text and icon + if(m_buttonListOffers.hasFocus() && (getControlChildFocus()>-1)) + { + int iIndex = getControlChildFocus(); + MARKETPLACE_CONTENTOFFER_INFO xOffer = StorageManager.GetOffer(iIndex); + + if(UpdateDisplay(xOffer)) + { + // image was available + m_bSelectionChanged=false; + } + } + } + +// if(m_bBitmapOfferIconDisplayed==false) +// { +// // do we have it yet? +// if +// } + // retrieve the icons for the DLC +// if(m_vIconRetrieval.size()>0) +// { +// // for each icon, request it, and remove it from the list +// // the callback for the retrieval will update the display if needed +// +// AUTO_VAR(itEnd, m_vIconRetrieval.end()); +// for (AUTO_VAR(it, m_vIconRetrieval.begin()); it != itEnd; it++) +// { +// +// } +// +// } +#endif +} + +#if defined _XBOX_ONE +void UIScene_DLCOffersMenu::GetDLCInfo( int iOfferC, bool bUpdateOnly ) +{ + MARKETPLACE_CONTENTOFFER_INFO xOffer; + int iCount=0; + bool bNoDLCToDisplay = true; + unsigned int uiDLCCount=0; + + + if(bUpdateOnly) // Just update the info on the current list + { + + } + else + { + // clear out the list + m_buttonListOffers.clearList(); + + // need to reorder the DLC display according to dlc uiSortIndex + SORTINDEXSTRUCT *OrderA = new SORTINDEXSTRUCT [iOfferC]; + + for(int i = 0; i < iOfferC; i++) + { + xOffer = StorageManager.GetOffer(i); + // Check that this is in the list of known DLC + DLC_INFO *pDLC=app.GetDLCInfoForFullOfferID(xOffer.wszProductID); + + if(pDLC!=NULL) + { + OrderA[uiDLCCount].uiContentIndex=i; + OrderA[uiDLCCount++].uiSortIndex=pDLC->uiSortIndex; + } + else + { + app.DebugPrintf("Unknown offer - %ls\n",xOffer.wszOfferName); + } + } + + qsort( OrderA, uiDLCCount, sizeof(SORTINDEXSTRUCT), OrderSortFunction ); + + for(int i = 0; i < uiDLCCount; i++) + { + xOffer = StorageManager.GetOffer(OrderA[i].uiContentIndex); + + // Check that this is in the list of known DLC + DLC_INFO *pDLC=app.GetDLCInfoForFullOfferID(xOffer.wszProductID); + + if(pDLC==NULL) + { + // skip this one + app.DebugPrintf("Unknown offer - %ls\n",xOffer.wszOfferName); + continue; + } + + if(pDLC->eDLCType==(eDLCContentType)m_iProductInfoIndex) + { + wstring wstrTemp=xOffer.wszOfferName; + + // 4J-PB - Rog requested we remove the Minecraft at the start of the name. It's required for the Bing search, but gets in the way here + app.DebugPrintf("Adding %ls at %d\n",wstrTemp.c_str(), i); + + if(wcsncmp(L"Minecraft ",wstrTemp.c_str(),10)==0) + { + app.DebugPrintf("Removing Minecraft from name\n"); + WCHAR *pwchNewName=(WCHAR *)wstrTemp.c_str(); + wstrTemp=&pwchNewName[10]; + } + +#ifdef _XBOX_ONE + // 4J-PB - the hasPurchased comes from the local installed package info + // find the DLC in the installed packages + XCONTENT_DATA *pContentData=StorageManager.GetInstalledDLC(xOffer.wszProductID); + + if(pContentData!=NULL) + { + m_buttonListOffers.addItem(wstrTemp,!pContentData->bTrialLicense,OrderA[i].uiContentIndex); + } + else + { + m_buttonListOffers.addItem(wstrTemp,false,OrderA[i].uiContentIndex); + } +#else + m_buttonListOffers.addItem(wstrTemp,xOffer.fUserHasPurchased,OrderA[i].uiContentIndex); +#endif + + // add the required image to the retrieval queue + m_vIconRetrieval.push_back(pDLC->wchBanner); + + /** 4J JEV: + * We've filtered results out from the list, need to keep track + * of the 'actual' list index. + */ + iCount++; + } + } + + + // Check if there is nothing to display, and display the default "nothing available at this time" + if(iCount>0) + { + bNoDLCToDisplay=false; + xOffer = StorageManager.GetOffer(OrderA[0].uiContentIndex); + //m_buttonListOffers.setCurrentSelection(0); + + UpdateDisplay(xOffer); + } + delete OrderA; + } + + // turn off the timer display + //m_Timer.SetShow(FALSE); + if(bNoDLCToDisplay) + { + // set the default text + + wchar_t formatting[40]; + wstring wstrTemp = app.GetString(IDS_NO_DLCOFFERS); +// swprintf(formatting, 40, L"", m_bIsSD?12:14); +// wstrTemp = formatting + wstrTemp; + + m_labelHTMLSellText.setLabel(wstrTemp); + m_labelPriceTag.setVisible(false); + } +} + +int UIScene_DLCOffersMenu::OrderSortFunction(const void* a, const void* b) +{ + return ((SORTINDEXSTRUCT*)b)->uiSortIndex - ((SORTINDEXSTRUCT*)a)->uiSortIndex; +} + +void UIScene_DLCOffersMenu::UpdateTooltips(MARKETPLACE_CONTENTOFFER_INFO& xOffer) +{ + m_bHasPurchased = xOffer.fUserHasPurchased; + m_bIsSelected = true; + updateTooltips(); +} + +bool UIScene_DLCOffersMenu::UpdateDisplay(MARKETPLACE_CONTENTOFFER_INFO& xOffer) +{ + bool bImageAvailable=false; +#ifdef _XBOX_ONE + DLC_INFO *dlc = app.GetDLCInfoForFullOfferID(xOffer.wszProductID); +#else + DLC_INFO *dlc = app.GetDLCInfoForFullOfferID(xOffer.wszOfferName); +#endif + + if (dlc != NULL) + { + WCHAR *cString = dlc->wchBanner; + + + // is the file in the local DLC images? + // is the file in the TMS XZP? + //int iIndex = app.GetLocalTMSFileIndex(cString, true); + + if(dlc->dwImageBytes!=0) + { + //app.LoadLocalTMSFile(cString); + + // set the image - no delete + registerSubstitutionTexture(cString,dlc->pbImageData,dlc->dwImageBytes,false); + m_bitmapIconOfferImage.setTextureName(cString); + bImageAvailable=true; + } + else + { + bool bPresent = app.IsFileInMemoryTextures(cString); + if (!bPresent) + { + // Image has not come in yet + // Set the item monitored in the timer, so we can set the image when it comes in + m_pNoImageFor_DLC=dlc; + + app.AddTMSPPFileTypeRequest(dlc->eDLCType,true); + bImageAvailable=false; + //m_bitmapIconOfferImage.setTextureName(L""); + } + else + { + if(hasRegisteredSubstitutionTexture(cString)==false) + { + BYTE *pData=NULL; + DWORD dwSize=0; + app.GetMemFileDetails(cString,&pData,&dwSize); + // set the image +#ifdef _XBOX_ONE + registerSubstitutionTexture(cString,pData,dwSize); +#else + registerSubstitutionTexture(cString,pData,dwSize,true); +#endif + m_bitmapIconOfferImage.setTextureName(cString); + } + else + { + m_bitmapIconOfferImage.setTextureName(cString); + } + bImageAvailable=true; + } + } + + m_labelHTMLSellText.setLabel(xOffer.wszSellText); + + // set the price info + m_labelPriceTag.setVisible(true); + m_labelPriceTag.setLabel(xOffer.wszCurrencyPrice); + + UpdateTooltips(xOffer); + } + else + { + wchar_t formatting[40]; + wstring wstrTemp = app.GetString(IDS_NO_DLCOFFERS); + m_labelHTMLSellText.setLabel(wstrTemp.c_str()); + m_labelPriceTag.setVisible(false); + } + + return bImageAvailable; +} +#endif + +#ifdef _XBOX_ONE +void UIScene_DLCOffersMenu::HandleDLCLicenseChange() +{ + // flag an update of the display + int iOfferC=app.GetDLCOffersCount(); + + GetDLCInfo(iOfferC,false); +} +#endif // _XBOX_ONE + +#ifdef __PS3__ +void UIScene_DLCOffersMenu::HandleDLCInstalled() +{ + app.DebugPrintf(4,"UIScene_DLCOffersMenu::HandleDLCInstalled\n"); + +// m_buttonListOffers.clearList(); +// m_bAddAllDLCButtons=true; +// m_bProductInfoShown=false; +} + +// void UIScene_DLCOffersMenu::HandleDLCMountingComplete() +// { +// app.DebugPrintf(4,"UIScene_SkinSelectMenu::HandleDLCMountingComplete\n"); +//} + + +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.h b/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.h new file mode 100644 index 0000000..c5fcac7 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DLCOffersMenu.h @@ -0,0 +1,96 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_DLCOffersMenu : public UIScene +{ +private: + enum EControls + { + eControl_OffersList, + }; + + bool m_bIsSD; + bool m_bHasPurchased; + bool m_bIsSelected; + + UIControl_DLCList m_buttonListOffers; + UIControl_Label m_labelOffers, m_labelPriceTag, m_labelXboxStore; + UIControl_HTMLLabel m_labelHTMLSellText; + UIControl_BitmapIcon m_bitmapIconOfferImage; + UIControl m_Timer; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListOffers, "OffersList") + UI_MAP_ELEMENT( m_labelOffers, "OffersList_Title") + UI_MAP_ELEMENT( m_labelPriceTag, "PriceTag") + UI_MAP_ELEMENT( m_labelHTMLSellText, "HTMLSellText") + UI_MAP_ELEMENT( m_bitmapIconOfferImage, "DLCIcon" ) + UI_MAP_ELEMENT( m_Timer, "Timer") + + if(m_loadedResolution == eSceneResolution_1080) + { + UI_MAP_ELEMENT( m_labelXboxStore, "XboxLabel" ) + } + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_DLCOffersMenu(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_DLCOffersMenu(); + static int ExitDLCOffersMenu(void *pParam,int iPad,C4JStorage::EMessageResult result); + + virtual EUIScene getSceneType() { return eUIScene_DLCOffersMenu;} + virtual void tick(); + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handlePress(F64 controlId, F64 childId); + virtual void handleSelectionChanged(F64 selectedId); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleTimerComplete(int id); +#ifdef __PS3__ + virtual void HandleDLCInstalled(); +#endif + +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + +private: +#ifdef _DURANGO + void GetDLCInfo( int iOfferC, bool bUpdateOnly=false ); + void UpdateTooltips(MARKETPLACE_CONTENTOFFER_INFO& xOffer); + bool UpdateDisplay(MARKETPLACE_CONTENTOFFER_INFO& xOffer); + + static int OrderSortFunction(const void* a, const void* b); + + bool m_bIgnorePress; + bool m_bDLCRequiredIsRetrieved; + DLC_INFO *m_pNoImageFor_DLC; + + typedef struct + { + unsigned int uiContentIndex; + unsigned int uiSortIndex; + } + SORTINDEXSTRUCT; + + vector m_vIconRetrieval; + bool m_bSelectionChanged; + +#endif + + bool m_bProductInfoShown; + int m_iProductInfoIndex; + int m_iCurrentDLC; + int m_iTotalDLC; + bool m_bAddAllDLCButtons; +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + std::vector*m_pvProductInfo; +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_DeathMenu.cpp b/Minecraft.Client/Common/UI/UIScene_DeathMenu.cpp new file mode 100644 index 0000000..2026bfc --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DeathMenu.cpp @@ -0,0 +1,191 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_DeathMenu.h" +#include "IUIScene_PauseMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" + +UIScene_DeathMenu::UIScene_DeathMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_buttonRespawn.init(app.GetString(IDS_RESPAWN),eControl_Respawn); + m_buttonExitGame.init(app.GetString(IDS_EXIT_GAME),eControl_ExitGame); + + m_labelTitle.setLabel(app.GetString(IDS_YOU_DIED)); + + m_bIgnoreInput = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft != NULL && pMinecraft->localgameModes[iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[iPad]; + + // This just allows it to be shown + gameMode->getTutorial()->showTutorialPopup(false); + } +} + +UIScene_DeathMenu::~UIScene_DeathMenu() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft != NULL && pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + + // This just allows it to be shown + gameMode->getTutorial()->showTutorialPopup(true); + } +} + +wstring UIScene_DeathMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"DeathMenuSplit"; + } + else + { + return L"DeathMenu"; + } +} + +void UIScene_DeathMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT); +} + +void UIScene_DeathMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + handled = true; + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +void UIScene_DeathMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Respawn: + m_bIgnoreInput = true; + app.SetAction(m_iPad,eAppAction_Respawn); +#ifdef _DURANGO + //InputManager.SetEnabledGtcButtons(_360_GTC_MENU|_360_GTC_PAUSE|_360_GTC_VIEW); +#endif + break; + case eControl_ExitGame: + { + Minecraft *pMinecraft=Minecraft::GetInstance(); + // 4J-PB - fix for #8333 - BLOCKER: If player decides to exit game, then cancels the exit player becomes stuck at game over screen + //m_bIgnoreInput = true; + // Check if it's the trial version + if(ProfileManager.IsFullVersion()) + { + + // is it the primary player exiting? + if(m_iPad==ProfileManager.GetPrimaryPad()) + { + UINT uiIDA[3]; + int playTime = -1; + if( pMinecraft->localplayers[m_iPad] != NULL ) + { + playTime = (int)pMinecraft->localplayers[m_iPad]->getSessionTimer(); + } + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Failed); + +#if defined (_XBOX_ONE) || defined(__ORBIS__) + if(g_NetworkManager.IsHost() && StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, m_iPad,&IUIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + +#else + if(StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else + { + if( g_NetworkManager.IsHost() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, m_iPad,&IUIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + } +#endif + } + else + { + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Failed); + + // just exit the player + app.SetAction(m_iPad,eAppAction_ExitPlayer); + } + } + else + { + // is it the primary player exiting? + if(m_iPad==ProfileManager.GetPrimaryPad()) + { + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Failed); + + // adjust the trial time played + ui.ReduceTrialTimerValue(); + + // exit the level + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), 0, 0, false); + } + else + { + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Failed); + + // just exit the player + app.SetAction(m_iPad,eAppAction_ExitPlayer); + } + } + } + break; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DeathMenu.h b/Minecraft.Client/Common/UI/UIScene_DeathMenu.h new file mode 100644 index 0000000..7285e41 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DeathMenu.h @@ -0,0 +1,44 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_DeathMenu : public UIScene +{ +private: + enum EControls + { + eControl_Respawn, + eControl_ExitGame + }; + + bool m_bIgnoreInput; + + UIControl_Button m_buttonRespawn, m_buttonExitGame; + UIControl_Label m_labelTitle; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonRespawn, "Respawn") + UI_MAP_ELEMENT( m_buttonExitGame, "ExitGame") + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_DeathMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_DeathMenu(); + + virtual EUIScene getSceneType() { return eUIScene_DeathMenu;} + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + +#ifdef _DURANGO + virtual long long getDefaultGtcButtons() { return 0; } +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp new file mode 100644 index 0000000..2a8ac9f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.cpp @@ -0,0 +1,216 @@ +#include "stdafx.h" + +#ifdef _DEBUG_MENUS_ENABLED +#include "UI.h" +#include "UIScene_DebugCreateSchematic.h" +#include "Minecraft.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" + +UIScene_DebugCreateSchematic::UIScene_DebugCreateSchematic(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelTitle.init(L"Name"); + m_labelStartX.init(L"StartX"); + m_labelStartY.init(L"StartY"); + m_labelStartZ.init(L"StartZ"); + m_labelEndX.init(L"EndX"); + m_labelEndY.init(L"EndY"); + m_labelEndZ.init(L"EndZ"); + + m_textInputStartX.init(L"",eControl_StartX); + m_textInputStartY.init(L"",eControl_StartY); + m_textInputStartZ.init(L"",eControl_StartZ); + m_textInputEndX.init(L"",eControl_EndX); + m_textInputEndY.init(L"",eControl_EndY); + m_textInputEndZ.init(L"",eControl_EndZ); + m_textInputName.init(L"",eControl_Name); + + m_checkboxSaveMobs.init(L"Save Mobs", eControl_SaveMobs,false); + m_checkboxUseCompression.init(L"Use Compression", eControl_UseCompression, false); + + m_buttonCreate.init(L"Create",eControl_Create); + + m_data = new ConsoleSchematicFile::XboxSchematicInitParam(); +} + +wstring UIScene_DebugCreateSchematic::getMoviePath() +{ + return L"DebugCreateSchematic"; +} + +void UIScene_DebugCreateSchematic::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_DebugCreateSchematic::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Create: + { + // We want the start to be even + if(m_data->startX > 0 && m_data->startX%2 != 0) + m_data->startX-=1; + else if(m_data->startX < 0 && m_data->startX%2 !=0) + m_data->startX-=1; + if(m_data->startY < 0) m_data->startY = 0; + else if(m_data->startY > 0 && m_data->startY%2 != 0) + m_data->startY-=1; + if(m_data->startZ > 0 && m_data->startZ%2 != 0) + m_data->startZ-=1; + else if(m_data->startZ < 0 && m_data->startZ%2 !=0) + m_data->startZ-=1; + + // We want the end to be odd to have a total size that is even + if(m_data->endX > 0 && m_data->endX%2 == 0) + m_data->endX+=1; + else if(m_data->endX < 0 && m_data->endX%2 ==0) + m_data->endX+=1; + if(m_data->endY > Level::maxBuildHeight) + m_data->endY = Level::maxBuildHeight; + else if(m_data->endY > 0 && m_data->endY%2 == 0) + m_data->endY+=1; + else if(m_data->endY < 0 && m_data->endY%2 ==0) + m_data->endY+=1; + if(m_data->endZ > 0 && m_data->endZ%2 == 0) + m_data->endZ+=1; + else if(m_data->endZ < 0 && m_data->endZ%2 ==0) + m_data->endZ+=1; + + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_ExportSchematic, (void *)m_data); + + navigateBack(); + } + break; + case eControl_Name: + case eControl_StartX: + case eControl_StartY: + case eControl_StartZ: + case eControl_EndX: + case eControl_EndY: + case eControl_EndZ: + m_keyboardCallbackControl = (eControls)((int)controlId); + InputManager.RequestKeyboard(L"Enter something",L"",(DWORD)0,25,&UIScene_DebugCreateSchematic::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); + break; + }; +} + +void UIScene_DebugCreateSchematic::handleCheckboxToggled(F64 controlId, bool selected) +{ + switch((int)controlId) + { + case eControl_SaveMobs: + m_data->bSaveMobs = selected; + break; + case eControl_UseCompression: + if (selected) + m_data->compressionType = APPROPRIATE_COMPRESSION_TYPE; + else + m_data->compressionType = Compression::eCompressionType_RLE; + break; + } +} + +int UIScene_DebugCreateSchematic::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) +{ + UIScene_DebugCreateSchematic *pClass=(UIScene_DebugCreateSchematic *)lpParam; + + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); + InputManager.GetText(pchText); + + if(pchText[0]!=0) + { + wstring value = (wchar_t *)pchText; + int iVal = 0; + if(!value.empty()) iVal = _fromString( value ); + switch(pClass->m_keyboardCallbackControl) + { + case eControl_Name: + pClass->m_textInputName.setLabel(value); + if(!value.empty()) + { + swprintf(pClass->m_data->name,64,L"%ls", value.c_str()); + } + else + { + swprintf(pClass->m_data->name,64,L"schematic"); + } + break; + case eControl_StartX: + pClass->m_textInputStartX.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->startX = iVal; + } + break; + case eControl_StartY: + pClass->m_textInputStartY.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->startY = iVal; + } + break; + case eControl_StartZ: + pClass->m_textInputStartZ.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->startZ = iVal; + } + break; + case eControl_EndX: + pClass->m_textInputEndX.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->endX = iVal; + } + break; + case eControl_EndY: + pClass->m_textInputEndY.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->endY = iVal; + } + break; + case eControl_EndZ: + pClass->m_textInputEndZ.setLabel(value); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + pClass->m_data->endZ = iVal; + } + break; + } + } + + return 0; +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h new file mode 100644 index 0000000..cbfe785 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugCreateSchematic.h @@ -0,0 +1,73 @@ +#pragma once +#ifdef _DEBUG_MENUS_ENABLED +#include "UIScene.h" +#include "..\..\Common\GameRules\ConsoleSchematicFile.h" + +class UIScene_DebugCreateSchematic : public UIScene +{ +private: + enum eControls + { + eControl_Name, + eControl_StartX, + eControl_StartY, + eControl_StartZ, + eControl_EndX, + eControl_EndY, + eControl_EndZ, + eControl_SaveMobs, + eControl_UseCompression, + eControl_Create, + }; + + eControls m_keyboardCallbackControl; + + ConsoleSchematicFile::XboxSchematicInitParam *m_data; + +public: + UIScene_DebugCreateSchematic(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_DebugCreateSchematic;} + +protected: + UIControl_TextInput m_textInputStartX, m_textInputStartY, m_textInputStartZ, m_textInputEndX, m_textInputEndY, m_textInputEndZ, m_textInputName; + UIControl_CheckBox m_checkboxSaveMobs, m_checkboxUseCompression; + UIControl_Button m_buttonCreate; + UIControl_Label m_labelStartX, m_labelStartY, m_labelStartZ, m_labelEndX, m_labelEndY, m_labelEndZ, m_labelTitle; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_textInputStartX, "StartX") + UI_MAP_ELEMENT( m_textInputStartY, "StartY") + UI_MAP_ELEMENT( m_textInputStartZ, "StartZ") + UI_MAP_ELEMENT( m_textInputEndX, "EndX") + UI_MAP_ELEMENT( m_textInputEndY, "EndY") + UI_MAP_ELEMENT( m_textInputEndZ, "EndZ") + UI_MAP_ELEMENT( m_textInputName, "Name") + + UI_MAP_ELEMENT( m_checkboxSaveMobs, "SaveMobs") + UI_MAP_ELEMENT( m_checkboxUseCompression, "UseCompression") + + UI_MAP_ELEMENT( m_buttonCreate, "Create") + + UI_MAP_ELEMENT( m_labelStartX, "LabelStartX") + UI_MAP_ELEMENT( m_labelStartY, "LabelStartY") + UI_MAP_ELEMENT( m_labelStartZ, "LabelStartZ") + UI_MAP_ELEMENT( m_labelEndX, "LabelEndX") + UI_MAP_ELEMENT( m_labelEndY, "LabelEndY") + UI_MAP_ELEMENT( m_labelEndZ, "LabelEndZ") + UI_MAP_ELEMENT( m_labelTitle, "LabelTitle") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + virtual void handleCheckboxToggled(F64 controlId, bool selected); + +private: + static int KeyboardCompleteCallback(LPVOID lpParam,const bool bRes); +}; +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DebugOptions.cpp b/Minecraft.Client/Common/UI/UIScene_DebugOptions.cpp new file mode 100644 index 0000000..658a851 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugOptions.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_DebugOptions.h" + +LPCWSTR UIScene_DebugOptionsMenu::m_DebugCheckboxTextA[eDebugSetting_Max+1]= +{ + L"Load Saves From Local Folder Mode", + L"Write Saves To Local Folder Mode", + L"Freeze Players", //L"Not Used", + L"Display Safe Area", + L"Mobs don't attack", + L"Freeze Time", + L"Disable Weather", + L"Craft Anything", + L"Use DPad for debug", + L"Mobs don't tick", + L"Instant Mine", + L"Show UI Console", + L"Distributable Save", + L"Debug Leaderboards", + L"Height-Water-Biome Maps", + L"Superflat Nether", + //L"Light/Dark background", + L"More lightning when thundering", + L"Go To Nether", + //L"Go To End", + L"Go To Overworld", + L"Unlock All DLC", //L"Toggle Font", + L"Show Marketing Guide", +}; + +UIScene_DebugOptionsMenu::UIScene_DebugOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + unsigned int uiDebugBitmask=app.GetGameSettingsDebugMask(iPad); + + IggyValuePath *root = IggyPlayerRootPath ( getMovie() ); + for(m_iTotalCheckboxElements = 0; m_iTotalCheckboxElements < eDebugSetting_Max && m_iTotalCheckboxElements < 21; ++m_iTotalCheckboxElements) + { + wstring label(m_DebugCheckboxTextA[m_iTotalCheckboxElements]); + m_checkboxes[m_iTotalCheckboxElements].init(label,m_iTotalCheckboxElements,(uiDebugBitmask&(1<gameRenderer->GetFovVal()); + m_sliderFov.init(TempString,eControl_FOV,0,100,(int)pMinecraft->gameRenderer->GetFovVal()); + + float currentTime = pMinecraft->level->getLevelData()->getTime() % 24000; + swprintf( (WCHAR *)TempString, 256, L"Set time (unsafe) (%d)", (int)currentTime); + m_sliderTime.init(TempString,eControl_Time,0,240,currentTime/100); + + m_buttonRain.init(L"Toggle Rain",eControl_Rain); + m_buttonThunder.init(L"Toggle Thunder",eControl_Thunder); + m_buttonSchematic.init(L"Create Schematic",eControl_Schematic); + m_buttonResetTutorial.init(L"Reset profile tutorial progress",eControl_ResetTutorial); + m_buttonSetCamera.init(L"Set camera",eControl_SetCamera); + m_buttonSetDay.init(L"Set Day", eControl_SetDay); + m_buttonSetNight.init(L"Set Night", eControl_SetNight); + + m_buttonListItems.init(eControl_Items); + + int listId = 0; + for(unsigned int i = 0; i < Item::items.length; ++i) + { + if(Item::items[i] != NULL) + { + m_itemIds.push_back(i); + m_buttonListItems.addItem(app.GetString(Item::items[i]->getDescriptionId()), listId); + ++listId; + } + } + + m_buttonListEnchantments.init(eControl_Enchantments); + + for(unsigned int i = 0; i < Enchantment::validEnchantments.size(); ++i ) + { + Enchantment *ench = Enchantment::validEnchantments.at(i); + + for(unsigned int level = ench->getMinLevel(); level <= ench->getMaxLevel(); ++level) + { + m_enchantmentIdAndLevels.push_back(pair(ench->id,level)); + m_buttonListEnchantments.addItem(app.GetString( ench->getDescriptionId() ) + _toString(level) ); + } + } + + m_buttonListMobs.init(eControl_Mobs); + m_buttonListMobs.addItem( L"Chicken" ); + m_mobFactories.push_back(eTYPE_CHICKEN); + m_buttonListMobs.addItem( L"Cow" ); + m_mobFactories.push_back(eTYPE_COW); + m_buttonListMobs.addItem( L"Pig" ); + m_mobFactories.push_back(eTYPE_PIG); + m_buttonListMobs.addItem( L"Sheep" ); + m_mobFactories.push_back(eTYPE_SHEEP); + m_buttonListMobs.addItem( L"Squid" ); + m_mobFactories.push_back(eTYPE_SQUID); + m_buttonListMobs.addItem( L"Wolf" ); + m_mobFactories.push_back(eTYPE_WOLF); + m_buttonListMobs.addItem( L"Creeper" ); + m_mobFactories.push_back(eTYPE_CREEPER); + m_buttonListMobs.addItem( L"Ghast" ); + m_mobFactories.push_back(eTYPE_GHAST); + m_buttonListMobs.addItem( L"Pig Zombie" ); + m_mobFactories.push_back(eTYPE_PIGZOMBIE); + m_buttonListMobs.addItem( L"Skeleton" ); + m_mobFactories.push_back(eTYPE_SKELETON); + m_buttonListMobs.addItem( L"Slime" ); + m_mobFactories.push_back(eTYPE_SLIME); + m_buttonListMobs.addItem( L"Spider" ); + m_mobFactories.push_back(eTYPE_SPIDER); + m_buttonListMobs.addItem( L"Zombie" ); + m_mobFactories.push_back(eTYPE_ZOMBIE); + m_buttonListMobs.addItem( L"Enderman" ); + m_mobFactories.push_back(eTYPE_ENDERMAN); + m_buttonListMobs.addItem( L"Silverfish" ); + m_mobFactories.push_back(eTYPE_SILVERFISH); + m_buttonListMobs.addItem( L"Cave Spider" ); + m_mobFactories.push_back(eTYPE_CAVESPIDER); + m_buttonListMobs.addItem( L"Mooshroom" ); + m_mobFactories.push_back(eTYPE_MUSHROOMCOW); + m_buttonListMobs.addItem( L"Snow Golem" ); + m_mobFactories.push_back(eTYPE_SNOWMAN); + m_buttonListMobs.addItem( L"Ender Dragon" ); + m_mobFactories.push_back(eTYPE_ENDERDRAGON); + m_buttonListMobs.addItem( L"Blaze" ); + m_mobFactories.push_back(eTYPE_BLAZE); + m_buttonListMobs.addItem( L"Magma Cube" ); + m_mobFactories.push_back(eTYPE_LAVASLIME); +} + +wstring UIScene_DebugOverlay::getMoviePath() +{ + return L"DebugMenu"; +} + +void UIScene_DebugOverlay::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + int itemId = -1; + swscanf((wchar_t*)region->name,L"item_%d",&itemId); + if (itemId == -1 || itemId > Item::ITEM_NUM_COUNT || Item::items[itemId] == NULL) + { + app.DebugPrintf("This is not the control we are looking for\n"); + } + else + { + shared_ptr item = shared_ptr( new ItemInstance(itemId,1,0) ); + if(item != NULL) customDrawSlotControl(region,m_iPad,item,1.0f,false,false); + } +} + +void UIScene_DebugOverlay::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + } +} + +void UIScene_DebugOverlay::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Items: + { + app.DebugPrintf("UIScene_DebugOverlay::handlePress for itemsList: %f\n", childId); + int id = childId; + //app.SetXuiServerAction(m_iPad, eXuiServerAction_DropItem, (void *)m_itemIds[id]); + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( GiveItemCommand::preparePacket(dynamic_pointer_cast(Minecraft::GetInstance()->localplayers[ProfileManager.GetPrimaryPad()]), m_itemIds[id]) ); + } + break; + case eControl_Mobs: + { + int id = childId; + if(idgetConnection(ProfileManager.GetPrimaryPad()); + conn->send( EnchantItemCommand::preparePacket(dynamic_pointer_cast(Minecraft::GetInstance()->localplayers[ProfileManager.GetPrimaryPad()]), m_enchantmentIdAndLevels[id].first, m_enchantmentIdAndLevels[id].second) ); + } + break; + case eControl_Schematic: + { +#ifndef _CONTENT_PACKAGE + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DebugCreateSchematic,NULL,eUILayer_Debug); +#endif + } + break; + case eControl_SetCamera: + { +#ifndef _CONTENT_PACKAGE + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DebugSetCamera,NULL,eUILayer_Debug); +#endif + } + break; + case eControl_Rain: + { + //app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_ToggleRain); + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( ToggleDownfallCommand::preparePacket() ); + } + break; + case eControl_Thunder: + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_ToggleThunder); + break; + case eControl_ResetTutorial: + Tutorial::debugResetPlayerSavedProgress( ProfileManager.GetPrimaryPad() ); + break; + case eControl_SetDay: + { + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( TimeCommand::preparePacket(false) ); + } + break; + case eControl_SetNight: + { + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( TimeCommand::preparePacket(true) ); + } + break; + }; +} + +void UIScene_DebugOverlay::handleSliderMove(F64 sliderId, F64 currentValue) +{ + switch((int)sliderId) + { + case eControl_Time: + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // Need to set the time on both levels to stop the flickering as the local level + // tries to predict the time + // Only works if we are on the host machine, but shouldn't break if not + MinecraftServer::SetTime(currentValue * 100); + pMinecraft->level->getLevelData()->setTime(currentValue * 100); + + WCHAR TempString[256]; + float currentTime = currentValue * 100; + swprintf( (WCHAR *)TempString, 256, L"Set time (unsafe) (%d)", (int)currentTime); + m_sliderTime.setLabel(TempString); + } + break; + case eControl_FOV: + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->gameRenderer->SetFovVal((float)currentValue); + + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"Set fov (%d)", (int)currentValue); + m_sliderFov.setLabel(TempString); + } + break; + }; +} +#endif diff --git a/Minecraft.Client/Common/UI/UIScene_DebugOverlay.h b/Minecraft.Client/Common/UI/UIScene_DebugOverlay.h new file mode 100644 index 0000000..9a0e1cd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugOverlay.h @@ -0,0 +1,65 @@ +#pragma once +#ifdef _DEBUG_MENUS_ENABLED +#include "UIScene.h" +#include "UIControl_ButtonList.h" + +class UIScene_DebugOverlay : public UIScene +{ +private: + enum eControls + { + eControl_SetCamera, + eControl_ResetTutorial, + eControl_Schematic, + eControl_Thunder, + eControl_Rain, + eControl_FOV, + eControl_SetDay, + eControl_SetNight, + eControl_Time, + eControl_Mobs, + eControl_Enchantments, + eControl_Items, + }; + + vector m_itemIds; + vector m_mobFactories; + vector< pair > m_enchantmentIdAndLevels; +public: + UIScene_DebugOverlay(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_DebugOverlay;} + +protected: + UIControl_ButtonList m_buttonListItems, m_buttonListMobs, m_buttonListEnchantments; + UIControl_Slider m_sliderFov, m_sliderTime; + UIControl_Button m_buttonRain, m_buttonThunder, m_buttonSchematic, m_buttonResetTutorial, m_buttonSetCamera, m_buttonSetDay, m_buttonSetNight; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListItems, "itemsList") + UI_MAP_ELEMENT( m_buttonListEnchantments, "enchantmentsList") + UI_MAP_ELEMENT( m_buttonListMobs, "mobList") + UI_MAP_ELEMENT( m_sliderFov, "fov") + UI_MAP_ELEMENT( m_sliderTime, "time") + UI_MAP_ELEMENT( m_buttonSetDay, "setDay") + UI_MAP_ELEMENT( m_buttonSetNight, "setNight") + UI_MAP_ELEMENT( m_buttonRain, "rain") + UI_MAP_ELEMENT( m_buttonThunder, "thunder") + UI_MAP_ELEMENT( m_buttonSchematic, "schematic") + UI_MAP_ELEMENT( m_buttonResetTutorial, "resetTutorial") + UI_MAP_ELEMENT( m_buttonSetCamera, "setCamera") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + +public: + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp new file mode 100644 index 0000000..dd5a429 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.cpp @@ -0,0 +1,158 @@ +#include "stdafx.h" + +#ifdef _DEBUG_MENUS_ENABLED +#include "UI.h" +#include "UIScene_DebugSetCamera.h" +#include "Minecraft.h" +#include "MultiPlayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIScene_DebugSetCamera::UIScene_DebugSetCamera(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + int playerNo = 0; + currentPosition = new DebugSetCameraPosition(); + currentPosition->player = playerNo; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (pMinecraft != NULL) + { + Vec3 *vec = pMinecraft->localplayers[playerNo]->getPos(1.0); + + currentPosition->m_camX = vec->x; + currentPosition->m_camY = vec->y - 1.62;// pMinecraft->localplayers[playerNo]->getHeadHeight(); + currentPosition->m_camZ = vec->z; + + currentPosition->m_yRot = pMinecraft->localplayers[playerNo]->yRot; + currentPosition->m_elev = pMinecraft->localplayers[playerNo]->xRot; + } + + WCHAR TempString[256]; + + swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camX); + m_textInputX.init(TempString, eControl_CamX); + + swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camY); + m_textInputY.init(TempString, eControl_CamY); + + swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_camZ); + m_textInputZ.init(TempString, eControl_CamZ); + + swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_yRot); + m_textInputYRot.init(TempString, eControl_YRot); + + swprintf( (WCHAR *)TempString, 256, L"%f", currentPosition->m_elev); + m_textInputElevation.init(TempString, eControl_Elevation); + + m_checkboxLockPlayer.init(L"Lock Player", eControl_LockPlayer, app.GetFreezePlayers()); + + m_buttonTeleport.init(L"Teleport", eControl_Teleport); + + m_labelTitle.init(L"Set Camera Position"); + m_labelCamX.init(L"CamX"); + m_labelCamY.init(L"CamY"); + m_labelCamZ.init(L"CamZ"); + m_labelYRotElev.init(L"Y-Rot & Elevation (Degs)"); +} + +wstring UIScene_DebugSetCamera::getMoviePath() +{ + return L"DebugSetCamera"; +} + +void UIScene_DebugSetCamera::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_DebugSetCamera::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Teleport: + app.SetXuiServerAction( ProfileManager.GetPrimaryPad(), + eXuiServerAction_SetCameraLocation, + (void *)currentPosition); + break; + case eControl_CamX: + case eControl_CamY: + case eControl_CamZ: + case eControl_YRot: + case eControl_Elevation: + m_keyboardCallbackControl = (eControls)((int)controlId); + InputManager.RequestKeyboard(L"Enter something",L"",(DWORD)0,25,&UIScene_DebugSetCamera::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Default); + break; + }; +} + +void UIScene_DebugSetCamera::handleCheckboxToggled(F64 controlId, bool selected) +{ + switch((int)controlId) + { + case eControl_LockPlayer: + app.SetFreezePlayers(selected); + break; + } +} + +int UIScene_DebugSetCamera::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) +{ + UIScene_DebugSetCamera *pClass=(UIScene_DebugSetCamera *)lpParam; + uint16_t pchText[2048];//[128]; + ZeroMemory(pchText, 2048/*128*/ * sizeof(uint16_t) ); + InputManager.GetText(pchText); + + if(pchText[0]!=0) + { + wstring value = (wchar_t *)pchText; + double val = 0; + if(!value.empty()) val = _fromString( value ); + switch(pClass->m_keyboardCallbackControl) + { + case eControl_CamX: + pClass->m_textInputX.setLabel(value); + pClass->currentPosition->m_camX = val; + break; + case eControl_CamY: + pClass->m_textInputY.setLabel(value); + pClass->currentPosition->m_camY = val; + break; + case eControl_CamZ: + pClass->m_textInputZ.setLabel(value); + pClass->currentPosition->m_camZ = val; + break; + case eControl_YRot: + pClass->m_textInputYRot.setLabel(value); + pClass->currentPosition->m_yRot = val; + break; + case eControl_Elevation: + pClass->m_textInputElevation.setLabel(value); + pClass->currentPosition->m_elev = val; + break; + } + } + + return 0; +} +#endif diff --git a/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h new file mode 100644 index 0000000..38db125 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DebugSetCamera.h @@ -0,0 +1,69 @@ +#pragma once +#ifdef _DEBUG_MENUS_ENABLED +#include "UIScene.h" + +class UIScene_DebugSetCamera : public UIScene +{ +private: + enum eControls + { + eControl_CamX, + eControl_CamY, + eControl_CamZ, + eControl_YRot, + eControl_Elevation, + eControl_LockPlayer, + eControl_Teleport, + }; + + typedef struct _FreezePlayerParam + { + int player; + bool freeze; + } FreezePlayerParam; + + DebugSetCameraPosition *currentPosition; + FreezePlayerParam *fpp; + + eControls m_keyboardCallbackControl; + +public: + UIScene_DebugSetCamera(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_DebugSetCamera;} + +protected: + UIControl_TextInput m_textInputX, m_textInputY, m_textInputZ, m_textInputYRot, m_textInputElevation; + UIControl_CheckBox m_checkboxLockPlayer; + UIControl_Button m_buttonTeleport; + UIControl_Label m_labelTitle, m_labelCamX, m_labelCamY, m_labelCamZ, m_labelYRotElev; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_textInputX, "CamX") + UI_MAP_ELEMENT( m_textInputY, "CamY") + UI_MAP_ELEMENT( m_textInputZ, "CamZ") + UI_MAP_ELEMENT( m_textInputYRot, "YRot") + UI_MAP_ELEMENT( m_textInputElevation, "Elevation") + UI_MAP_ELEMENT( m_checkboxLockPlayer, "LockPlayer") + UI_MAP_ELEMENT( m_buttonTeleport, "Teleport") + + UI_MAP_ELEMENT( m_labelTitle, "LabelTitle") + UI_MAP_ELEMENT( m_labelCamX, "LabelCamX") + UI_MAP_ELEMENT( m_labelCamY, "LabelCamY") + UI_MAP_ELEMENT( m_labelCamZ, "LabelCamZ") + UI_MAP_ELEMENT( m_labelYRotElev, "LabelYRotElev") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + virtual void handleCheckboxToggled(F64 controlId, bool selected); + +private: + static int KeyboardCompleteCallback(LPVOID lpParam,const bool bRes); +}; +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DispenserMenu.cpp b/Minecraft.Client/Common/UI/UIScene_DispenserMenu.cpp new file mode 100644 index 0000000..1eda166 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DispenserMenu.cpp @@ -0,0 +1,197 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\Minecraft.h" +#include "UIScene_DispenserMenu.h" + +UIScene_DispenserMenu::UIScene_DispenserMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelDispenser.init(app.GetString(IDS_DISPENSER)); + + TrapScreenInput *initData = (TrapScreenInput *)_initData; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Trap_Menu, this); + } + + TrapMenu* menu = new TrapMenu( initData->inventory, initData->trap ); + + m_containerSize = initData->trap->getContainerSize(); + Initialize( initData->iPad, menu, true, m_containerSize, eSectionTrapUsing, eSectionTrapMax ); + + m_slotListTrap.addSlots(0, 9); + + delete initData; +} + +wstring UIScene_DispenserMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"DispenserMenuSplit"; + } + else + { + return L"DispenserMenu"; + } +} + +void UIScene_DispenserMenu::handleReload() +{ + Initialize( m_iPad, m_menu, true, m_containerSize, eSectionTrapUsing, eSectionTrapMax ); + + m_slotListTrap.addSlots(0, 9); +} + +int UIScene_DispenserMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionTrapTrap: + cols = 3; + break; + case eSectionTrapInventory: + cols = 9; + break; + case eSectionTrapUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_DispenserMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionTrapTrap: + rows = 3; + break; + case eSectionTrapInventory: + rows = 3; + break; + case eSectionTrapUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_DispenserMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionTrapTrap: + pPosition->x = m_slotListTrap.getXPos(); + pPosition->y = m_slotListTrap.getYPos(); + break; + case eSectionTrapInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionTrapUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_DispenserMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + switch( eSection ) + { + case eSectionTrapTrap: + sectionSize.x = m_slotListTrap.getWidth(); + sectionSize.y = m_slotListTrap.getHeight(); + break; + case eSectionTrapInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionTrapUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; +} + +void UIScene_DispenserMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionTrapTrap: + slotList = &m_slotListTrap; + break; + case eSectionTrapInventory: + slotList = &m_slotListInventory; + break; + case eSectionTrapUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_DispenserMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionTrapTrap: + control = &m_slotListTrap; + break; + case eSectionTrapInventory: + control = &m_slotListInventory; + break; + case eSectionTrapUsing: + control = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + return control; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_DispenserMenu.h b/Minecraft.Client/Common/UI/UIScene_DispenserMenu.h new file mode 100644 index 0000000..6661c7a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_DispenserMenu.h @@ -0,0 +1,40 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_DispenserMenu.h" + +class InventoryMenu; + +class UIScene_DispenserMenu : public UIScene_AbstractContainerMenu, public IUIScene_DispenserMenu +{ +private: + int m_containerSize; + +public: + UIScene_DispenserMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_DispenserMenu;} + +protected: + UIControl_SlotList m_slotListTrap; + UIControl_Label m_labelDispenser; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListTrap, "Trap") + UI_MAP_ELEMENT( m_labelDispenser, "dispenserLabel") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_EULA.cpp b/Minecraft.Client/Common/UI/UIScene_EULA.cpp new file mode 100644 index 0000000..3177344 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EULA.cpp @@ -0,0 +1,145 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_EULA.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIScene_EULA::UIScene_EULA(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + parentLayer->addComponent(iPad,eUIComponent_Panorama); + parentLayer->addComponent(iPad,eUIComponent_Logo); + + m_buttonConfirm.init(app.GetString(IDS_TOOLTIPS_ACCEPT),eControl_Confirm); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + wstring EULA = app.GetString(IDS_EULA); + EULA.append(L"\r\n"); + +#if defined(__PS3__) + if(app.IsEuropeanSKU()) + { + EULA.append(app.GetString(IDS_EULA_SCEE)); + // if it's the BD build + if(StorageManager.GetBootTypeDisc()) + { + EULA.append(app.GetString(IDS_EULA_SCEE_BD)); + } + } + else if(app.IsAmericanSKU()) + { + EULA.append(app.GetString(IDS_EULA_SCEA)); + } +#elif defined __ORBIS__ + if(app.IsEuropeanSKU()) + { + EULA.append(app.GetString(IDS_EULA_SCEE)); + // 4J-PB - we can't tell if it's a disc or digital version, so let's show this anyway + EULA.append(app.GetString(IDS_EULA_SCEE_BD)); + } + else if(app.IsAmericanSKU()) + { + EULA.append(app.GetString(IDS_EULA_SCEA)); + } +#endif +#else + wstring EULA = L""; +#endif + + vector paragraphs; + int lastIndex = 0; + for ( int index = EULA.find(L"\r\n", lastIndex, 2); + index != wstring::npos; + index = EULA.find(L"\r\n", lastIndex, 2) + ) + { + paragraphs.push_back( EULA.substr(lastIndex, index-lastIndex) + L" " ); + lastIndex = index + 2; + } + paragraphs.push_back( EULA.substr( lastIndex, EULA.length() - lastIndex ) ); + + for(unsigned int i = 0; i < paragraphs.size(); ++i) + { + m_labelDescription.addText(paragraphs[i],i == (paragraphs.size() - 1) ); + } + + // 4J-PB - If we have a signed in user connected, let's get the DLC now + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( (InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i)) ) + { + if(!app.DLCInstallProcessCompleted() && !app.DLCInstallPending()) + { + app.StartInstallDLCProcess(i); + break; + } + } + } + + m_bIgnoreInput=false; + + //ui.setFontCachingCalculationBuffer(20000); + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} + +UIScene_EULA::~UIScene_EULA() +{ + m_parentLayer->removeComponent(eUIComponent_Panorama); + m_parentLayer->removeComponent(eUIComponent_Logo); +} + +wstring UIScene_EULA::getMoviePath() +{ + return L"EULA"; +} + +void UIScene_EULA::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT ); +} + +void UIScene_EULA::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + +#ifdef __ORBIS__ + // ignore all players except player 0 - it's their profile that is currently being used + if(iPad!=0) return; +#endif + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_OK: + case ACTION_MENU_DOWN: + case ACTION_MENU_UP: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_OTHER_STICK_DOWN: + case ACTION_MENU_OTHER_STICK_UP: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_EULA::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Confirm: + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + app.SetGameSettings(0,eGameSetting_PS3_EULA_Read,1); + ui.NavigateToScene(0,eUIScene_SaveMessage); + ui.setFontCachingCalculationBuffer(-1); + break; + }; +} diff --git a/Minecraft.Client/Common/UI/UIScene_EULA.h b/Minecraft.Client/Common/UI/UIScene_EULA.h new file mode 100644 index 0000000..4715b11 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EULA.h @@ -0,0 +1,45 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_EULA : public UIScene +{ +private: + enum EControls + { + eControl_Confirm, + }; + + bool m_bIgnoreInput; + + UIControl_Button m_buttonConfirm; + UIControl_DynamicLabel m_labelDescription; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_buttonConfirm, "AcceptButton") + UI_MAP_ELEMENT(m_labelDescription, "EULAtext") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_EULA(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_EULA(); + + virtual EUIScene getSceneType() { return eUIScene_EULA;} + + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + virtual void updateTooltips(); + +protected: + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + + virtual long long getDefaultGtcButtons() { return 0; } +}; diff --git a/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.cpp b/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.cpp new file mode 100644 index 0000000..8a4f1c6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.cpp @@ -0,0 +1,284 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\Minecraft.h" +#include "UIScene_EnchantingMenu.h" + +UIScene_EnchantingMenu::UIScene_EnchantingMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelEnchant.init(app.GetString(IDS_ENCHANT)); + + m_enchantButton[0].init(0); + m_enchantButton[1].init(1); + m_enchantButton[2].init(2); + + EnchantingScreenInput *initData = (EnchantingScreenInput *)_initData; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Enchanting_Menu, this); + } + + EnchantmentMenu *menu = new EnchantmentMenu(initData->inventory, initData->level, initData->x, initData->y, initData->z); + + Initialize( initData->iPad, menu, true, EnchantmentMenu::INV_SLOT_START, eSectionEnchantUsing, eSectionEnchantMax ); + + m_slotListIngredient.addSlots(EnchantmentMenu::INGREDIENT_SLOT, 1); + + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_ENCHANTING); + + delete initData; +} + +wstring UIScene_EnchantingMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"EnchantingMenuSplit"; + } + else + { + return L"EnchantingMenu"; + } +} + +void UIScene_EnchantingMenu::handleReload() +{ + Initialize( m_iPad, m_menu, true, EnchantmentMenu::INV_SLOT_START, eSectionEnchantUsing, eSectionEnchantMax ); + + m_slotListIngredient.addSlots(EnchantmentMenu::INGREDIENT_SLOT, 1); +} + +int UIScene_EnchantingMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionEnchantSlot: + cols = 1; + break; + case eSectionEnchantInventory: + cols = 9; + break; + case eSectionEnchantUsing: + cols = 9; + break; + default: + assert( false ); + break; + }; + return cols; +} + +int UIScene_EnchantingMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionEnchantSlot: + rows = 1; + break; + case eSectionEnchantInventory: + rows = 3; + break; + case eSectionEnchantUsing: + rows = 1; + break; + default: + assert( false ); + break; + }; + return rows; +} + +void UIScene_EnchantingMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionEnchantSlot: + pPosition->x = m_slotListIngredient.getXPos(); + pPosition->y = m_slotListIngredient.getYPos(); + break; + case eSectionEnchantInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionEnchantUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + case eSectionEnchantButton1: + pPosition->x = m_enchantButton[0].getXPos(); + pPosition->y = m_enchantButton[0].getYPos(); + break; + case eSectionEnchantButton2: + pPosition->x = m_enchantButton[1].getXPos(); + pPosition->y = m_enchantButton[1].getYPos(); + break; + case eSectionEnchantButton3: + pPosition->x = m_enchantButton[2].getXPos(); + pPosition->y = m_enchantButton[2].getYPos(); + break; + default: + assert( false ); + break; + }; +} + +void UIScene_EnchantingMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + switch( eSection ) + { + case eSectionEnchantSlot: + sectionSize.x = m_slotListIngredient.getWidth(); + sectionSize.y = m_slotListIngredient.getHeight(); + break; + case eSectionEnchantInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionEnchantUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + case eSectionEnchantButton1: + sectionSize.x = m_enchantButton[0].getWidth(); + sectionSize.y = m_enchantButton[0].getHeight(); + break; + case eSectionEnchantButton2: + sectionSize.x = m_enchantButton[1].getWidth(); + sectionSize.y = m_enchantButton[1].getHeight(); + break; + case eSectionEnchantButton3: + sectionSize.x = m_enchantButton[2].getWidth(); + sectionSize.y = m_enchantButton[2].getHeight(); + break; + default: + assert( false ); + break; + }; + + if(IsSectionSlotList(eSection)) + { + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; + } + else + { + GetPositionOfSection(eSection, pPosition); + pSize->x = sectionSize.x; + pSize->y = sectionSize.y; + } +} + +void UIScene_EnchantingMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionEnchantSlot: + slotList = &m_slotListIngredient; + break; + case eSectionEnchantInventory: + slotList = &m_slotListInventory; + break; + case eSectionEnchantUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + }; + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_EnchantingMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionEnchantSlot: + control = &m_slotListIngredient; + break; + case eSectionEnchantInventory: + control = &m_slotListInventory; + break; + case eSectionEnchantUsing: + control = &m_slotListHotbar; + break; + case eSectionEnchantButton1: + control = &m_enchantButton[0]; + break; + case eSectionEnchantButton2: + control = &m_enchantButton[1]; + break; + case eSectionEnchantButton3: + control = &m_enchantButton[2]; + break; + default: + assert( false ); + break; + }; + return control; +} + +void UIScene_EnchantingMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + + if(wcscmp((wchar_t *)region->name,L"EnchantmentBook")==0) + { + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + delete customDrawRegion; + + m_enchantBook.render(region); + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + } + else + { + int slotId = -1; + swscanf((wchar_t*)region->name,L"slot_Button%d",&slotId); + if(slotId >= 0) + { + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + delete customDrawRegion; + + m_enchantButton[slotId-1].render(region); + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + } + else + { + UIScene_AbstractContainerMenu::customDraw(region); + } + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.h b/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.h new file mode 100644 index 0000000..89ccd12 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EnchantingMenu.h @@ -0,0 +1,54 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_EnchantingMenu.h" + +class InventoryMenu; + +class UIScene_EnchantingMenu : public UIScene_AbstractContainerMenu, public IUIScene_EnchantingMenu +{ +private: + enum EControls + { + eControl_UNKNOWN, + eControl_Button1, + eControl_Button2, + eControl_Button3, + }; +public: + UIScene_EnchantingMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_EnchantingMenu;} + +protected: + UIControl_SlotList m_slotListIngredient; + UIControl_Label m_labelEnchant; + UIControl_EnchantmentButton m_enchantButton[3]; + UIControl_EnchantmentBook m_enchantBook; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListIngredient, "ingredient") + UI_MAP_ELEMENT( m_enchantButton[0], "Button1") + UI_MAP_ELEMENT( m_enchantButton[1], "Button2") + UI_MAP_ELEMENT( m_enchantButton[2], "Button3") + UI_MAP_ELEMENT( m_labelEnchant, "enchantLabel") + + UI_MAP_ELEMENT( m_enchantBook, "iggy_EnchantmentBook") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_EndPoem.cpp b/Minecraft.Client/Common/UI/UIScene_EndPoem.cpp new file mode 100644 index 0000000..f825efd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EndPoem.cpp @@ -0,0 +1,271 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_EndPoem.h" +#include "UIBitmapFont.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIScene_EndPoem::UIScene_EndPoem(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + + //ui.setFontCachingCalculationBuffer(20000); + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bIgnoreInput = false; + + // 4J Stu - Don't need these, the AS handles the scrolling and makes it look nice +#if 0 + wstring halfScreenLineBreaks; + + if(RenderManager.IsHiDef()) + { + // HD - 17 line page + halfScreenLineBreaks = L"










"; + } + else + { + // 480 - 14 line page + halfScreenLineBreaks = L"







"; + } +#endif + + //wchar_t startTags[64]; + //swprintf(startTags,64,L"",app.GetHTMLFontSize(eHTMLSize_EndPoem)); + //noNoiseString.append(halfScreenLineBreaks); + //noNoiseString.append(halfScreenLineBreaks); + noNoiseString.append( app.GetString(IDS_WIN_TEXT) ); + noNoiseString.append( app.GetString(IDS_WIN_TEXT_PART_2) ); + noNoiseString.append( app.GetString(IDS_WIN_TEXT_PART_3) ); + + //noNoiseString.append(halfScreenLineBreaks); + + // 4J Stu - Iggy seems to strip our trailing linebreaks, so added a space to made sure it scrolls this far + noNoiseString.append( L" " ); + + noNoiseString = app.FormatHTMLString(m_iPad, noNoiseString, 0xff000000); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + wstring playerName = L""; + if(pMinecraft->localplayers[ui.GetWinUserIndex()] != NULL) + { + playerName = escapeXML( pMinecraft->localplayers[ui.GetWinUserIndex()]->getDisplayName() ); + } + else + { + playerName = escapeXML( pMinecraft->localplayers[ProfileManager.GetPrimaryPad()]->getDisplayName() ); + } + noNoiseString = replaceAll(noNoiseString,L"{*PLAYER*}",playerName); + + Random random(8124371); + int found=(int)noNoiseString.find(L"{*NOISE*}"); + int length; + while (found!=string::npos) + { + length = random.nextInt(4) + 3; + m_noiseLengths.push_back(length); + found=(int)noNoiseString.find(L"{*NOISE*}",found+1); + } + + updateNoise(); + + + // 4J-JEV: Find paragraph start and end points. + m_paragraphs = vector(); + int lastIndex = 0; + for ( int index = 0; + index != wstring::npos; + index = noiseString.find(L"

", index+12, 12) + ) + { + m_paragraphs.push_back( noiseString.substr(lastIndex, index-lastIndex) ); + lastIndex = index; + } + //lastIndex += 12; + m_paragraphs.push_back( noiseString.substr( lastIndex, noiseString.length() - lastIndex ) ); + + //m_htmlPoem.init(noiseString.c_str()); + //m_htmlPoem.startAutoScroll(); + + //wstring result = m_htmlControl.GetText(); + + //wcout << result.c_str(); + +#if TO_BE_IMPLEMENTED + m_scrollDir = 1; + HRESULT hr = XuiHtmlControlSetSmoothScroll(m_htmlControl.m_hObj, XUI_SMOOTHSCROLL_VERTICAL,TRUE,AUTO_SCROLL_SPEED,1.0f,AUTO_SCROLL_SPEED); + XuiHtmlControlVScrollBy(m_htmlControl.m_hObj,m_scrollDir * 1000); + + SetTimer(0,200); +#endif + + m_requestedLabel = 0; +} + +wstring UIScene_EndPoem::getMoviePath() +{ + return L"EndPoem"; +} + +void UIScene_EndPoem::updateTooltips() +{ + ui.SetTooltips( XUSER_INDEX_ANY, -1, m_bIgnoreInput?-1:IDS_TOOLTIPS_CONTINUE); +} + +void UIScene_EndPoem::tick() +{ + UIScene::tick(); + + if( m_requestedLabel >= 0 && m_requestedLabel < m_paragraphs.size()) + { + wstring label = m_paragraphs[m_requestedLabel]; + + IggyDataValue result; + IggyDataValue value[3]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = m_requestedLabel; + + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = (m_requestedLabel == (m_paragraphs.size() - 1)); + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetNextLabel , 3 , value ); + + m_requestedLabel = -1; + } +} + +void UIScene_EndPoem::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + if(pressed) ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + m_bIgnoreInput = true; + Minecraft *pMinecraft = Minecraft::GetInstance(); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(pMinecraft->localplayers[i] != NULL) + { + app.SetAction(i,eAppAction_Respawn); + } + } + + // This just allows it to be shown + if(pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(true); + + updateTooltips(); + navigateBack(); + + handled = true; + } + break; + case ACTION_MENU_DOWN: + case ACTION_MENU_UP: + case ACTION_MENU_OTHER_STICK_DOWN: + case ACTION_MENU_OTHER_STICK_UP: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_EndPoem::handleDestroy() +{ + + //ui.setFontCachingCalculationBuffer(-1); +} + +void UIScene_EndPoem::handleRequestMoreData(F64 startIndex, bool up) +{ + m_requestedLabel = (int)startIndex; +} + +void UIScene_EndPoem::updateNoise() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + noiseString = noNoiseString; + + int length = 0; + wchar_t replacements[64]; + wstring replaceString = L""; + wchar_t randomChar = L'a'; + Random *random = pMinecraft->font->random; + + bool darken = false; + + wstring tag = L"{*NOISE*}"; + + AUTO_VAR(it, m_noiseLengths.begin()); + int found=(int)noiseString.find(tag); + while (found!=string::npos && it != m_noiseLengths.end() ) + { + length = *it; + ++it; + + replaceString = L""; + for(int i = 0; i < length; ++i) + { + randomChar = SharedConstants::acceptableLetters[random->nextInt((int)SharedConstants::acceptableLetters.length())]; + + wstring randomCharStr = L""; + randomCharStr.push_back(randomChar); + if(randomChar == L'<') + { + randomCharStr = L"<"; + } + else if (randomChar == L'>' ) + { + randomCharStr = L">"; + } + else if(randomChar == L'"') + { + randomCharStr = L"""; + } + else if(randomChar == L'&') + { + randomCharStr = L"&"; + } + else if(randomChar == L'\\') + { + randomCharStr = L"\\\\"; + } + else if(randomChar == L'{') + { + randomCharStr = L"}"; + } + + int randomVal = random->nextInt(2); + eMinecraftColour colour = eHTMLColor_8; + if(randomVal == 1) colour = eHTMLColor_9; + else if(randomVal == 2) colour = eHTMLColor_a; + ZeroMemory(replacements,64*sizeof(wchar_t)); + swprintf(replacements,64,L"%ls",app.GetHTMLColour(colour),randomCharStr.c_str()); + replaceString.append(replacements); + } + + noiseString.replace( found, tag.length(), replaceString ); + + //int pos = 0; + //do { + // pos = random->nextInt(SharedConstants::acceptableLetters.length()); + //} while (pMinecraft->font->charWidths[ch + 32] != pMinecraft->font->charWidths[pos + 32]); + //ib.put(listPos + 256 + random->nextInt(2) + 8 + (darken ? 16 : 0)); + //ib.put(listPos + pos + 32); + + found=(int)noiseString.find(tag,found+1); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_EndPoem.h b/Minecraft.Client/Common/UI/UIScene_EndPoem.h new file mode 100644 index 0000000..75024f6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_EndPoem.h @@ -0,0 +1,41 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_EndPoem : public UIScene +{ +private: + wstring noNoiseString; + wstring noiseString; + vector m_noiseLengths; + bool m_bIgnoreInput; + int m_requestedLabel; + + vector m_paragraphs; + + IggyName m_funcSetNextLabel; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_NAME(m_funcSetNextLabel, L"SetNextLabel") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_EndPoem(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_EndPoem;} + virtual void updateTooltips(); + +protected: + virtual wstring getMoviePath(); + +public: + virtual void tick(); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleDestroy(); + + virtual void handleRequestMoreData(F64 startIndex, bool up); + +private: + void updateNoise(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.cpp b/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.cpp new file mode 100644 index 0000000..ed31cfa --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.cpp @@ -0,0 +1,374 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_FullscreenProgress.h" +#include "..\..\Minecraft.h" +#include "..\..\ProgressRenderer.h" + + +UIScene_FullscreenProgress::UIScene_FullscreenProgress(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + parentLayer->addComponent(iPad,eUIComponent_Panorama); + parentLayer->addComponent(iPad,eUIComponent_Logo); + parentLayer->showComponent(iPad,eUIComponent_Logo,true); + parentLayer->showComponent(iPad,eUIComponent_MenuBackground,false); + + m_controlTimer.setVisible( false ); + + m_titleText = L""; + m_statusText = L""; + + m_lastTitle = -1; + m_lastStatus = -1; + m_lastProgress = 0; + + m_buttonConfirm.init( app.GetString( IDS_CONFIRM_OK ), eControl_Confirm ); + m_buttonConfirm.setVisible(false); + + LoadingInputParams *params = (LoadingInputParams *)initData; + + m_CompletionData = params->completionData; + m_iPad=params->completionData->iPad; + m_cancelFunc = params->cancelFunc; + m_cancelFuncParam = params->m_cancelFuncParam; + m_completeFunc = params->completeFunc; + m_completeFuncParam = params->m_completeFuncParam; + + m_cancelText = params->cancelText; + m_bWasCancelled=false; + m_bWaitForThreadToDelete = params->waitForThreadToDelete; + + // Clear the progress text + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStart(-1); + pMinecraft->progressRenderer->progressStage(-1); + m_progressBar.init(L"",0,0,100,0); + + // set the tip + wstring wsText= app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + + wchar_t startTags[64]; + swprintf(startTags,64,L"

",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"

"; + m_labelTip.init(wsText); + + addTimer(TIMER_FULLSCREEN_TIPS, TIMER_FULLSCREEN_TIPS_TIME); + + m_labelTitle.init(L""); + + m_labelTip.setVisible( m_CompletionData->bShowTips ); + + thread = new C4JThread(params->func, params->lpParam, "FullscreenProgress"); + thread->SetProcessor(CPU_CORE_UI_SCENE); // TODO 4J Stu - Make sure this is a good thread/core to use + + m_threadCompleted = false; + thread->Run(); + threadStarted = true; + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(false); +#endif +} + +UIScene_FullscreenProgress::~UIScene_FullscreenProgress() +{ + m_parentLayer->removeComponent(eUIComponent_Panorama); + m_parentLayer->removeComponent(eUIComponent_Logo); + + delete thread; + + delete m_CompletionData; +} + +wstring UIScene_FullscreenProgress::getMoviePath() +{ + return L"FullscreenProgress"; +} + +void UIScene_FullscreenProgress::updateTooltips() +{ + ui.SetTooltips( m_parentLayer->IsFullscreenGroup()?XUSER_INDEX_ANY:m_iPad, m_threadCompleted?IDS_TOOLTIPS_SELECT:-1, m_threadCompleted?-1:m_cancelText, -1, -1 ); +} + +void UIScene_FullscreenProgress::handleDestroy() +{ + int code = thread->GetExitCode(); + DWORD exitcode = *((DWORD *)&code); + + // If we're active, have a cancel func, and haven't already cancelled, call cancel func + if( exitcode == STILL_ACTIVE && m_cancelFunc != NULL && !m_bWasCancelled) + { + m_bWasCancelled = true; + m_cancelFunc(m_cancelFuncParam); + } +} + +void UIScene_FullscreenProgress::tick() +{ + UIScene::tick(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + int currentProgress = pMinecraft->progressRenderer->getCurrentPercent(); + if(currentProgress < 0) currentProgress = 0; + if(currentProgress != m_lastProgress) + { + m_lastProgress = currentProgress; + m_progressBar.setProgress(currentProgress); + //app.DebugPrintf("Updated progress value\n"); + } + + int title = pMinecraft->progressRenderer->getCurrentTitle(); + if(title >= 0 && title != m_lastTitle) + { + m_lastTitle = title; + m_titleText = app.GetString( title ); + m_labelTitle.setLabel(m_titleText); + } + + ProgressRenderer::eProgressStringType eProgressType=pMinecraft->progressRenderer->getType(); + + if(eProgressType==ProgressRenderer::eProgressStringType_ID) + { + int status = pMinecraft->progressRenderer->getCurrentStatus(); + if(status >= 0 && status != m_lastStatus) + { + m_lastStatus = status; + m_statusText = app.GetString( status ); + m_progressBar.setLabel(m_statusText.c_str()); + } + } + else + { + wstring& wstrText = pMinecraft->progressRenderer->getProgressString(); + m_progressBar.setLabel(wstrText.c_str()); + } + + + int code = thread->GetExitCode(); + DWORD exitcode = *((DWORD *)&code); + + //app.DebugPrintf("CScene_FullscreenProgress Timer %d\n",pTimer->nId); + + if( exitcode != STILL_ACTIVE ) + { + // If we failed (currently used by network connection thread), navigate back + if( exitcode != S_OK ) + { + if( exitcode == ERROR_CANCELLED ) + { + // Current thread cancelled for whatever reason + // Currently used only for the CConsoleMinecraftApp::RemoteSaveThreadProc thread + // Assume to just ignore this thread as something else is now running that will + // cause another action + } + else + { + /*m_threadCompleted = true; + m_buttonConfirm.SetShow( TRUE ); + m_buttonConfirm.SetFocus( m_CompletionData->iPad ); + m_CompletionData->type = e_ProgressCompletion_NavigateToHomeMenu; + + int exitReasonStringId; + switch( app.GetDisconnectReason() ) + { + default: + exitReasonStringId = IDS_CONNECTION_FAILED; + } + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId );*/ + //app.NavigateBack(m_CompletionData->iPad); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_FAILED), g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_SERVER), uiIDA,1, XUSER_INDEX_ANY,NULL,NULL, app.GetStringTable()); + + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + } + } + else + { + if(( m_CompletionData->bRequiresUserAction == TRUE ) && (!m_bWasCancelled)) + { + m_threadCompleted = true; + m_buttonConfirm.setVisible( true ); + updateTooltips(); + } + else + { + if(m_bWasCancelled) + { + m_threadCompleted = true; + } + app.DebugPrintf("FullScreenProgress complete with action: "); + switch(m_CompletionData->type) + { + case e_ProgressCompletion_AutosaveNavigateBack: + app.DebugPrintf("e_ProgressCompletion_AutosaveNavigateBack\n"); + { + // 4J Stu - Fix for #65437 - Customer Encountered: Code: Settings: Autosave option doesn't work when the Host goes into idle state during gameplay. + // Autosave obviously cannot occur if an ignore autosave menu is displayed, so even if we navigate back to a scene and not empty + // then we still want to reset this flag which was set true by the navigate to the fullscreen progress + ui.SetIgnoreAutosaveMenuDisplayed(m_iPad, false); + + // This just allows it to be shown + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(true); + ui.UpdatePlayerBasePositions(); + navigateBack(); + } + break; + + case e_ProgressCompletion_NavigateBack: + app.DebugPrintf("e_ProgressCompletion_NavigateBack\n"); + { + ui.UpdatePlayerBasePositions(); + navigateBack(); + } + break; + case e_ProgressCompletion_NavigateBackToScene: + app.DebugPrintf("e_ProgressCompletion_NavigateBackToScene\n"); + ui.UpdatePlayerBasePositions(); + // 4J Stu - If used correctly this scene will not have interfered with any other scene at all, so just navigate back + navigateBack(); + break; + case e_ProgressCompletion_CloseUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseUIScenes\n"); + ui.CloseUIScenes(m_CompletionData->iPad); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseAllPlayersUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseAllPlayersUIScenes\n"); + ui.CloseAllPlayersScenes(); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_NavigateToHomeMenu: + app.DebugPrintf("e_ProgressCompletion_NavigateToHomeMenu\n"); + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + break; + default: + app.DebugPrintf("Default\n"); + break; + } + } + } + } +} + +void UIScene_FullscreenProgress::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //if( m_showTooltips ) + { + //ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + case ACTION_MENU_B: + case ACTION_MENU_CANCEL: + if( pressed && m_cancelFunc != NULL && !m_bWasCancelled ) + { + m_bWasCancelled = true; + m_cancelFunc( m_cancelFuncParam ); + } + break; + } + } +} + +void UIScene_FullscreenProgress::handlePress(F64 controlId, F64 childId) +{ + if(m_threadCompleted && (int)controlId == eControl_Confirm) + { + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(m_iPad, ACTION_MENU_A, false, true, false); + + // if there's a complete function, call it + if(m_completeFunc) + { + m_completeFunc(m_completeFuncParam); + } + + switch(m_CompletionData->type) + { + case e_ProgressCompletion_NavigateBack: + app.DebugPrintf("e_ProgressCompletion_NavigateBack\n"); + { + ui.UpdatePlayerBasePositions(); + navigateBack(); + } + break; + case e_ProgressCompletion_NavigateBackToScene: + app.DebugPrintf("e_ProgressCompletion_NavigateBackToScene\n"); + ui.UpdatePlayerBasePositions(); + // 4J Stu - If used correctly this scene will not have interfered with any other scene at all, so just navigate back + navigateBack(); + break; + case e_ProgressCompletion_CloseUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseUIScenes\n"); + ui.CloseUIScenes(m_CompletionData->iPad); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseAllPlayersUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseAllPlayersUIScenes\n"); + ui.CloseAllPlayersScenes(); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_NavigateToHomeMenu: + app.DebugPrintf("e_ProgressCompletion_NavigateToHomeMenu\n"); + ui.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + break; + } + } +} + +void UIScene_FullscreenProgress::handleTimerComplete(int id) +{ + switch(id) + { + case TIMER_FULLSCREEN_TIPS: + { + // display the next tip + wstring wsText=app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + wchar_t startTags[64]; + swprintf(startTags,64,L"

",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"

"; + m_labelTip.setLabel(wsText); + } + break; + } +} + +void UIScene_FullscreenProgress::SetWasCancelled(bool wasCancelled) +{ + m_bWasCancelled = wasCancelled; +} + +bool UIScene_FullscreenProgress::isReadyToDelete() +{ + if( m_bWaitForThreadToDelete ) + { + return !thread->isRunning(); + } + else + { + return true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.h b/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.h new file mode 100644 index 0000000..aeb428c --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_FullscreenProgress.h @@ -0,0 +1,69 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_FullscreenProgress : public UIScene +{ +private: + enum EControl + { + eControl_Confirm, + }; + + static const int TIMER_FULLSCREEN_TIPS = 1; + static const int TIMER_FULLSCREEN_TIPS_TIME = 7000; + + C4JThread* thread; + bool threadStarted; + UIFullscreenProgressCompletionData *m_CompletionData; + bool m_threadCompleted; + int m_iPad; + void (*m_cancelFunc)(LPVOID param); + void (*m_completeFunc)(LPVOID param); + LPVOID m_cancelFuncParam; + LPVOID m_completeFuncParam; + bool m_bWaitForThreadToDelete; + + wstring m_titleText, m_statusText; + int m_lastTitle, m_lastStatus, m_lastProgress; + int m_cancelText; + bool m_bWasCancelled; + + UIControl_Progress m_progressBar; + UIControl_Label m_labelTitle, m_labelTip; + UIControl_Button m_buttonConfirm; + UIControl m_controlTimer; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_progressBar, "ProgressBar") + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_MAP_ELEMENT( m_labelTip, "Tip") + UI_MAP_ELEMENT( m_buttonConfirm, "Confirm") + UI_MAP_ELEMENT( m_controlTimer, "Timer") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_FullscreenProgress(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_FullscreenProgress(); + + virtual EUIScene getSceneType() { return eUIScene_FullscreenProgress;} + virtual void updateTooltips(); + virtual void handleDestroy(); + + void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + + virtual long long getDefaultGtcButtons() { return 0; } + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + void handlePress(F64 controlId, F64 childId); + + virtual void handleTimerComplete(int id); + + void SetWasCancelled(bool wasCancelled); + + virtual bool isReadyToDelete(); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.cpp b/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.cpp new file mode 100644 index 0000000..1ba4053 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.cpp @@ -0,0 +1,256 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\Minecraft.h" +#include "UIScene_FurnaceMenu.h" + +UIScene_FurnaceMenu::UIScene_FurnaceMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelFurnace.init(app.GetString(IDS_FURNACE)); + m_labelIngredient.init(app.GetString(IDS_INGREDIENT)); + m_labelFuel.init(app.GetString(IDS_FUEL)); + + m_progressFurnaceFire.init(L"",0,0,12,0); + m_progressFurnaceArrow.init(L"",0,0,24,0); + + FurnaceScreenInput *initData = (FurnaceScreenInput *)_initData; + m_furnace = initData->furnace; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Furnace_Menu, this); + } + + FurnaceMenu* menu = new FurnaceMenu( initData->inventory, initData->furnace ); + + Initialize( initData->iPad, menu, true, FurnaceMenu::INV_SLOT_START, eSectionFurnaceUsing, eSectionFurnaceMax ); + + m_slotListFuel.addSlots(FurnaceMenu::FUEL_SLOT, 1); + m_slotListIngredient.addSlots(FurnaceMenu::INGREDIENT_SLOT, 1); + m_slotListResult.addSlots(FurnaceMenu::RESULT_SLOT, 1); + + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_FORGING); + + delete initData; +} + +wstring UIScene_FurnaceMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"FurnaceMenuSplit"; + } + else + { + return L"FurnaceMenu"; + } +} + +void UIScene_FurnaceMenu::handleReload() +{ + Initialize( m_iPad, m_menu, true, FurnaceMenu::INV_SLOT_START, eSectionFurnaceUsing, eSectionFurnaceMax ); + + m_slotListFuel.addSlots(FurnaceMenu::FUEL_SLOT, 1); + m_slotListIngredient.addSlots(FurnaceMenu::INGREDIENT_SLOT, 1); + m_slotListResult.addSlots(FurnaceMenu::RESULT_SLOT, 1); +} + +void UIScene_FurnaceMenu::tick() +{ + m_progressFurnaceFire.setProgress( m_furnace->getLitProgress( 12 ) ); + m_progressFurnaceArrow.setProgress( m_furnace->getBurnProgress( 24 ) ); + UIScene_AbstractContainerMenu::tick(); +} + +int UIScene_FurnaceMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionFurnaceResult: + cols = 1; + break; + case eSectionFurnaceFuel: + cols = 1; + break; + case eSectionFurnaceIngredient: + cols = 1; + break; + case eSectionFurnaceInventory: + cols = 9; + break; + case eSectionFurnaceUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_FurnaceMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionFurnaceResult: + rows = 1; + break; + case eSectionFurnaceFuel: + rows = 1; + break; + case eSectionFurnaceIngredient: + rows = 1; + break; + case eSectionFurnaceInventory: + rows = 3; + break; + case eSectionFurnaceUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_FurnaceMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionFurnaceResult: + pPosition->x = m_slotListResult.getXPos(); + pPosition->y = m_slotListResult.getYPos(); + break; + case eSectionFurnaceFuel: + pPosition->x = m_slotListFuel.getXPos(); + pPosition->y = m_slotListFuel.getYPos(); + break; + case eSectionFurnaceIngredient: + pPosition->x = m_slotListIngredient.getXPos(); + pPosition->y = m_slotListIngredient.getYPos(); + break; + case eSectionFurnaceInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionFurnaceUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_FurnaceMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + switch( eSection ) + { + case eSectionFurnaceResult: + sectionSize.x = m_slotListResult.getWidth(); + sectionSize.y = m_slotListResult.getHeight(); + break; + case eSectionFurnaceFuel: + sectionSize.x = m_slotListFuel.getWidth(); + sectionSize.y = m_slotListFuel.getHeight(); + break; + case eSectionFurnaceIngredient: + sectionSize.x = m_slotListIngredient.getWidth(); + sectionSize.y = m_slotListIngredient.getHeight(); + break; + case eSectionFurnaceInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionFurnaceUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; +} + +void UIScene_FurnaceMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionFurnaceResult: + slotList = &m_slotListResult; + break; + case eSectionFurnaceFuel: + slotList = &m_slotListFuel; + break; + case eSectionFurnaceIngredient: + slotList = &m_slotListIngredient; + break; + case eSectionFurnaceInventory: + slotList = &m_slotListInventory; + break; + case eSectionFurnaceUsing: + slotList = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_FurnaceMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionFurnaceResult: + control = &m_slotListResult; + break; + case eSectionFurnaceFuel: + control = &m_slotListFuel; + break; + case eSectionFurnaceIngredient: + control = &m_slotListIngredient; + break; + case eSectionFurnaceInventory: + control = &m_slotListInventory; + break; + case eSectionFurnaceUsing: + control = &m_slotListHotbar; + break; + default: + assert( false ); + break; + } + return control; +} diff --git a/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.h b/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.h new file mode 100644 index 0000000..dcea967 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_FurnaceMenu.h @@ -0,0 +1,50 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_FurnaceMenu.h" + +class InventoryMenu; + +class UIScene_FurnaceMenu : public UIScene_AbstractContainerMenu, public IUIScene_FurnaceMenu +{ +private: + shared_ptr m_furnace; + +public: + UIScene_FurnaceMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_FurnaceMenu;} + +protected: + UIControl_SlotList m_slotListFuel, m_slotListIngredient, m_slotListResult; + UIControl_Label m_labelFurnace, m_labelIngredient, m_labelFuel; + UIControl_Progress m_progressFurnaceFire, m_progressFurnaceArrow; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListIngredient, "Ingredient") + UI_MAP_ELEMENT( m_slotListFuel, "Fuel") + UI_MAP_ELEMENT( m_slotListResult, "Result") + UI_MAP_ELEMENT( m_labelFurnace, "Furnace_text") + UI_MAP_ELEMENT( m_labelIngredient, "Ingredient_Label") + UI_MAP_ELEMENT( m_labelFuel, "Fuel_Label") + + UI_MAP_ELEMENT( m_progressFurnaceFire, "FurnaceFire") + UI_MAP_ELEMENT( m_progressFurnaceArrow, "FurnaceArrow") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual void tick(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_HUD.cpp b/Minecraft.Client/Common/UI/UIScene_HUD.cpp new file mode 100644 index 0000000..27eeb76 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HUD.cpp @@ -0,0 +1,997 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_HUD.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\..\EnderDragonRenderer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +UIScene_HUD::UIScene_HUD(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + m_bSplitscreen = false; + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_lastActiveSlot = 0; + m_lastScale = 1; + m_bToolTipsVisible = true; + m_lastExpProgress = 0.0f; + m_lastExpLevel = 0; + m_lastMaxHealth = 20; + m_lastHealthBlink = false; + m_lastHealthPoison = false; + m_lastMaxFood = 20; + m_lastFoodPoison = false; + m_lastAir = 10; + m_lastArmour = 0; + m_showHealth = true; + m_showFood = true; + m_showAir = true; + m_showArmour = true; + m_showExpBar = true; + m_lastRegenEffect = false; + m_lastSaturation = 0; + m_lastDragonHealth = 0.0f; + m_showDragonHealth = false; + m_ticksWithNoBoss = 0; + m_uiSelectedItemOpacityCountDown = 0; + m_displayName = L""; + m_lastShowDisplayName = true; + + SetDragonLabel( app.GetString( IDS_BOSS_ENDERDRAGON_HEALTH ) ); + SetSelectedLabel(L""); + + for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i) + { + m_labelChatText[i].init(L""); + } + m_labelJukebox.init(L""); + + addTimer(0, 100); +} + +wstring UIScene_HUD::getMoviePath() +{ + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + m_bSplitscreen = true; + return L"HUDSplit"; + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + m_bSplitscreen = false; + return L"HUD"; + break; + } +} + +void UIScene_HUD::updateSafeZone() +{ + // Distance from edge + F64 safeTop = 0.0; + F64 safeBottom = 0.0; + F64 safeLeft = 0.0; + F64 safeRight = 0.0; + + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + safeTop = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + safeLeft = getSafeZoneHalfWidth(); + safeTop = getSafeZoneHalfHeight(); + safeBottom = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + safeRight = getSafeZoneHalfWidth(); + safeTop = getSafeZoneHalfHeight(); + safeBottom = getSafeZoneHalfHeight(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + safeTop = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + safeTop = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + safeBottom = getSafeZoneHalfHeight(); + safeRight = getSafeZoneHalfWidth(); + break; + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + safeTop = getSafeZoneHalfHeight(); + safeBottom = getSafeZoneHalfHeight(); + safeLeft = getSafeZoneHalfWidth(); + safeRight = getSafeZoneHalfWidth(); + break; + } + setSafeZone(safeTop, safeBottom, safeLeft, safeRight); +} + +void UIScene_HUD::tick() +{ + UIScene::tick(); + if(getMovie() && app.GetGameStarted()) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) + { + return; + } + + if(pMinecraft->localplayers[m_iPad]->dimension == 1) + { + if (EnderDragonRenderer::bossInstance == NULL) + { + if(m_ticksWithNoBoss<=20) + { + ++m_ticksWithNoBoss; + } + if( m_ticksWithNoBoss > 20 ) + { + ShowDragonHealth(false); + } + } + else + { + shared_ptr boss = EnderDragonRenderer::bossInstance; + // 4J Stu - Don't clear this here as it's wiped for other players + //EnderDragonRenderer::bossInstance = nullptr; + m_ticksWithNoBoss = 0; + + ShowDragonHealth(true); + SetDragonHealth( (float)boss->getSynchedHealth()/boss->getMaxHealth()); + } + } + else + { + ShowDragonHealth(false); + } + } +} + +void UIScene_HUD::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + int slot = -1; + swscanf((wchar_t*)region->name,L"slot_%d",&slot); + if (slot == -1) + { + app.DebugPrintf("This is not the control we are looking for\n"); + } + else + { + Slot *invSlot = pMinecraft->localplayers[m_iPad]->inventoryMenu->getSlot(InventoryMenu::USE_ROW_SLOT_START + slot); + shared_ptr item = invSlot->getItem(); + if(item != NULL) + { + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + float fVal; + + if(ucAlpha<80) + { + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(m_iPad); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=0.01f*80.0f; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + customDrawSlotControl(region,m_iPad,item,fVal,item->isFoil(),true); + } + } +} + +void UIScene_HUD::handleReload() +{ + m_lastActiveSlot = 0; + m_lastScale = 1; + m_bToolTipsVisible = true; + m_lastExpProgress = 0.0f; + m_lastExpLevel = 0; + m_lastMaxHealth = 20; + m_lastHealthBlink = false; + m_lastHealthPoison = false; + m_lastMaxFood = 20; + m_lastFoodPoison = false; + m_lastAir = 10; + m_lastArmour = 0; + m_showHealth = true; + m_showFood = true; + m_showAir = true; + m_showArmour = true; + m_showExpBar = true; + m_lastRegenEffect = false; + m_lastSaturation = 0; + m_lastDragonHealth = 0.0f; + m_showDragonHealth = false; + m_ticksWithNoBoss = 0; + m_uiSelectedItemOpacityCountDown = 0; + m_displayName = L""; + + m_labelDisplayName.setVisible(m_lastShowDisplayName); + + SetDragonLabel( app.GetString( IDS_BOSS_ENDERDRAGON_HEALTH ) ); + SetSelectedLabel(L""); + + for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i) + { + m_labelChatText[i].init(L""); + } + m_labelJukebox.init(L""); + + int iGuiScale; + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localplayers[m_iPad]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISize); + } + else + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen); + } + SetHudSize(iGuiScale); + + SetDisplayName(ProfileManager.GetDisplayName(m_iPad)); + + repositionHud(); + + SetTooltipsEnabled(((ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad())) || (app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Tooltips) != 0))); +} + +void UIScene_HUD::SetHudSize(int scale) +{ + if(scale != m_lastScale) + { + m_lastScale = scale; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = scale; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcLoadHud , 1 , value ); + } +} + +void UIScene_HUD::SetExpBarProgress(float progress) +{ + if(progress != m_lastExpProgress) + { + m_lastExpProgress = progress; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = progress; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetExpBarProgress , 1 , value ); + } +} + +void UIScene_HUD::SetExpLevel(int level) +{ + if(level != m_lastExpLevel) + { + m_lastExpLevel = level; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = level; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetPlayerLevel , 1 , value ); + } +} + +void UIScene_HUD::SetActiveSlot(int slot) +{ + if(slot != m_lastActiveSlot) + { + m_lastActiveSlot = slot; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = slot; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetActiveSlot , 1 , value ); + } +} + +void UIScene_HUD::SetHealth(int iHealth, int iLastHealth, bool bBlink, bool bPoison) +{ + int maxHealth = max(iHealth, iLastHealth); + if(maxHealth != m_lastMaxHealth || bBlink != m_lastHealthBlink || bPoison != m_lastHealthPoison) + { + m_lastMaxHealth = maxHealth; + m_lastHealthBlink = bBlink; + m_lastHealthPoison = bPoison; + + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = maxHealth; + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bBlink; + value[2].type = IGGY_DATATYPE_boolean; + value[2].boolval = bPoison; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetHealth , 3 , value ); + } +} + +void UIScene_HUD::SetFood(int iFood, int iLastFood, bool bPoison) +{ + // Ignore iLastFood as food doesn't flash + int maxFood = iFood; //, iLastFood); + if(maxFood != m_lastMaxFood || bPoison != m_lastFoodPoison) + { + m_lastMaxFood = maxFood; + m_lastFoodPoison = bPoison; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = maxFood; + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bPoison; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetFood , 2 , value ); + } +} + +void UIScene_HUD::SetAir(int iAir) +{ + if(iAir != m_lastAir) + { + app.DebugPrintf("SetAir to %d\n", iAir); + m_lastAir = iAir; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iAir; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetAir , 1 , value ); + } +} + +void UIScene_HUD::SetArmour(int iArmour) +{ + if(iArmour != m_lastArmour) + { + app.DebugPrintf("SetArmour to %d\n", iArmour); + m_lastArmour = iArmour; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iArmour; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetArmour , 1 , value ); + } +} + +void UIScene_HUD::ShowHealth(bool show) +{ + if(show != m_showHealth) + { + app.DebugPrintf("ShowHealth to %s\n", show?"TRUE":"FALSE"); + m_showHealth = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowHealth , 1 , value ); + } +} + +void UIScene_HUD::ShowFood(bool show) +{ + if(show != m_showFood) + { + app.DebugPrintf("ShowFood to %s\n", show?"TRUE":"FALSE"); + m_showFood = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowFood , 1 , value ); + } +} + +void UIScene_HUD::ShowAir(bool show) +{ + if(show != m_showAir) + { + app.DebugPrintf("ShowAir to %s\n", show?"TRUE":"FALSE"); + m_showAir = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowAir , 1 , value ); + } +} + +void UIScene_HUD::ShowArmour(bool show) +{ + if(show != m_showArmour) + { + app.DebugPrintf("ShowArmour to %s\n", show?"TRUE":"FALSE"); + m_showArmour = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowArmour , 1 , value ); + } +} + +void UIScene_HUD::ShowExpBar(bool show) +{ + if(show != m_showExpBar) + { + app.DebugPrintf("ShowExpBar to %s\n", show?"TRUE":"FALSE"); + m_showExpBar = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowExpbar , 1 , value ); + } +} + +void UIScene_HUD::SetRegenerationEffect(bool bEnabled) +{ + if(bEnabled != m_lastRegenEffect) + { + app.DebugPrintf("SetRegenerationEffect to %s\n", bEnabled?"TRUE":"FALSE"); + m_lastRegenEffect = bEnabled; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = bEnabled; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetRegenerationEffect , 1 , value ); + } +} + +void UIScene_HUD::SetFoodSaturationLevel(int iSaturation) +{ + if(iSaturation != m_lastSaturation) + { + app.DebugPrintf("Set saturation to %d\n", iSaturation); + m_lastSaturation = iSaturation; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iSaturation; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetFoodSaturationLevel , 1 , value ); + } +} + +void UIScene_HUD::SetDragonHealth(float health) +{ + if(health != m_lastDragonHealth) + { + app.DebugPrintf("Set dragon health to %f\n", health); + m_lastDragonHealth = health; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = health; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetDragonHealth , 1 , value ); + } +} + +void UIScene_HUD::SetDragonLabel(const wstring &label) +{ + IggyDataValue result; + IggyDataValue value[1]; + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetDragonLabel , 1 , value ); +} + +void UIScene_HUD::ShowDragonHealth(bool show) +{ + if(show != m_showDragonHealth) + { + app.DebugPrintf("ShowDragonHealth to %s\n", show?"TRUE":"FALSE"); + m_showDragonHealth = show; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowDragonHealth , 1 , value ); + } +} + +void UIScene_HUD::SetSelectedLabel(const wstring &label) +{ + // 4J Stu - Timing here is kept the same as on Xbox360, even though we do it differently now and do the fade out in Flash rather than directly setting opacity + if(!label.empty()) m_uiSelectedItemOpacityCountDown = SharedConstants::TICKS_PER_SECOND * 3; + + IggyDataValue result; + IggyDataValue value[1]; + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetSelectedLabel , 1 , value ); +} + +void UIScene_HUD::HideSelectedLabel() +{ + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcHideSelectedLabel , 0 , NULL ); +} + +void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewport) +{ + if(m_bSplitscreen) + { + S32 xPos = 0; + S32 yPos = 0; + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + yPos = (S32)(ui.getScreenHeight() / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xPos = (S32)(ui.getScreenWidth() / 2); + yPos = (S32)(ui.getScreenHeight() / 2); + break; + } + ui.setupRenderPosition(xPos, yPos); + + S32 tileXStart = 0; + S32 tileYStart = 0; + S32 tileWidth = width; + S32 tileHeight = height; + + switch( viewport ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + tileHeight = (S32)(ui.getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + tileWidth = (S32)(ui.getScreenWidth()); + tileYStart = (S32)(m_movieHeight / 2); + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + tileYStart = (S32)(m_movieHeight / 2); + break; + } + + IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight ); + + m_renderWidth = tileWidth; + m_renderHeight = tileHeight; + + IggyPlayerDrawTilesStart ( getMovie() ); + IggyPlayerDrawTile ( getMovie() , + tileXStart , + tileYStart , + tileXStart + tileWidth , + tileYStart + tileHeight , + 0 ); + IggyPlayerDrawTilesEnd ( getMovie() ); + } + else + { + UIScene::render(width, height, viewport); + } +} + +void UIScene_HUD::handleTimerComplete(int id) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + bool anyVisible = false; + if(pMinecraft->localplayers[m_iPad]!= NULL) + { + Gui *pGui = pMinecraft->gui; + //DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) ); + for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) + { + float opacity = pGui->getOpacity(m_iPad, i); + if( opacity > 0 ) + { + m_controlLabelBackground[i].setOpacity(opacity); + m_labelChatText[i].setOpacity(opacity); + m_labelChatText[i].setLabel( pGui->getMessagesCount(m_iPad) ? pGui->getMessage(m_iPad,i) : L"" ); + + anyVisible = true; + } + else + { + m_controlLabelBackground[i].setOpacity(0); + m_labelChatText[i].setOpacity(0); + m_labelChatText[i].setLabel(L""); + } + } + if(pGui->getJukeboxOpacity(m_iPad) > 0) anyVisible = true; + m_labelJukebox.setOpacity( pGui->getJukeboxOpacity(m_iPad) ); + m_labelJukebox.setLabel( pGui->getJukeboxMessage(m_iPad) ); + } + else + { + for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) + { + m_controlLabelBackground[i].setOpacity(0); + m_labelChatText[i].setOpacity(0); + m_labelChatText[i].setLabel(L""); + } + m_labelJukebox.setOpacity( 0 ); + } + + //setVisible(anyVisible); +} + +void UIScene_HUD::repositionHud() +{ + if(!m_bSplitscreen) return; + + S32 width = 0; + S32 height = 0; + m_parentLayer->getRenderDimensions( width, height ); + + switch( m_parentLayer->getViewport() ) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + height = (S32)(ui.getScreenHeight()); + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + width = (S32)(ui.getScreenWidth()); + break; + } + + app.DebugPrintf(app.USER_SR, "Reposition HUD with dims %d, %d\n", width, height ); + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = width; + value[1].type = IGGY_DATATYPE_number; + value[1].number = height; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcRepositionHud , 2 , value ); +} + +void UIScene_HUD::ShowDisplayName(bool show) +{ + m_lastShowDisplayName = show; + m_labelDisplayName.setVisible(show); +} + +void UIScene_HUD::SetDisplayName(const wstring &displayName) +{ + if(displayName.compare(m_displayName) != 0) + { + m_displayName = displayName; + + IggyDataValue result; + IggyDataValue value[1]; + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)displayName.c_str(); + stringVal.length = displayName.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetDisplayName , 1 , value ); + + m_labelDisplayName.setVisible(m_lastShowDisplayName); + } +} + +void UIScene_HUD::SetTooltipsEnabled(bool bEnabled) +{ + if(m_bToolTipsVisible != bEnabled) + { + m_bToolTipsVisible = bEnabled; + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = bEnabled; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetTooltipsEnabled , 1 , value ); + } +} + +void UIScene_HUD::handleGameTick() +{ + if(getMovie() && app.GetGameStarted()) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) + { + m_parentLayer->showComponent(m_iPad, eUIScene_HUD,false); + return; + } + m_parentLayer->showComponent(m_iPad, eUIScene_HUD,true); + + int iGuiScale; + + if(pMinecraft->localplayers[m_iPad]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISize); + } + else + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen); + } + SetHudSize(iGuiScale); + + SetDisplayName(ProfileManager.GetDisplayName(m_iPad)); + + SetTooltipsEnabled(((ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad())) || (app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Tooltips) != 0))); + +#if TO_BE_IMPLEMENTED + // Move the whole hud group if we are not in fullscreen + if(pMinecraft->localplayers[m_iPad]->m_iScreenSection != C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + int iTooltipsYOffset = 0; + // if tooltips are off, set the y offset to zero + if(app.GetGameSettings(m_iPad,eGameSetting_Tooltips)==0) + { + switch(iGuiScale) + { + case 0: + iTooltipsYOffset=28;//screenHeight/10; + break; + case 2: + iTooltipsYOffset=28;//screenHeight/10; + break; + case 1: + default: + iTooltipsYOffset=28;//screenHeight/10; + break; + } + } + + float fHeight, fWidth; + GetBounds(&fWidth, &fHeight); + + int iSafezoneYHalf = 0; + switch(pMinecraft->localplayers[m_iPad]->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + iSafezoneYHalf = -fHeight/10;// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + iSafezoneYHalf = (fHeight/2)-(fHeight/10);// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + iSafezoneYHalf = (fHeight/2)-(fHeight/10);// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + iSafezoneYHalf = -fHeight/10; // 5% (the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + iSafezoneYHalf = -fHeight/10; // 5% (the whole screen is 2x this screen) + break; + }; + + D3DXVECTOR3 pos; + m_hudGroup.GetPosition(&pos); + pos.y = iTooltipsYOffset + iSafezoneYHalf; + m_hudGroup.SetPosition(&pos); + } +#endif + SetActiveSlot(pMinecraft->localplayers[m_iPad]->inventory->selected); + + // Update xp progress + if (pMinecraft->localgameModes[m_iPad]->canHurtPlayer()) + { + ShowExpBar(true); + int xpNeededForNextLevel = pMinecraft->localplayers[m_iPad]->getXpNeededForNextLevel(); + int progress = (int)(pMinecraft->localplayers[m_iPad]->experienceProgress *xpNeededForNextLevel); + SetExpBarProgress((float)progress/xpNeededForNextLevel); + } + else + { + ShowExpBar(false); + } + + // Update xp level + if (pMinecraft->localgameModes[m_iPad]->hasExperience() && pMinecraft->localplayers[m_iPad]->experienceLevel > 0) + { + SetExpLevel(pMinecraft->localplayers[m_iPad]->experienceLevel); + } + else + { + SetExpLevel(0); + } + + if (pMinecraft->localgameModes[m_iPad]->canHurtPlayer()) + { + ShowHealth(true); + ShowFood(true); + + SetRegenerationEffect(pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::regeneration)); + + // Update health + bool blink = pMinecraft->localplayers[m_iPad]->invulnerableTime / 3 % 2 == 1; + if (pMinecraft->localplayers[m_iPad]->invulnerableTime < 10) blink = false; + int iHealth = pMinecraft->localplayers[m_iPad]->getHealth(); + int iLastHealth = pMinecraft->localplayers[m_iPad]->lastHealth; + bool bHasPoison = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::poison); + SetHealth(iHealth, iLastHealth, blink, bHasPoison); + + // Update food + //bool foodBlink = false; + FoodData *foodData = pMinecraft->localplayers[m_iPad]->getFoodData(); + int food = foodData->getFoodLevel(); + int oldFood = foodData->getLastFoodLevel(); + bool hasHungerEffect = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::hunger); + int saturationLevel = pMinecraft->localplayers[m_iPad]->getFoodData()->getSaturationLevel(); + SetFood(food, oldFood, hasHungerEffect); + SetFoodSaturationLevel(saturationLevel); + + // Update armour + int armor = pMinecraft->localplayers[m_iPad]->getArmorValue(); + if(armor > 0) + { + ShowArmour(true); + SetArmour(armor); + } + else + { + ShowArmour(false); + } + + // Update air + if (pMinecraft->localplayers[m_iPad]->isUnderLiquid(Material::water)) + { + ShowAir(true); + int count = (int) ceil((pMinecraft->localplayers[m_iPad]->getAirSupply() - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY); + SetAir(count); + } + else + { + ShowAir(false); + } + } + else + { + ShowHealth(false); + ShowFood(false); + ShowAir(false); + ShowArmour(false); + } + + if(m_uiSelectedItemOpacityCountDown>0) + { + --m_uiSelectedItemOpacityCountDown; + + // 4J Stu - Timing here is kept the same as on Xbox360, even though we do it differently now and do the fade out in Flash rather than directly setting opacity + if(m_uiSelectedItemOpacityCountDown < (SharedConstants::TICKS_PER_SECOND * 1) ) + { + HideSelectedLabel(); + m_uiSelectedItemOpacityCountDown = 0; + } + } + + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + float fVal; + + if(ucAlpha<80) + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(m_iPad) && (ucAlpha<15)) + { + ucAlpha=15; + } + + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(m_iPad); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=0.01f*80.0f; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + } + else + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(m_iPad) && (ucAlpha<15)) + { + ucAlpha=15; + } + fVal=0.01f*(float)ucAlpha; + } + setOpacity(fVal); + + bool bDisplayGui=app.GetGameStarted() && !ui.GetMenuDisplayed(m_iPad) && !(app.GetXuiAction(m_iPad)==eAppAction_AutosaveSaveGameCapturedThumbnail) && app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)!=0; + if(bDisplayGui && pMinecraft->localplayers[m_iPad] != NULL) + { + setVisible(true); + } + else + { + setVisible(false); + } + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_HUD.h b/Minecraft.Client/Common/UI/UIScene_HUD.h new file mode 100644 index 0000000..cd0d880 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HUD.h @@ -0,0 +1,183 @@ +#pragma once + +#include "UIScene.h" + +#define CHAT_LINES_COUNT 10 + +class UIScene_HUD : public UIScene +{ +private: + bool m_bSplitscreen; + + int m_lastActiveSlot; + int m_lastScale; + bool m_bToolTipsVisible; + float m_lastExpProgress; + int m_lastExpLevel; + int m_lastMaxHealth; + bool m_lastHealthBlink, m_lastHealthPoison; + int m_lastMaxFood; + bool m_lastFoodPoison; + int m_lastAir; + int m_lastArmour; + float m_lastDragonHealth; + bool m_showDragonHealth; + int m_ticksWithNoBoss; + bool m_lastShowDisplayName; + + bool m_showHealth, m_showFood, m_showAir, m_showArmour, m_showExpBar; + bool m_lastRegenEffect; + int m_lastSaturation; + + unsigned int m_uiSelectedItemOpacityCountDown; + + wstring m_displayName; + +protected: + UIControl_Label m_labelChatText[CHAT_LINES_COUNT]; + UIControl_Label m_labelJukebox; + UIControl m_controlLabelBackground[CHAT_LINES_COUNT]; + UIControl_Label m_labelDisplayName; + + IggyName m_funcLoadHud, m_funcSetExpBarProgress, m_funcSetPlayerLevel, m_funcSetActiveSlot; + IggyName m_funcSetHealth, m_funcSetFood, m_funcSetAir, m_funcSetArmour; + IggyName m_funcShowHealth, m_funcShowFood, m_funcShowAir, m_funcShowArmour, m_funcShowExpbar; + IggyName m_funcSetRegenerationEffect, m_funcSetFoodSaturationLevel; + IggyName m_funcSetDragonHealth, m_funcSetDragonLabel, m_funcShowDragonHealth; + IggyName m_funcSetSelectedLabel, m_funcHideSelectedLabel; + IggyName m_funcRepositionHud, m_funcSetDisplayName, m_funcSetTooltipsEnabled; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_labelChatText[0],"Label1") + UI_MAP_ELEMENT(m_labelChatText[1],"Label2") + UI_MAP_ELEMENT(m_labelChatText[2],"Label3") + UI_MAP_ELEMENT(m_labelChatText[3],"Label4") + UI_MAP_ELEMENT(m_labelChatText[4],"Label5") + UI_MAP_ELEMENT(m_labelChatText[5],"Label6") + UI_MAP_ELEMENT(m_labelChatText[6],"Label7") + UI_MAP_ELEMENT(m_labelChatText[7],"Label8") + UI_MAP_ELEMENT(m_labelChatText[8],"Label9") + UI_MAP_ELEMENT(m_labelChatText[9],"Label10") + + UI_MAP_ELEMENT(m_controlLabelBackground[0],"Label1Background") + UI_MAP_ELEMENT(m_controlLabelBackground[1],"Label2Background") + UI_MAP_ELEMENT(m_controlLabelBackground[2],"Label3Background") + UI_MAP_ELEMENT(m_controlLabelBackground[3],"Label4Background") + UI_MAP_ELEMENT(m_controlLabelBackground[4],"Label5Background") + UI_MAP_ELEMENT(m_controlLabelBackground[5],"Label6Background") + UI_MAP_ELEMENT(m_controlLabelBackground[6],"Label7Background") + UI_MAP_ELEMENT(m_controlLabelBackground[7],"Label8Background") + UI_MAP_ELEMENT(m_controlLabelBackground[8],"Label9Background") + UI_MAP_ELEMENT(m_controlLabelBackground[9],"Label10Background") + + UI_MAP_ELEMENT(m_labelJukebox,"Jukebox") + + UI_MAP_ELEMENT(m_labelDisplayName,"LabelGamertag") + + UI_MAP_NAME(m_funcLoadHud, L"LoadHud") + UI_MAP_NAME(m_funcSetExpBarProgress, L"SetExpBarProgress") + UI_MAP_NAME(m_funcSetPlayerLevel, L"SetPlayerLevel") + UI_MAP_NAME(m_funcSetActiveSlot, L"SetActiveSlot") + + UI_MAP_NAME(m_funcSetHealth, L"SetHealth") + UI_MAP_NAME(m_funcSetFood, L"SetFood") + UI_MAP_NAME(m_funcSetAir, L"SetAir") + UI_MAP_NAME(m_funcSetArmour, L"SetArmour") + + UI_MAP_NAME(m_funcShowHealth, L"ShowHealth") + UI_MAP_NAME(m_funcShowFood, L"ShowFood") + UI_MAP_NAME(m_funcShowAir, L"ShowAir") + UI_MAP_NAME(m_funcShowArmour, L"ShowArmour") + UI_MAP_NAME(m_funcShowExpbar, L"ShowExpBar") + + UI_MAP_NAME(m_funcSetRegenerationEffect, L"SetRegenerationEffect") + UI_MAP_NAME(m_funcSetFoodSaturationLevel, L"SetFoodSaturationLevel") + + UI_MAP_NAME(m_funcSetDragonHealth, L"SetDragonHealth") + UI_MAP_NAME(m_funcSetDragonLabel, L"SetDragonLabel") + UI_MAP_NAME(m_funcShowDragonHealth, L"ShowDragonHealthBar") + + UI_MAP_NAME(m_funcSetSelectedLabel, L"SetSelectedLabel") + UI_MAP_NAME(m_funcHideSelectedLabel, L"HideSelectedLabel") + + UI_MAP_NAME(m_funcRepositionHud, L"RepositionHud") + UI_MAP_NAME(m_funcSetDisplayName, L"SetGamertag") + + UI_MAP_NAME(m_funcSetTooltipsEnabled, L"SetTooltipsEnabled") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_HUD(int iPad, void *initData, UILayer *parentLayer); + + virtual void tick(); + + virtual void updateSafeZone(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual EUIScene getSceneType() { return eUIScene_HUD;} + + // Returns true if this scene handles input + virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return false; } + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + + virtual void handleReload(); + +private: + void SetHudSize(int scale); + void SetExpBarProgress(float progress); + void SetExpLevel(int level); + void SetActiveSlot(int slot); + + void SetHealth(int iHealth, int iLastHealth, bool bBlink, bool bPoison); + void SetFood(int iFood, int iLastFood, bool bPoison); + void SetAir(int iAir); + void SetArmour(int iArmour); + + void ShowHealth(bool show); + void ShowFood(bool show); + void ShowAir(bool show); + void ShowArmour(bool show); + void ShowExpBar(bool show); + + void SetRegenerationEffect(bool bEnabled); + void SetFoodSaturationLevel(int iSaturation); + + void SetDragonHealth(float health); + void SetDragonLabel(const wstring &label); + void ShowDragonHealth(bool show); + + void HideSelectedLabel(); + + void SetDisplayName(const wstring &displayName); + + void SetTooltipsEnabled(bool bEnabled); + +public: + void SetSelectedLabel(const wstring &label); + void ShowDisplayName(bool show); + + void handleGameTick(); + + // RENDERING + virtual void render(S32 width, S32 height, C4JRender::eViewportType viewport); + +protected: + void handleTimerComplete(int id); + +#ifdef _DURANGO + virtual long long getDefaultGtcButtons() { return _360_GTC_PAUSE | _360_GTC_MENU | _360_GTC_VIEW; } +#endif + +private: + void repositionHud(); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.cpp new file mode 100644 index 0000000..3fe0332 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.cpp @@ -0,0 +1,233 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_HelpAndOptionsMenu.h" +#include "..\..\Minecraft.h" + +UIScene_HelpAndOptionsMenu::UIScene_HelpAndOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bNotInGame=(Minecraft::GetInstance()->level==NULL); + + m_buttons[BUTTON_HAO_CHANGESKIN].init(app.GetString(IDS_CHANGE_SKIN),BUTTON_HAO_CHANGESKIN); + m_buttons[BUTTON_HAO_HOWTOPLAY].init(app.GetString(IDS_HOW_TO_PLAY),BUTTON_HAO_HOWTOPLAY); + m_buttons[BUTTON_HAO_CONTROLS].init(app.GetString(IDS_CONTROLS),BUTTON_HAO_CONTROLS); + m_buttons[BUTTON_HAO_SETTINGS].init(app.GetString(IDS_SETTINGS),BUTTON_HAO_SETTINGS); + m_buttons[BUTTON_HAO_CREDITS].init(app.GetString(IDS_CREDITS),BUTTON_HAO_CREDITS); + //m_buttons[BUTTON_HAO_REINSTALL].init(app.GetString(IDS_REINSTALL_CONTENT),BUTTON_HAO_REINSTALL); + m_buttons[BUTTON_HAO_DEBUG].init(app.GetString(IDS_DEBUG_SETTINGS),BUTTON_HAO_DEBUG); + + /* 4J-TomK - we should never remove a control before the other buttons controls are initialised! + (because vita touchboxes are rebuilt on remove since the remaining positions might change) */ + // We don't have a reinstall content, so remove the button + removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false ); + + doHorizontalResizeCheck(); + +#ifdef _FINAL_BUILD + removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); +#else + if(!app.DebugSettingsOn()) removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); +#endif + +#ifdef _XBOX_ONE + // 4J-PB - in order to buy the skin packs, we need the signed offer ids for them, which we get in the availability info + // we need to retrieve this info though, so do it here + app.AddDLCRequest(e_Marketplace_Content); // content is skin packs, texture packs and mash-up packs + + // we also need to mount the local DLC so we can tell what's been purchased + app.StartInstallDLCProcess(iPad); +#endif + + + + // 4J-PB - do not need a storage device to see this menu - just need one when you choose to re-install them + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + // any content to be re-installed? + if(m_iPad==ProfileManager.GetPrimaryPad() && bNotInGame) + { + // We should show the reinstall menu + app.DebugPrintf("Reinstall Menu required...\n"); + } + else + { + removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false); + } + + if(app.GetLocalPlayerCount()>1) + { + // no credits in splitscreen + removeControl( &m_buttons[BUTTON_HAO_CREDITS], false); + +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); +#endif + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false); + } + } + + if(!ProfileManager.IsFullVersion() )//|| ProfileManager.IsGuest(m_iPad)) + { + removeControl( &m_buttons[BUTTON_HAO_CHANGESKIN], false); + } + + //StorageManager.TMSPP_GetUserQuotaInfo(C4JStorage::eGlobalStorage_TitleUser,iPad); + //StorageManager.WebServiceRequestGetFriends(iPad); +} + +UIScene_HelpAndOptionsMenu::~UIScene_HelpAndOptionsMenu() +{ +} + +wstring UIScene_HelpAndOptionsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"HelpAndOptionsMenuSplit"; + } + else + { + return L"HelpAndOptionsMenu"; + } +} + +void UIScene_HelpAndOptionsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_HelpAndOptionsMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +void UIScene_HelpAndOptionsMenu::handleReload() +{ +#ifdef _FINAL_BUILD + removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); +#else + if(!app.DebugSettingsOn()) removeControl( &m_buttons[BUTTON_HAO_DEBUG], false); +#endif + + // 4J-PB - do not need a storage device to see this menu - just need one when you choose to re-install them + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + // any content to be re-installed? + if(m_iPad==ProfileManager.GetPrimaryPad() && bNotInGame) + { + // We should show the reinstall menu + app.DebugPrintf("Reinstall Menu required...\n"); + } + else + { + removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false); + } + + if(app.GetLocalPlayerCount()>1) + { + // no credits in splitscreen + removeControl( &m_buttons[BUTTON_HAO_CREDITS], false); + +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); +#endif + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + removeControl( &m_buttons[BUTTON_HAO_REINSTALL], false); + } + } + + if(!ProfileManager.IsFullVersion() )//|| ProfileManager.IsGuest(m_iPad)) + { +#if TO_BE_IMPLEMENTED + m_Buttons[BUTTON_HAO_CHANGESKIN].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_CHANGESKIN].EnableInput(FALSE); + // set the focus to the second button + + XuiElementSetUserFocus(m_Buttons[BUTTON_HAO_HOWTOPLAY].m_hObj, m_iPad); +#endif + } + + if(!ProfileManager.IsFullVersion() )//|| ProfileManager.IsGuest(m_iPad)) + { + removeControl( &m_buttons[BUTTON_HAO_CHANGESKIN], false); + } + + doHorizontalResizeCheck(); +} + +void UIScene_HelpAndOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed && !repeat) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + //CD - Added for audio + if(pressed) + { + ui.PlayUISFX(eSFX_Press); + } + + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_HelpAndOptionsMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case BUTTON_HAO_CHANGESKIN: + ui.NavigateToScene(m_iPad, eUIScene_SkinSelectMenu); + break; + case BUTTON_HAO_HOWTOPLAY: + ui.NavigateToScene(m_iPad, eUIScene_HowToPlayMenu); + break; + case BUTTON_HAO_CONTROLS: + ui.NavigateToScene(m_iPad, eUIScene_ControlsMenu); + break; + case BUTTON_HAO_SETTINGS: + ui.NavigateToScene(m_iPad, eUIScene_SettingsMenu); + break; + case BUTTON_HAO_CREDITS: + ui.NavigateToScene(m_iPad, eUIScene_Credits); + break; + case BUTTON_HAO_REINSTALL: + ui.NavigateToScene(m_iPad, eUIScene_ReinstallMenu); + break; + case BUTTON_HAO_DEBUG: + ui.NavigateToScene(m_iPad, eUIScene_DebugOptions); + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.h new file mode 100644 index 0000000..203011d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HelpAndOptionsMenu.h @@ -0,0 +1,50 @@ +#pragma once + +#include "UIScene.h" + +#define BUTTON_HAO_CHANGESKIN 0 +#define BUTTON_HAO_HOWTOPLAY 1 +#define BUTTON_HAO_CONTROLS 2 +#define BUTTON_HAO_SETTINGS 3 +#define BUTTON_HAO_CREDITS 4 +#define BUTTON_HAO_REINSTALL 5 +#define BUTTON_HAO_DEBUG 6 +#define BUTTONS_HAO_MAX BUTTON_HAO_DEBUG + 1 + +class UIScene_HelpAndOptionsMenu : public UIScene +{ +private: + UIControl_Button m_buttons[BUTTONS_HAO_MAX]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_CHANGESKIN], "Button1") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_HOWTOPLAY], "Button2") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_CONTROLS], "Button3") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_SETTINGS], "Button4") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_CREDITS], "Button5") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_REINSTALL], "Button6") + UI_MAP_ELEMENT( m_buttons[BUTTON_HAO_DEBUG], "Button7") + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bNotInGame; +public: + UIScene_HelpAndOptionsMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_HelpAndOptionsMenu(); + + virtual EUIScene getSceneType() { return eUIScene_HelpAndOptionsMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual void handleReload(); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_HowToPlay.cpp b/Minecraft.Client/Common/UI/UIScene_HowToPlay.cpp new file mode 100644 index 0000000..68c7591 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HowToPlay.cpp @@ -0,0 +1,328 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_HowToPlay.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +static UIScene_HowToPlay::SHowToPlayPageDef gs_aPageDefs[ eHowToPlay_NumPages ] = +{ + { IDS_HOW_TO_PLAY_WHATSNEW, 0, 0}, // eHowToPlay_WhatsNew + { IDS_HOW_TO_PLAY_BASICS, 0, 0}, // eHowToPlay_Basics + { IDS_HOW_TO_PLAY_MULTIPLAYER, 0, 0}, // eHowToPlay_Multiplayer + { IDS_HOW_TO_PLAY_HUD, 0, 0}, // eHowToPlay_HUD + { IDS_HOW_TO_PLAY_CREATIVE, UIScene_HowToPlay::eHowToPlay_LabelCreativeInventory, 1}, // eHowToPlay_Creative + { IDS_HOW_TO_PLAY_INVENTORY, UIScene_HowToPlay::eHowToPlay_LabelIInventory, 1}, // eHowToPlay_Inventory + { IDS_HOW_TO_PLAY_CHEST, UIScene_HowToPlay::eHowToPlay_LabelSCInventory, 2}, // eHowToPlay_Chest + { IDS_HOW_TO_PLAY_LARGECHEST, UIScene_HowToPlay::eHowToPlay_LabelLCInventory, 2}, // eHowToPlay_LargeChest + { IDS_HOW_TO_PLAY_ENDERCHEST, 0, 0}, // eHowToPlay_EnderChest + { IDS_HOW_TO_PLAY_CRAFTING, UIScene_HowToPlay::eHowToPlay_LabelCItem, 3}, // eHowToPlay_InventoryCrafting + { IDS_HOW_TO_PLAY_CRAFT_TABLE, UIScene_HowToPlay::eHowToPlay_LabelCTItem, 3}, // eHowToPlay_CraftTable + { IDS_HOW_TO_PLAY_FURNACE, UIScene_HowToPlay::eHowToPlay_LabelFFuel, 4}, // eHowToPlay_Furnace + { IDS_HOW_TO_PLAY_DISPENSER, UIScene_HowToPlay::eHowToPlay_LabelDText, 2}, // eHowToPlay_Dispenser + { IDS_HOW_TO_PLAY_BREWING, UIScene_HowToPlay::eHowToPlay_LabelBBrew, 2}, // eHowToPlay_Brewing + { IDS_HOW_TO_PLAY_ENCHANTMENT, UIScene_HowToPlay::eHowToPlay_LabelEEnchant, 2}, // eHowToPlay_Enchantment + { IDS_HOW_TO_PLAY_ANVIL, UIScene_HowToPlay::eHowToPlay_LabelAnvil_Inventory, 3}, // eHowToPlay_Anvil + { IDS_HOW_TO_PLAY_FARMANIMALS, 0, 0}, // eHowToPlay_Breeding + { IDS_HOW_TO_PLAY_BREEDANIMALS, 0, 0}, // eHowToPlay_Breeding + { IDS_HOW_TO_PLAY_TRADING, UIScene_HowToPlay::eHowToPlay_LabelTrading_Inventory, 5}, // eHowToPlay_Trading + { IDS_HOW_TO_PLAY_NETHERPORTAL, 0, 0}, // eHowToPlay_NetherPortal + { IDS_HOW_TO_PLAY_THEEND, 0, 0}, // eHowToPlay_NetherPortal +#ifdef _XBOX + { IDS_HOW_TO_PLAY_SOCIALMEDIA, 0, 0}, // eHowToPlay_SocialMedia + { IDS_HOW_TO_PLAY_BANLIST, 0, 0}, // eHowToPlay_BanList +#endif + { IDS_HOW_TO_PLAY_HOSTOPTIONS, 0, 0}, // eHowToPlay_HostOptions +}; + +int gs_pageToFlashMapping[eHowToPlay_NumPages] = +{ + 0, //eHowToPlay_WhatsNew = 0, + 1, //eHowToPlay_Basics, + 2, //eHowToPlay_Multiplayer, + 3, //eHowToPlay_HUD, + 4, //eHowToPlay_Creative, + 5, //eHowToPlay_Inventory, + 6, //eHowToPlay_Chest, + 7, //eHowToPlay_LargeChest, + 23, //eHowToPlay_Enderchest, + 8, //eHowToPlay_InventoryCrafting, + 9, //eHowToPlay_CraftTable, + 10, //eHowToPlay_Furnace, + 11, //eHowToPlay_Dispenser, + + 12, //eHowToPlay_Brewing, + 13, //eHowToPlay_Enchantment, + 21, //eHowToPlay_Anvil, + 14, //eHowToPlay_FarmingAnimals, + 15, //eHowToPlay_Breeding, + 22, //eHowToPlay_Trading, + + 16, //eHowToPlay_NetherPortal, + 17, //eHowToPlay_TheEnd, +#ifdef _XBOX + 18, //eHowToPlay_SocialMedia, + 19, //eHowToPlay_BanList, +#endif + 20, //eHowToPlay_HostOptions, +}; + +UIScene_HowToPlay::UIScene_HowToPlay(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + wstring inventoryString = app.GetString(IDS_INVENTORY); + m_labels[ eHowToPlay_LabelCTItem].init(app.GetString(IDS_ITEM_HATCHET_WOOD)); + m_labels[ eHowToPlay_LabelCTGroup].init(app.GetString(IDS_GROUPNAME_TOOLS)); + m_labels[ eHowToPlay_LabelCTInventory3x3].init(inventoryString); + m_labels[ eHowToPlay_LabelCItem].init(app.GetString(IDS_TILE_WORKBENCH)); + m_labels[ eHowToPlay_LabelCGroup].init(app.GetString(IDS_GROUPNAME_STRUCTURES)); + m_labels[ eHowToPlay_LabelCInventory2x2].init(inventoryString); + m_labels[ eHowToPlay_LabelFFuel].init(app.GetString(IDS_FUEL)); + m_labels[ eHowToPlay_LabelFInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelFIngredient].init(app.GetString(IDS_INGREDIENT)); + m_labels[ eHowToPlay_LabelFChest].init(app.GetString(IDS_FURNACE)); + m_labels[ eHowToPlay_LabelLCInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelCreativeInventory].init(app.GetString(IDS_GROUPNAME_BUILDING_BLOCKS)); + m_labels[ eHowToPlay_LabelLCChest].init(app.GetString(IDS_CHEST)); + m_labels[ eHowToPlay_LabelSCInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelSCChest].init(app.GetString(IDS_CHEST)); + m_labels[ eHowToPlay_LabelIInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelDInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelDText].init(app.GetString(IDS_DISPENSER)); + m_labels[ eHowToPlay_LabelEEnchant].init(app.GetString(IDS_ENCHANT)); + m_labels[ eHowToPlay_LabelEInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelBBrew].init(app.GetString(IDS_BREWING_STAND)); + m_labels[ eHowToPlay_LabelBInventory].init(inventoryString); + m_labels[ eHowToPlay_LabelAnvil_Inventory].init(inventoryString.c_str()); + + wstring wsTemp = app.GetString(IDS_REPAIR_COST); + wsTemp.replace( wsTemp.find(L"%d"), 2, wstring(L"8") ); + + m_labels[ eHowToPlay_LabelAnvil_Cost].init(wsTemp.c_str()); + m_labels[ eHowToPlay_LabelAnvil_ARepairAndName].init(app.GetString(IDS_REPAIR_AND_NAME)); + m_labels[ eHowToPlay_LabelTrading_Inventory].init(inventoryString.c_str()); + m_labels[ eHowToPlay_LabelTrading_Offer2].init(app.GetString(IDS_ITEM_EMERALD)); + m_labels[ eHowToPlay_LabelTrading_Offer1].init(app.GetString(IDS_ITEM_EMERALD)); + m_labels[ eHowToPlay_LabelTrading_NeededForTrade].init(app.GetString(IDS_REQUIRED_ITEMS_FOR_TRADE)); + + wsTemp = app.GetString(IDS_VILLAGER_OFFERS_ITEM); + wsTemp = replaceAll(wsTemp,L"{*VILLAGER_TYPE*}",app.GetString(IDS_VILLAGER_PRIEST)); + wsTemp.replace(wsTemp.find(L"%s"),2, app.GetString(IDS_TILE_LIGHT_GEM)); + m_labels[ eHowToPlay_LabelTrading_VillagerOffers].init(wsTemp.c_str()); + + // Extract pad and required page from init data. We just put the data into the pointer rather than using it as an address. + size_t uiInitData = ( size_t )( initData ); + + EHowToPlayPage eStartPage = ( EHowToPlayPage )( ( uiInitData >> 16 ) & 0xFFF ); // Ignores MSB which is set to 1! + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_HowToPlay, (ETelemetry_HowToPlay_SubMenuId)eStartPage); + + StartPage( eStartPage ); +} + +wstring UIScene_HowToPlay::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"HowToPlaySplit"; + } + else + { + return L"HowToPlay"; + } +} + +void UIScene_HowToPlay::updateTooltips() +{ + // Tool tips. + int iPage = ( int )( m_eCurrPage ); + + int firstPage = eHowToPlay_WhatsNew; +#ifdef __PS3__ + // If it's the blu ray, or the first Japanese digital game, there's no What's New until the first patch, which will take this line out + if(StorageManager.GetBootTypeDisc() || (app.GetProductSKU()==e_sku_SCEJ)) + { + ++firstPage; + } +#elif defined(__ORBIS__) || defined(_DURANGO) || defined(__PSVITA__) + // No What's New for the first PS4 and Xbox One builds + if(true) + { + ++firstPage; + } +#endif + + int iA = -1; + int iX = -1; + if ( iPage == firstPage ) + { + // No previous page. + iA = IDS_HOW_TO_PLAY_NEXT; + } + else if ( ( iPage + 1 ) == eHowToPlay_NumPages ) + { + // No next page. + iX = IDS_HOW_TO_PLAY_PREV; + } + else + { + iA = IDS_HOW_TO_PLAY_NEXT; + iX = IDS_HOW_TO_PLAY_PREV; + } + ui.SetTooltips( m_iPad, iA, IDS_TOOLTIPS_BACK, iX ); +} + +void UIScene_HowToPlay::handleReload() +{ + StartPage( m_eCurrPage ); +} + +void UIScene_HowToPlay::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_A: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + // Next page + int iNextPage = ( int )( m_eCurrPage ) + 1; + if ( iNextPage != eHowToPlay_NumPages ) + { + StartPage( ( EHowToPlayPage )( iNextPage ) ); + ui.PlayUISFX(eSFX_Press); + } + handled = true; + } + break; + case ACTION_MENU_X: + if(pressed) + { + // Previous page + int iPrevPage = ( int )( m_eCurrPage ) - 1; + +#ifdef __PS3__ + // If it's the blu ray, or the first Japanese digital game, there's no What's New until the first patch, which will take this line out + if(StorageManager.GetBootTypeDisc() || (app.GetProductSKU()==e_sku_SCEJ)) + { + if ( iPrevPage >= 0 && !((iPrevPage==eHowToPlay_WhatsNew))) + { + StartPage( ( EHowToPlayPage )( iPrevPage ) ); + ui.PlayUISFX(eSFX_Press); + } + } + else +#elif defined(__ORBIS__) || defined(_DURANGO) || defined(__PSVITA__) + // No What's New for the first PS4 and Xbox One builds + if(true) + { + if ( iPrevPage >= 0 && !((iPrevPage==eHowToPlay_WhatsNew))) + { + StartPage( ( EHowToPlayPage )( iPrevPage ) ); + ui.PlayUISFX(eSFX_Press); + } + } + else +#endif + { + if ( iPrevPage >= 0 ) + { + StartPage( ( EHowToPlayPage )( iPrevPage ) ); + ui.PlayUISFX(eSFX_Press); + } + + } + handled = true; + } + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_HowToPlay::StartPage( EHowToPlayPage ePage ) +{ + m_eCurrPage = ePage; + + // Turn on just what we need for this screen. + SHowToPlayPageDef* pDef = &( gs_aPageDefs[ m_eCurrPage ] ); + + // Replace button identifiers in the text with actual button images. + wstring replacedText = app.FormatHTMLString(m_iPad, app.GetString( pDef->m_iTextStringID )); + // 4J-PB - replace the title with the platform specific title, and the platform name +// replacedText = replaceAll(replacedText,L"{*TITLE_UPDATE_NAME*}",app.GetString(IDS_TITLE_UPDATE_NAME)); +#ifndef _WINDOWS64 + replacedText = replaceAll(replacedText,L"{*KICK_PLAYER_DESCRIPTION*}",app.GetString(IDS_KICK_PLAYER_DESCRIPTION)); +#endif +#ifdef _XBOX_ONE + replacedText = replaceAll(replacedText,L"{*PLATFORM_NAME*}",app.GetString(IDS_PLATFORM_NAME)); +#endif + replacedText = replaceAll(replacedText,L"{*BACK_BUTTON*}",app.GetString(IDS_BACK_BUTTON)); + replacedText = replaceAll(replacedText,L"{*DISABLES_ACHIEVEMENTS*}",app.GetString(IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS)); + + // strip out any tab characters and repeated spaces + stripWhitespaceForHtml( replacedText, true ); + + // Set the text colour + wstring finalText(replacedText.c_str() ); + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + finalText = startTags + finalText; + + vector paragraphs; + int lastIndex = 0; + for ( int index = finalText.find(L"\r\n", lastIndex, 2); + index != wstring::npos; + index = finalText.find(L"\r\n", lastIndex, 2) + ) + { + paragraphs.push_back( finalText.substr(lastIndex, index-lastIndex) + L" " ); + lastIndex = index + 2; + } + paragraphs.push_back( finalText.substr( lastIndex, finalText.length() - lastIndex ) ); + + // Set the text in the scene + IggyDataValue result; + + IggyDataValue *value = new IggyDataValue[paragraphs.size()+1]; + IggyStringUTF16 * stringVal = new IggyStringUTF16[paragraphs.size()]; + + value[0].type = IGGY_DATATYPE_number; + value[0].number = gs_pageToFlashMapping[(int)ePage]; + + for(unsigned int i = 0; i < paragraphs.size(); ++i) + { + stringVal[i].string = (IggyUTF16 *)paragraphs[i].c_str(); + stringVal[i].length = paragraphs[i].length(); + value[i+1].type = IGGY_DATATYPE_string_UTF16; + value[i+1].string16 = stringVal[i]; + } + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcLoadPage , 1 + paragraphs.size(), value ); + + delete [] value; + delete [] stringVal; + + updateTooltips(); + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_HowToPlay, (ETelemetry_HowToPlay_SubMenuId)ePage); + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} diff --git a/Minecraft.Client/Common/UI/UIScene_HowToPlay.h b/Minecraft.Client/Common/UI/UIScene_HowToPlay.h new file mode 100644 index 0000000..cff0725 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HowToPlay.h @@ -0,0 +1,123 @@ +#pragma once + +#include "UIScene.h" + + +class UIScene_HowToPlay : public UIScene +{ +public: + enum EHowToPlayLabelControls + { + eHowToPlay_LabelNone = -1, + eHowToPlay_LabelIInventory =0, + eHowToPlay_LabelSCInventory , + eHowToPlay_LabelSCChest , + eHowToPlay_LabelLCInventory , + eHowToPlay_LabelLCChest , + eHowToPlay_LabelCItem , + eHowToPlay_LabelCGroup , + eHowToPlay_LabelCInventory2x2 , + eHowToPlay_LabelCTItem , + eHowToPlay_LabelCTGroup , + eHowToPlay_LabelCTInventory3x3 , + eHowToPlay_LabelFFuel , + eHowToPlay_LabelFInventory , + eHowToPlay_LabelFIngredient , + eHowToPlay_LabelFChest , + eHowToPlay_LabelDText , + eHowToPlay_LabelDInventory , + eHowToPlay_LabelCreativeInventory, + eHowToPlay_LabelEEnchant, + eHowToPlay_LabelEInventory, + eHowToPlay_LabelBBrew, + eHowToPlay_LabelBInventory, + eHowToPlay_LabelAnvil_Inventory, + eHowToPlay_LabelAnvil_Cost, + eHowToPlay_LabelAnvil_ARepairAndName, + eHowToPlay_LabelTrading_Inventory, + eHowToPlay_LabelTrading_Offer2, + eHowToPlay_LabelTrading_Offer1, + eHowToPlay_LabelTrading_NeededForTrade, + eHowToPlay_LabelTrading_VillagerOffers, + eHowToPlay_NumLabels + }; + + struct SHowToPlayPageDef + { + int m_iTextStringID; // -1 if not used. + int m_iLabelStartIndex; // index of the labels if there are any for the page + int m_iLabelCount; + }; + +private: + EHowToPlayPage m_eCurrPage; + + IggyName m_funcLoadPage; + UIControl_DynamicLabel m_DynamicLabel; + UIControl_Label m_labels[ eHowToPlay_NumLabels ]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_DynamicLabel , "DynamicHtmlText" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCTGroup ] , "Label1_9" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCTItem ] , "Label2_9") + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCTInventory3x3 ] , "Label3_9" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCGroup ] , "Label1_8" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCItem ] , "Label2_8" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCInventory2x2 ] , "Label3_8" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelFChest ] , "Label1_10" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelFIngredient ] , "Label2_10" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelFFuel ] , "Label3_10" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelFInventory ] , "Label4_10" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelLCChest ] , "Label1_7" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelLCInventory ] , "Label2_7" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelCreativeInventory ] , "Label1_4" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelSCChest ] , "Label1_6" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelSCInventory ] , "Label2_6" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelIInventory ] , "Label1_5" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelDText ] , "Label1_11" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelDInventory ] , "Label2_11" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelEEnchant ] , "Label1_13" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelEInventory ] , "Label2_13" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelBBrew ] , "Label1_12" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelBInventory ] , "Label2_12" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelTrading_VillagerOffers ] , "Label1_22" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelTrading_NeededForTrade ] , "Label2_22" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelTrading_Inventory ] , "Label3_22" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelTrading_Offer1 ] , "Label4_22" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelTrading_Offer2 ] , "Label5_22" ) + + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelAnvil_ARepairAndName ] , "Label1_21" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelAnvil_Cost ] , "Label2_21" ) + UI_MAP_ELEMENT( m_labels[ eHowToPlay_LabelAnvil_Inventory ] , "Label3_21" ) + + UI_MAP_NAME(m_funcLoadPage, L"LoadHowToPlayPage") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_HowToPlay(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_HowToPlay;} + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual void handleReload(); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +private: + void StartPage( EHowToPlayPage ePage ); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.cpp b/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.cpp new file mode 100644 index 0000000..5fc6e02 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.cpp @@ -0,0 +1,205 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_HowToPlayMenu.h" + +// strings for buttons in the list +unsigned int UIScene_HowToPlayMenu::m_uiHTPButtonNameA[]= +{ + IDS_HOW_TO_PLAY_MENU_WHATSNEW, // eHTPButton_WhatsNew + IDS_HOW_TO_PLAY_MENU_BASICS, // eHTPButton_Basics, + IDS_HOW_TO_PLAY_MENU_MULTIPLAYER, // eHTPButton_Multiplayer + IDS_HOW_TO_PLAY_MENU_HUD, // eHTPButton_Hud, + IDS_HOW_TO_PLAY_MENU_CREATIVE, // eHTPButton_Creative, + IDS_HOW_TO_PLAY_MENU_INVENTORY, // eHTPButton_Inventory, + IDS_HOW_TO_PLAY_MENU_CHESTS, // eHTPButton_Chest, + IDS_HOW_TO_PLAY_MENU_CRAFTING, // eHTPButton_Crafting, + IDS_HOW_TO_PLAY_MENU_FURNACE, // eHTPButton_Furnace, + IDS_HOW_TO_PLAY_MENU_DISPENSER, // eHTPButton_Dispenser, + + IDS_HOW_TO_PLAY_MENU_BREWING, // eHTPButton_Brewing, + IDS_HOW_TO_PLAY_MENU_ENCHANTMENT, // eHTPButton_Enchantment, + IDS_HOW_TO_PLAY_MENU_ANVIL, + IDS_HOW_TO_PLAY_MENU_FARMANIMALS, // eHTPButton_Breeding, + IDS_HOW_TO_PLAY_MENU_BREEDANIMALS, // eHTPButton_Breeding, + IDS_HOW_TO_PLAY_MENU_TRADING, + + IDS_HOW_TO_PLAY_MENU_NETHERPORTAL, // eHTPButton_NetherPortal, + IDS_HOW_TO_PLAY_MENU_THEEND, // eHTPButton_TheEnd, +#ifdef _XBOX + IDS_HOW_TO_PLAY_MENU_SOCIALMEDIA, // eHTPButton_SocialMedia, + IDS_HOW_TO_PLAY_MENU_BANLIST, // eHTPButton_BanningLevels, +#endif + IDS_HOW_TO_PLAY_MENU_HOSTOPTIONS, // eHTPButton_HostOptions, +}; + +// mapping the buttons to a scene value +unsigned int UIScene_HowToPlayMenu::m_uiHTPSceneA[]= +{ + eHowToPlay_WhatsNew, + eHowToPlay_Basics, + eHowToPlay_Multiplayer, + eHowToPlay_HUD, + eHowToPlay_Creative, + eHowToPlay_Inventory, + eHowToPlay_Chest, + eHowToPlay_InventoryCrafting, + eHowToPlay_Furnace, + eHowToPlay_Dispenser, + + eHowToPlay_Brewing, + eHowToPlay_Enchantment, + eHowToPlay_Anvil, + eHowToPlay_FarmingAnimals, + eHowToPlay_Breeding, + eHowToPlay_Trading, + + eHowToPlay_NetherPortal, + eHowToPlay_TheEnd, +#ifdef _XBOX + eHowToPlay_SocialMedia, + eHowToPlay_BanList, +#endif + eHowToPlay_HostOptions, +}; + +UIScene_HowToPlayMenu::UIScene_HowToPlayMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_buttonListHowTo.init(eControl_Buttons); + + for(unsigned int i = 0; i < eHTPButton_Max; ++i) + { +#ifdef __PS3__ + // If it's the blu ray, or the first Japanese digital game, there's no What's New until the first patch, which will take this line out + if(StorageManager.GetBootTypeDisc() || (app.GetProductSKU()==e_sku_SCEJ)) + { + if(!(i==eHTPButton_WhatsNew) ) + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i);//iCount++); + } + } + else +#elif defined(__ORBIS__) || defined(_DURANGO) || defined(__PSVITA__) + // No What's New for the first PS4 and Xbox One builds + if(true) + { + if(!(i==eHTPButton_WhatsNew) ) + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i);//iCount++); + } + } + else +#endif + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i);//iCount++); + } + } +} + +wstring UIScene_HowToPlayMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"HowToPlayMenuSplit"; + } + else + { + return L"HowToPlayMenu"; + } +} + +void UIScene_HowToPlayMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_HowToPlayMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + } +} + +void UIScene_HowToPlayMenu::handleReload() +{ + for(unsigned int i = 0; i < eHTPButton_Max; ++i) + { +#ifdef __PS3__ + // If it's the blu ray, or the first Japanese digital game, there's no What's New until the first patch, which will take this line out + if(StorageManager.GetBootTypeDisc() || (app.GetProductSKU()==e_sku_SCEJ)) + { + if(!(i==eHTPButton_WhatsNew) ) + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i); + } + } + else +#elif defined(__ORBIS__) || defined(_DURANGO) || defined(__PSVITA__) + // No What's New for the first PS4 and Xbox One builds + if(true) + { + if(!(i==eHTPButton_WhatsNew) ) + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i); + } + } + else +#endif + { + m_buttonListHowTo.addItem( app.GetString(m_uiHTPButtonNameA[i]) , i); + } + } +} + +void UIScene_HowToPlayMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_HowToPlayMenu::handlePress(F64 controlId, F64 childId) +{ + if( (int)controlId == eControl_Buttons) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + unsigned int uiInitData; + uiInitData = ( ( 1 << 31 ) | ( m_uiHTPSceneA[(int)childId] << 16 ) | ( short )( m_iPad ) ); + ui.NavigateToScene(m_iPad, eUIScene_HowToPlay, ( void* )( uiInitData ) ); + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.h b/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.h new file mode 100644 index 0000000..5a60cdd --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_HowToPlayMenu.h @@ -0,0 +1,68 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_HowToPlayMenu : public UIScene +{ +private: + enum EControls + { + eControl_Buttons, + }; + + enum eHTPButton + { + eHTPButton_WhatsNew = 0, + eHTPButton_Basics, + eHTPButton_Multiplayer, + eHTPButton_Hud, + eHTPButton_Creative, + eHTPButton_Inventory, + eHTPButton_Chest, + eHTPButton_Crafting, + eHTPButton_Furnace, + eHTPButton_Dispenser, + eHTPButton_Brewing, + eHTPButton_Enchantment, + eHTPButton_Anvil, + eHTPButton_FarmingAnimals, + eHTPButton_Breeding, + eHTPButton_Trading, + eHTPButton_NetherPortal, + eHTPButton_TheEnd, +#ifdef _XBOX + eHTPButton_SocialMedia, + eHTPButton_BanningLevels, +#endif + eHTPButton_HostOptions, + eHTPButton_Max, + }; + + static unsigned int m_uiHTPButtonNameA[eHTPButton_Max]; + static unsigned int m_uiHTPSceneA[eHTPButton_Max]; + + UIControl_ButtonList m_buttonListHowTo; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListHowTo, "HowToList") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_HowToPlayMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_HowToPlayMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual void handleReload(); +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.cpp new file mode 100644 index 0000000..de8af0a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_InGameHostOptionsMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +UIScene_InGameHostOptionsMenu::UIScene_InGameHostOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_checkboxFireSpreads.init(app.GetString(IDS_FIRE_SPREADS), eControl_FireSpreads, app.GetGameHostOption(eGameHostOption_FireSpreads)!=0); + m_checkboxTNT.init(app.GetString(IDS_TNT_EXPLODES), eControl_TNT, app.GetGameHostOption(eGameHostOption_TNT)!=0); + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + unsigned int privs = app.GetPlayerPrivileges(localPlayer->GetSmallId()); + if(app.GetGameHostOption(eGameHostOption_CheatsEnabled) + && Player::getPlayerGamePrivilege(privs,Player::ePlayerGamePrivilege_CanTeleport) + && g_NetworkManager.GetPlayerCount() > 1) + { + m_buttonTeleportToPlayer.init(app.GetString(IDS_TELEPORT_TO_PLAYER), eControl_TeleportToPlayer); + m_buttonTeleportToMe.init(app.GetString(IDS_TELEPORT_TO_ME), eControl_TeleportToMe); + } + else + { + removeControl(&m_buttonTeleportToPlayer, true); + removeControl(&m_buttonTeleportToMe, true); + } +} + +wstring UIScene_InGameHostOptionsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"InGameHostOptionsSplit"; + } + else + { + return L"InGameHostOptions"; + } +} + +void UIScene_InGameHostOptionsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_InGameHostOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + unsigned int hostOptions = app.GetGameHostOption(eGameHostOption_All); + app.SetGameHostOption(hostOptions,eGameHostOption_FireSpreads,m_checkboxFireSpreads.IsChecked()); + app.SetGameHostOption(hostOptions,eGameHostOption_TNT,m_checkboxTNT.IsChecked()); + + // Send update settings packet to server + if(hostOptions != app.GetGameHostOption(eGameHostOption_All) ) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player->connection) + { + player->connection->send( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, hostOptions) ) ); + } + } + + navigateBack(); + + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_InGameHostOptionsMenu::handlePress(F64 controlId, F64 childId) +{ + TeleportMenuInitData *initData = new TeleportMenuInitData(); + initData->iPad = m_iPad; + initData->teleportToPlayer = false; + if( (int)controlId == eControl_TeleportToPlayer ) + { + initData->teleportToPlayer = true; + } + ui.NavigateToScene(m_iPad,eUIScene_TeleportMenu,(void*)initData); +} diff --git a/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.h new file mode 100644 index 0000000..2471141 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameHostOptionsMenu.h @@ -0,0 +1,38 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_InGameHostOptionsMenu : public UIScene +{ +private: + enum EControls + { + eControl_FireSpreads, + eControl_TNT, + eControl_TeleportToPlayer, + eControl_TeleportToMe, + }; + + UIControl_CheckBox m_checkboxFireSpreads, m_checkboxTNT; + UIControl_Button m_buttonTeleportToPlayer, m_buttonTeleportToMe; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_checkboxFireSpreads, "CheckboxFireSpreads") + UI_MAP_ELEMENT( m_checkboxTNT, "CheckboxTNT") + UI_MAP_ELEMENT( m_buttonTeleportToPlayer, "TeleportToPlayer") + UI_MAP_ELEMENT( m_buttonTeleportToMe, "TeleportPlayerToMe") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_InGameHostOptionsMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_InGameHostOptionsMenu;} + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handlePress(F64 controlId, F64 childId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp new file mode 100644 index 0000000..5c3f73f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.cpp @@ -0,0 +1,605 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_InGameInfoMenu.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" + +UIScene_InGameInfoMenu::UIScene_InGameInfoMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_buttonGameOptions.init(app.GetString(IDS_HOST_OPTIONS),eControl_GameOptions); + m_labelTitle.init(app.GetString(IDS_PLAYERS_INVITE)); + m_playerList.init(eControl_GamePlayers); + + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + m_playerNames[i] = L""; + } + + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL ) + { + m_players[i] = player->GetSmallId(); + ++m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( player->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + m_playersVoiceState[i] = voiceStatus; + m_playersColourState[i] = app.GetPlayerColour( m_players[i] ); + m_playerNames[i] = playerName; + m_playerList.addItem( playerName, app.GetPlayerColour( m_players[i] ), voiceStatus); + } + } + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &UIScene_InGameInfoMenu::OnPlayerChanged, this); + + INetworkPlayer *thisPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + m_isHostPlayer = false; + if(thisPlayer != NULL) m_isHostPlayer = thisPlayer->IsHost() == TRUE; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + if(!m_isHostPlayer && !localPlayer->isModerator() ) + { + removeControl( &m_buttonGameOptions, false ); + } + + updateTooltips(); + +#if TO_BE_IMPLEMENTED + SetTimer( TOOLTIP_TIMERID , INGAME_INFO_TOOLTIP_TIMER ); +#endif + + // get rid of the quadrant display if it's on + ui.HidePressStart(); + +#if TO_BE_IMPLEMENTED + SetTimer(IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); +#endif +} + +wstring UIScene_InGameInfoMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"InGameInfoMenuSplit"; + } + else + { + return L"InGameInfoMenu"; + } +} + +void UIScene_InGameInfoMenu::updateTooltips() +{ + int keyX = IDS_TOOLTIPS_INVITE_FRIENDS; + int ikeyY = -1; + + XPARTY_USER_LIST partyList; + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + keyX = IDS_TOOLTIPS_INVITE_PARTY; + } + + if(g_NetworkManager.IsLocalGame()) keyX = -1; +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode()) keyX = -1; +#endif + + + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ m_playerList.getCurrentSelection() ] ); + + int keyA = -1; + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + + bool isOp = m_isHostPlayer || localPlayer->isModerator(); + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + bool trust = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + + if( isOp ) + { + if(m_buttonGameOptions.hasFocus()) + { + keyA = IDS_TOOLTIPS_SELECT; + } + else if( selectedPlayer != NULL) + { + bool editingHost = selectedPlayer->IsHost(); + if( (cheats && (m_isHostPlayer || !editingHost ) ) || (!trust && (m_isHostPlayer || !editingHost)) +#if (!defined(_CONTENT_PACKAGE) && !defined(_FINAL_BUILD) && defined(_DEBUG_MENUS_ENABLED)) + || (m_isHostPlayer && editingHost) +#endif + ) + { + keyA = IDS_TOOLTIPS_PRIVILEGES; + } + else if(selectedPlayer->IsLocal() != TRUE && selectedPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + // Only ops will hit this, can kick anyone not local and not local to the host + keyA = IDS_TOOLTIPS_KICK; + } + } + } + +#if defined(__PS3__) || defined(__ORBIS__) + if(m_iPad == ProfileManager.GetPrimaryPad() ) ikeyY = IDS_TOOLTIPS_GAME_INVITES; +#else + if(!m_buttonGameOptions.hasFocus()) + { + // if the player is me, then view gamer profile + if(selectedPlayer != NULL && selectedPlayer->IsLocal() && selectedPlayer->GetUserIndex()==m_iPad) + { + ikeyY = IDS_TOOLTIPS_VIEW_GAMERPROFILE; + } + else + { + ikeyY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + } +#endif + ui.SetTooltips( m_iPad, keyA,IDS_TOOLTIPS_BACK,keyX,ikeyY); +} + +void UIScene_InGameInfoMenu::handleDestroy() +{ + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &UIScene_InGameInfoMenu::OnPlayerChanged, this); + + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +void UIScene_InGameInfoMenu::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + if( navBack ) g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &UIScene_InGameInfoMenu::OnPlayerChanged, this); +} + +void UIScene_InGameInfoMenu::handleReload() +{ + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL ) + { + m_players[i] = player->GetSmallId(); + ++m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( player->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + m_playersVoiceState[i] = voiceStatus; + m_playersColourState[i] = app.GetPlayerColour( m_players[i] ); + m_playerNames[i] = playerName; + m_playerList.addItem( playerName, app.GetPlayerColour( m_players[i] ), voiceStatus); + } + } + + INetworkPlayer *thisPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + m_isHostPlayer = false; + if(thisPlayer != NULL) m_isHostPlayer = thisPlayer->IsHost() == TRUE; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + if(!m_isHostPlayer && !localPlayer->isModerator() ) + { + removeControl( &m_buttonGameOptions, false ); + } + + updateTooltips(); + + if(controlHasFocus(eControl_GamePlayers)) + { + m_playerList.setCurrentSelection(getControlChildFocus()); + } +} + +void UIScene_InGameInfoMenu::tick() +{ + UIScene::tick(); + + for(DWORD i = 0; i < m_playersCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL ) + { + m_players[i] = player->GetSmallId(); + int voiceStatus = 0; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( player->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + if(voiceStatus != m_playersVoiceState[i]) + { + m_playersVoiceState[i] = voiceStatus; + m_playerList.setVOIPIcon( i, voiceStatus ); + } + + short icon = app.GetPlayerColour( m_players[i] ); + + if(icon != m_playersColourState[i]) + { + m_playersColourState[i] = icon; + m_playerList.setPlayerIcon( i, (int)app.GetPlayerColour( m_players[i] ) ); + } + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + if(playerName.compare( m_playerNames[i] ) != 0 ) + { + m_playerList.setButtonLabel(i, playerName); + m_playerNames[i] = playerName; + } + } + } +} + +void UIScene_InGameInfoMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed && !repeat) + { + ui.PlayUISFX(eSFX_Back); + navigateBack(); + } + break; + case ACTION_MENU_Y: +#if defined(__PS3__) || defined(__ORBIS__) + if(pressed && iPad == ProfileManager.GetPrimaryPad()) + { +#ifdef __PS3__ + // are we offline? + if(!ProfileManager.IsSignedInLive(iPad)) + { + // get them to sign in to online + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_InGameInfoMenu::MustSignInReturnedPSN,this, app.GetStringTable()); + } + else +#endif + { +#ifdef __ORBIS__ + SQRNetworkManager_Orbis::RecvInviteGUI(); +#else // __PS3__ + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#endif + } + } +#else + + + if(pressed && m_playerList.hasFocus() && (m_playerList.getItemCount() > 0) && (m_playerList.getCurrentSelection() < m_playersCount) ) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId(m_players[m_playerList.getCurrentSelection()]); + if( player != NULL ) + { + PlayerUID uid = player->GetUID(); + if( uid != INVALID_XUID ) + { +#ifdef __PSVITA__ + PSVITA_STUBBED; +#else + ProfileManager.ShowProfileCard(iPad,uid); +#endif + } + } + } + +#endif + break; + case ACTION_MENU_X: + + if(pressed && !repeat && !g_NetworkManager.IsLocalGame() ) + { +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() == false) + g_NetworkManager.SendInviteGUI(iPad); +#else + g_NetworkManager.SendInviteGUI(iPad); +#endif + } + + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_InGameInfoMenu::handlePress(F64 controlId, F64 childId) +{ + app.DebugPrintf("Pressed = %d, %d\n", (int)controlId, (int)childId); + switch((int)controlId) + { + case eControl_GameOptions: + ui.NavigateToScene(m_iPad,eUIScene_InGameHostOptionsMenu); + break; + case eControl_GamePlayers: + int currentSelection = (int)childId; + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ currentSelection ] ); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + + bool isOp = m_isHostPlayer || localPlayer->isModerator(); + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + bool trust = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + + if( isOp && selectedPlayer != NULL) + { + bool editingHost = selectedPlayer->IsHost(); + if( (cheats && (m_isHostPlayer || !editingHost ) ) || (!trust && (m_isHostPlayer || !editingHost)) +#if (!defined(_CONTENT_PACKAGE) && !defined(_FINAL_BUILD) && defined(_DEBUG_MENUS_ENABLED)) + || (m_isHostPlayer && editingHost) +#endif + ) + { + InGamePlayerOptionsInitData *pInitData = new InGamePlayerOptionsInitData(); + pInitData->iPad = m_iPad; + pInitData->networkSmallId = m_players[ currentSelection ]; + pInitData->playerPrivileges = app.GetPlayerPrivileges(m_players[ currentSelection ] ); + ui.NavigateToScene(m_iPad,eUIScene_InGamePlayerOptionsMenu,pInitData); + } + else if(selectedPlayer->IsLocal() != TRUE && selectedPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + // Only ops will hit this, can kick anyone not local and not local to the host + BYTE *smallId = new BYTE(); + *smallId = m_players[currentSelection]; + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_UNLOCK_KICK_PLAYER_TITLE, IDS_UNLOCK_KICK_PLAYER, uiIDA, 2, m_iPad,&UIScene_InGameInfoMenu::KickPlayerReturned,smallId,app.GetStringTable(),NULL,0,false); + } + } + break; + } +} + +void UIScene_InGameInfoMenu::handleFocusChange(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_GamePlayers: + m_playerList.updateChildFocus( (int) childId ); + }; + updateTooltips(); +} + +void UIScene_InGameInfoMenu::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + UIScene_InGameInfoMenu *scene = (UIScene_InGameInfoMenu *)callbackParam; + bool playerFound = false; + int foundIndex = 0; + for(int i = 0; i < scene->m_playersCount; ++i) + { + if(!playerFound && scene->m_players[i] == pPlayer->GetSmallId() ) + { + if( scene->m_playerList.getCurrentSelection() == scene->m_playerList.getItemCount() - 1 ) + { + scene->m_playerList.setCurrentSelection( scene->m_playerList.getItemCount() - 2 ); + } + // Player removed + playerFound = true; + foundIndex = i; + } + } + + if( playerFound ) + { + --scene->m_playersCount; + scene->m_playersVoiceState[scene->m_playersCount] = 0; + scene->m_playersColourState[scene->m_playersCount] = 0; + scene->m_playerNames[scene->m_playersCount] = L""; + scene->m_playerList.removeItem(scene->m_playersCount); + } + + if( !playerFound ) + { + // Player added + scene->m_players[scene->m_playersCount] = pPlayer->GetSmallId(); + ++scene->m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(pPlayer != NULL && pPlayer->HasVoice() ) + { + if( pPlayer->IsMutedByLocalUser(scene->m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( pPlayer->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + scene->m_playerList.addItem( playerName, app.GetPlayerColour( scene->m_players[scene->m_playersCount - 1] ), voiceStatus); + } +} + +int UIScene_InGameInfoMenu::KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + BYTE smallId = *(BYTE *)pParam; + delete pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[iPad]; + if(localPlayer->connection) + { + localPlayer->connection->send( shared_ptr( new KickPlayerPacket(smallId) ) ); + } + } + + return 0; +} + +#if defined __PS3__ || defined __PSVITA__ +int UIScene_InGameInfoMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_InGameInfoMenu* pClass = (UIScene_InGameInfoMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_InGameInfoMenu::ViewInvites_SignInReturned, pClass); +#else // __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_InGameInfoMenu::ViewInvites_SignInReturned, pClass); +#endif + } + + return 0; +} + +int UIScene_InGameInfoMenu::ViewInvites_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + if(bContinue==true) + { + // Check if we're signed in to LIVE + if(ProfileManager.IsSignedInLive(iPad)) + { +#ifdef __ORBIS__ + SQRNetworkManager_Orbis::RecvInviteGUI(); +#elif defined(__PS3__) + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#else // __PSVITA__ + PSVITA_STUBBED; +#endif + } + } + return 0; +} +#endif diff --git a/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.h b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.h new file mode 100644 index 0000000..94966fa --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameInfoMenu.h @@ -0,0 +1,62 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_InGameInfoMenu : public UIScene +{ +private: + enum EControls + { + eControl_GameOptions, + eControl_GamePlayers, + }; + + bool m_isHostPlayer; + int m_playersCount; + BYTE m_players[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's + char m_playersVoiceState[MINECRAFT_NET_MAX_PLAYERS]; + short m_playersColourState[MINECRAFT_NET_MAX_PLAYERS]; + wstring m_playerNames[MINECRAFT_NET_MAX_PLAYERS]; + + UIControl_Button m_buttonGameOptions; + UIControl_PlayerList m_playerList; + UIControl_Label m_labelTitle; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonGameOptions, "GameOptions") + UI_MAP_ELEMENT( m_playerList, "GamePlayers") + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_InGameInfoMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_InGameInfoMenu;} + virtual void updateTooltips(); + + virtual void handleReload(); + + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + virtual void handleGainFocus(bool navBack); + void handlePress(F64 controlId, F64 childId); + virtual void handleDestroy(); + virtual void handleFocusChange(F64 controlId, F64 childId); + +public: + static int KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + +private: +#if defined(__PS3__) || defined (__PSVITA__) || defined(__ORBIS__) + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ViewInvites_SignInReturned(void *pParam,bool bContinue, int iPad); +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.cpp new file mode 100644 index 0000000..6eb22b0 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.cpp @@ -0,0 +1,438 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_InGamePlayerOptionsMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + + +#define CHECKBOXES_TIMER_ID 0 +#define CHECKBOXES_TIMER_TIME 100 + +UIScene_InGamePlayerOptionsMenu::UIScene_InGamePlayerOptionsMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bShouldNavBack = false; + + InGamePlayerOptionsInitData *initData = (InGamePlayerOptionsInitData *)_initData; + m_networkSmallId = initData->networkSmallId; + m_playerPrivileges = initData->playerPrivileges; + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + INetworkPlayer *editingPlayer = g_NetworkManager.GetPlayerBySmallId(m_networkSmallId); + + if(editingPlayer != NULL) + { + m_labelGamertag.init(editingPlayer->GetDisplayName()); + } + + bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + m_editingSelf = (localPlayer != NULL && localPlayer == editingPlayer); + + if( m_editingSelf || trustPlayers || editingPlayer->IsHost()) + { + removeControl( &m_checkboxes[eControl_BuildAndMine], true ); + removeControl( &m_checkboxes[eControl_UseDoorsAndSwitches], true ); + removeControl( &m_checkboxes[eControl_UseContainers], true ); + removeControl( &m_checkboxes[eControl_AttackPlayers], true ); + removeControl( &m_checkboxes[eControl_AttackAnimals], true ); + } + else + { + bool checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotMine)==0 && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotBuild)==0); + m_checkboxes[eControl_BuildAndMine].init( app.GetString(IDS_CAN_BUILD_AND_MINE), eControl_BuildAndMine, checked); + + checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches)!=0); + m_checkboxes[eControl_UseDoorsAndSwitches].init( app.GetString(IDS_CAN_USE_DOORS_AND_SWITCHES), eControl_UseDoorsAndSwitches, checked); + + checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanUseContainers)!=0); + m_checkboxes[eControl_UseContainers].init( app.GetString(IDS_CAN_OPEN_CONTAINERS), eControl_UseContainers, checked); + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotAttackPlayers)==0; + m_checkboxes[eControl_AttackPlayers].init( app.GetString(IDS_CAN_ATTACK_PLAYERS), eControl_AttackPlayers, checked); + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotAttackAnimals)==0; + m_checkboxes[eControl_AttackAnimals].init( app.GetString(IDS_CAN_ATTACK_ANIMALS), eControl_AttackAnimals, checked); + } + + if(m_editingSelf) + { +#if (defined(_CONTENT_PACKAGE) || defined(_FINAL_BUILD) && !defined(_DEBUG_MENUS_ENABLED)) + removeControl( &m_checkboxes[eControl_Op], true ); +#else + m_checkboxes[eControl_Op].init(L"DEBUG: Creative",eControl_Op,Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode)); +#endif + + removeControl( &m_buttonKick, true ); + removeControl( &m_checkboxes[eControl_CheatTeleport], true ); + + if(cheats) + { + bool canBeInvisible = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; + m_checkboxes[eControl_HostInvisible].SetEnable(canBeInvisible); + bool checked = canBeInvisible && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Invisible)!=0 && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Invulnerable)!=0); + m_checkboxes[eControl_HostInvisible].init( app.GetString(IDS_INVISIBLE), eControl_HostInvisible, checked); + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(inCreativeMode) + { + removeControl( &m_checkboxes[eControl_HostFly], true ); + removeControl( &m_checkboxes[eControl_HostHunger], true ); + } + else + { + bool canFly = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly); + bool canChangeHunger = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger); + + m_checkboxes[eControl_HostFly].SetEnable(canFly); + checked = canFly && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanFly)!=0; + m_checkboxes[eControl_HostFly].init( app.GetString(IDS_CAN_FLY), eControl_HostFly, checked); + + m_checkboxes[eControl_HostHunger].SetEnable(canChangeHunger); + checked = canChangeHunger && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_ClassicHunger)!=0; + m_checkboxes[eControl_HostHunger].init( app.GetString(IDS_DISABLE_EXHAUSTION), eControl_HostHunger, checked); + } + } + else + { + removeControl( &m_checkboxes[eControl_HostInvisible], true ); + removeControl( &m_checkboxes[eControl_HostFly], true ); + removeControl( &m_checkboxes[eControl_HostHunger], true ); + } + } + else + { + if(localPlayer->IsHost()) + { + // Only host can make people moderators, or enable teleporting for them + m_checkboxes[eControl_Op].init( app.GetString(IDS_MODERATOR), eControl_Op, Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Op)!=0); + } + else + { + removeControl( &m_checkboxes[eControl_Op], true ); + } + + /*if(localPlayer->IsHost() && cheats ) + { + m_checkboxes[eControl_HostInvisible].SetEnable(true); + bool checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible)!=0; + m_checkboxes[eControl_HostInvisible].init( app.GetString(IDS_CAN_INVISIBLE), eControl_HostInvisible, checked); + + m_checkboxes[eControl_HostFly].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleFly)!=0; + m_checkboxes[eControl_HostFly].init( app.GetString(IDS_CAN_FLY), eControl_HostFly, checked); + + m_checkboxes[eControl_HostHunger].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleClassicHunger)!=0; + m_checkboxes[eControl_HostHunger].init( app.GetString(IDS_CAN_DISABLE_EXHAUSTION), eControl_HostHunger, checked); + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanTeleport)!=0; + m_checkboxes[eControl_CheatTeleport].init(app.GetString(IDS_ENABLE_TELEPORT),eControl_CheatTeleport,checked); + } + else + { + removeControl( &m_checkboxes[eControl_HostInvisible], true ); + removeControl( &m_checkboxes[eControl_HostFly], true ); + removeControl( &m_checkboxes[eControl_HostHunger], true ); + removeControl( &m_checkboxes[eControl_CheatTeleport], true ); + }*/ + + if(localPlayer->IsHost() && cheats ) + { + m_checkboxes[eControl_HostInvisible].SetEnable(true); + bool checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible)!=0; + m_checkboxes[eControl_HostInvisible].init( app.GetString(IDS_CAN_INVISIBLE), eControl_HostInvisible, checked); + + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(inCreativeMode) + { + removeControl( &m_checkboxes[eControl_HostFly], true ); + removeControl( &m_checkboxes[eControl_HostHunger], true ); + } + else + { + m_checkboxes[eControl_HostFly].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleFly)!=0; + m_checkboxes[eControl_HostFly].init( app.GetString(IDS_CAN_FLY), eControl_HostFly, checked); + + m_checkboxes[eControl_HostHunger].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleClassicHunger)!=0; + m_checkboxes[eControl_HostHunger].init( app.GetString(IDS_CAN_DISABLE_EXHAUSTION), eControl_HostHunger, checked); + } + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanTeleport)!=0; + m_checkboxes[eControl_CheatTeleport].init(app.GetString(IDS_ENABLE_TELEPORT),eControl_CheatTeleport,checked); + } + else + { + removeControl( &m_checkboxes[eControl_HostInvisible], true ); + removeControl( &m_checkboxes[eControl_HostFly], true ); + removeControl( &m_checkboxes[eControl_HostHunger], true ); + removeControl( &m_checkboxes[eControl_CheatTeleport], true ); + } + + + // Can only kick people if they are not local, and not local to the host + if(editingPlayer->IsLocal() != TRUE && editingPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + m_buttonKick.init( app.GetString(IDS_KICK_PLAYER), eControl_Kick); + } + else + { + removeControl( &m_buttonKick, true ); + } + } + + short colourIndex = app.GetPlayerColour( m_networkSmallId ); + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = colourIndex; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetPlayerIcon , 1 , value ); + +#if TO_BE_IMPLEMENTED + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } +#endif + + m_bModeratorState = m_checkboxes[eControl_Op].IsChecked(); + + resetCheatCheckboxes(); + + addTimer(CHECKBOXES_TIMER_ID,CHECKBOXES_TIMER_TIME); + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &UIScene_InGamePlayerOptionsMenu::OnPlayerChanged, this); + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} + +wstring UIScene_InGamePlayerOptionsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"InGamePlayerOptionsSplit"; + } + else + { + return L"InGamePlayerOptions"; + } +} + +void UIScene_InGamePlayerOptionsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_InGamePlayerOptionsMenu::tick() +{ + UIScene::tick(); + + if(m_bShouldNavBack) + { + m_bShouldNavBack = false; + ui.NavigateBack(m_iPad); + } +} + +void UIScene_InGamePlayerOptionsMenu::handleDestroy() +{ + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &UIScene_InGamePlayerOptionsMenu::OnPlayerChanged, this); +} + +void UIScene_InGamePlayerOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + if(m_editingSelf) + { +#if (defined(_CONTENT_PACKAGE) || defined(_FINAL_BUILD) && !defined(_DEBUG_MENUS_ENABLED)) +#else + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode,m_checkboxes[eControl_Op].IsChecked()); +#endif + if(cheats) + { + bool canBeInvisible = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; + if(canBeInvisible) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Invisible,m_checkboxes[eControl_HostInvisible].IsChecked()); + if(canBeInvisible) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Invulnerable,m_checkboxes[eControl_HostInvisible].IsChecked()); + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(!inCreativeMode) + { + bool canFly = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly); + bool canChangeHunger = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger); + + if(canFly) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanFly,m_checkboxes[eControl_HostFly].IsChecked()); + if(canChangeHunger) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_ClassicHunger,m_checkboxes[eControl_HostHunger].IsChecked()); + } + } + } + else + { + INetworkPlayer *editingPlayer = g_NetworkManager.GetPlayerBySmallId(m_networkSmallId); + if(!trustPlayers && (editingPlayer != NULL && !editingPlayer->IsHost() ) ) + { + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotMine,!m_checkboxes[eControl_BuildAndMine].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotBuild,!m_checkboxes[eControl_BuildAndMine].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackPlayers,!m_checkboxes[eControl_AttackPlayers].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackAnimals, !m_checkboxes[eControl_AttackAnimals].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches, m_checkboxes[eControl_UseDoorsAndSwitches].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseContainers, m_checkboxes[eControl_UseContainers].IsChecked()); + } + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + + if(localPlayer->IsHost()) + { + if(cheats) + { + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleInvisible,m_checkboxes[eControl_HostInvisible].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly,m_checkboxes[eControl_HostFly].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger,m_checkboxes[eControl_HostHunger].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport,m_checkboxes[eControl_CheatTeleport].IsChecked()); + } + + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Op,m_checkboxes[eControl_Op].IsChecked()); + } + } + unsigned int originalPrivileges = app.GetPlayerPrivileges(m_networkSmallId); + if(originalPrivileges != m_playerPrivileges) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player->connection) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( m_networkSmallId, -1, m_playerPrivileges) ) ); + } + } + navigateBack(); + + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_InGamePlayerOptionsMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Kick: + { + BYTE *smallId = new BYTE(); + *smallId = m_networkSmallId; + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_UNLOCK_KICK_PLAYER_TITLE, IDS_UNLOCK_KICK_PLAYER, uiIDA, 2, m_iPad,&UIScene_InGamePlayerOptionsMenu::KickPlayerReturned,smallId,app.GetStringTable(),NULL,0,false); + } + break; + }; +} + +int UIScene_InGamePlayerOptionsMenu::KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + BYTE smallId = *(BYTE *)pParam; + delete pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[iPad]; + if(localPlayer->connection) + { + localPlayer->connection->send( shared_ptr( new KickPlayerPacket(smallId) ) ); + } + + // Fix for #61494 - [CRASH]: TU7: Code: Multiplayer: Title may crash while kicking a player from an online game. + // We cannot do a navigate back here is this actually occurs on a thread other than the main thread. On rare occasions this can clash + // with the XUI render and causes a crash. The OnPlayerChanged event should perform the navigate back on the main thread + //app.NavigateBack(iPad); + } + + return 0; +} + +void UIScene_InGamePlayerOptionsMenu::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + app.DebugPrintf("UIScene_InGamePlayerOptionsMenu::OnPlayerChanged"); + UIScene_InGamePlayerOptionsMenu *scene = (UIScene_InGamePlayerOptionsMenu *)callbackParam; + + UIScene_InGameInfoMenu *infoScene = (UIScene_InGameInfoMenu *)scene->getBackScene(); + if(infoScene != NULL) UIScene_InGameInfoMenu::OnPlayerChanged(infoScene,pPlayer,leaving); + + if(leaving && pPlayer != NULL && pPlayer->GetSmallId() == scene->m_networkSmallId) + { + scene->m_bShouldNavBack = true; + } +} + +void UIScene_InGamePlayerOptionsMenu::resetCheatCheckboxes() +{ + bool isModerator = m_checkboxes[eControl_Op].IsChecked(); + //bool cheatsEnabled = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + + if (!m_editingSelf) + { + m_checkboxes[eControl_HostInvisible].SetEnable(isModerator); + m_checkboxes[eControl_HostFly].SetEnable(isModerator); + m_checkboxes[eControl_HostHunger].SetEnable(isModerator); + m_checkboxes[eControl_CheatTeleport].SetEnable(isModerator); + } +} + +void UIScene_InGamePlayerOptionsMenu::handleCheckboxToggled(F64 controlId, bool selected) +{ + switch((int)controlId) + { + case eControl_Op: + // flag that the moderator state has changed + //resetCheatCheckboxes(); + break; + } +} + +void UIScene_InGamePlayerOptionsMenu::handleTimerComplete(int id) +{ + switch(id) + { + case CHECKBOXES_TIMER_ID: + { + bool bIsModerator = m_checkboxes[eControl_Op].IsChecked(); + if(m_bModeratorState!=bIsModerator) + { + m_bModeratorState=bIsModerator; + resetCheatCheckboxes(); + } + } + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.h new file mode 100644 index 0000000..78e30f6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGamePlayerOptionsMenu.h @@ -0,0 +1,90 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_InGamePlayerOptionsMenu : public UIScene +{ +private: + enum EControls + { + // Checkboxes + eControl_BuildAndMine, + eControl_UseDoorsAndSwitches, + eControl_UseContainers, + eControl_AttackPlayers, + eControl_AttackAnimals, + eControl_Op, + eControl_CheatTeleport, + eControl_HostFly, + eControl_HostHunger, + eControl_HostInvisible, + + eControl_CHECKBOXES_COUNT, + + // Others + eControl_Kick = eControl_CHECKBOXES_COUNT, + }; + + bool m_bShouldNavBack; + bool m_editingSelf; + BYTE m_networkSmallId; + unsigned int m_playerPrivileges; + + UIControl_Label m_labelGamertag; + UIControl_CheckBox m_checkboxes[eControl_CHECKBOXES_COUNT]; + UIControl_Button m_buttonKick; + IggyName m_funcSetPlayerIcon; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_checkboxes[eControl_BuildAndMine], "CheckboxBuildAndMine") + UI_MAP_ELEMENT( m_checkboxes[eControl_UseDoorsAndSwitches], "CheckboxUseDoorsAndSwitches") + UI_MAP_ELEMENT( m_checkboxes[eControl_UseContainers], "CheckboxUseContainers") + UI_MAP_ELEMENT( m_checkboxes[eControl_AttackPlayers], "CheckboxAttackPlayers") + UI_MAP_ELEMENT( m_checkboxes[eControl_AttackAnimals], "CheckboxAttackAnimals") + UI_MAP_ELEMENT( m_checkboxes[eControl_Op], "CheckboxOp") + UI_MAP_ELEMENT( m_checkboxes[eControl_CheatTeleport], "CheckboxTeleport") + UI_MAP_ELEMENT( m_checkboxes[eControl_HostFly], "CheckboxHostFly") + UI_MAP_ELEMENT( m_checkboxes[eControl_HostHunger], "CheckboxHostHunger") + UI_MAP_ELEMENT( m_checkboxes[eControl_HostInvisible], "CheckboxHostInvisible") + + UI_MAP_ELEMENT( m_buttonKick, "ButtonKick") + + UI_MAP_ELEMENT( m_labelGamertag, "Gamertag") + + UI_MAP_NAME( m_funcSetPlayerIcon, L"SetPlayerIcon" ); + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bModeratorState; + + +public: + UIScene_InGamePlayerOptionsMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_InGamePlayerOptionsMenu;} + virtual void updateTooltips(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + virtual void handleCheckboxToggled(F64 controlId, bool selected); + virtual void handleTimerComplete(int id); + +public: + virtual void tick(); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleDestroy(); + virtual void handlePress(F64 controlId, F64 childId); + + + static int KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + +private: + /** 4J-JEV: + For enabling/disabling 'Can Fly', 'Can Teleport', 'Can Disable Hunger' etc + used after changing the moderator checkbox. + */ + void resetCheatCheckboxes(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.cpp new file mode 100644 index 0000000..b0dbc59 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.cpp @@ -0,0 +1,497 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_InGameSaveManagementMenu.h" + +#if defined(__ORBIS__) || defined(__PSVITA__) +#include +#endif + +int UIScene_InGameSaveManagementMenu::LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes) +{ + UIScene_InGameSaveManagementMenu *pClass= (UIScene_InGameSaveManagementMenu *)lpParam; + + app.DebugPrintf("Received data for save thumbnail\n"); + + if(pbThumbnail && dwThumbnailBytes) + { + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = new BYTE[dwThumbnailBytes]; + memcpy(pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData, pbThumbnail, dwThumbnailBytes); + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = dwThumbnailBytes; + } + else + { + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = NULL; + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = 0; + app.DebugPrintf("Save thumbnail data is NULL, or has size 0\n"); + } + pClass->m_bSaveThumbnailReady = true; + + return 0; +} + +UIScene_InGameSaveManagementMenu::UIScene_InGameSaveManagementMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_iRequestingThumbnailId = 0; + m_iSaveInfoC=0; + m_bIgnoreInput = false; + m_iState=e_SavesIdle; + //m_bRetrievingSaveInfo=false; + + m_buttonListSaves.init(eControl_SavesList); + + m_labelSavesListTitle.init( app.GetString(IDS_SAVE_INCOMPLETE_DELETE_SAVES) ); + m_controlSavesTimer.setVisible( true ); + + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + m_spaceIndicatorSaves.init(L"",eControl_SpaceIndicator,0, (4LL *1024LL * 1024LL * 1024LL) ); +#endif + m_bUpdateSaveSize = false; + + m_bAllLoaded = false; + m_bRetrievingSaveThumbnails = false; + m_bSaveThumbnailReady = false; + m_bExitScene=false; + m_pSaveDetails=NULL; + m_bSavesDisplayed=false; + m_saveDetails = NULL; + m_iSaveDetailsCount = 0; + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) + // Always clear the saves when we enter this menu + StorageManager.ClearSavesInfo(); +#endif + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true || app.DLCInstallPending()) + { + // if we're waiting for DLC to mount, don't fill the save list. The custom message on end of dlc mounting will do that + m_bIgnoreInput = true; + } + else + { + Initialise(); + } + +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() && SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + { + g_NetworkManager.startAdhocMatching(); // create the client matching context and clear out the friends list + } + +#endif + + // If we're not ignoring input, then we aren't still waiting for the DLC to mount, and can now check for corrupt dlc. Otherwise this will happen when the dlc has finished mounting. + if( !m_bIgnoreInput) + { + app.m_dlcManager.checkForCorruptDLCAndAlert(); + } + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); +} + + +UIScene_InGameSaveManagementMenu::~UIScene_InGameSaveManagementMenu() +{ + m_parentLayer->removeComponent(eUIComponent_MenuBackground); + + if(m_saveDetails) + { + for(int i = 0; i < m_iSaveDetailsCount; ++i) + { + delete m_saveDetails[i].pbThumbnailData; + } + delete [] m_saveDetails; + } + app.LeaveSaveNotificationSection(); + StorageManager.SetSaveDisabled(false); + StorageManager.ContinueIncompleteOperation(); +} + +void UIScene_InGameSaveManagementMenu::updateTooltips() +{ + int iA = -1; + if( m_bSavesDisplayed && m_iSaveDetailsCount > 0) + { + iA = IDS_TOOLTIPS_DELETESAVE; + } + ui.SetTooltips( m_parentLayer->IsFullscreenGroup()?XUSER_INDEX_ANY:m_iPad, iA, IDS_SAVE_INCOMPLETE_RETRY_SAVING); +} + +// +void UIScene_InGameSaveManagementMenu::Initialise() +{ + m_iSaveListIndex = 0; + + // Check if we're in the trial version + if(ProfileManager.IsFullVersion()==false) + { + } + else if(StorageManager.GetSaveDisabled()) + { + GetSaveInfo(); + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame(); + + GetSaveInfo(); + } + + m_bIgnoreInput=false; +} + +void UIScene_InGameSaveManagementMenu::handleReload() +{ + m_bIgnoreInput = false; + m_iRequestingThumbnailId = 0; + m_bAllLoaded=false; + m_bRetrievingSaveThumbnails=false; + m_bSavesDisplayed=false; + m_iSaveInfoC=0; +} + +void UIScene_InGameSaveManagementMenu::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + + updateTooltips(); + + if(navBack) + { + // re-enable button presses + m_bIgnoreInput=false; + } +} + +wstring UIScene_InGameSaveManagementMenu::getMoviePath() +{ + return L"SaveMenu"; +} + +void UIScene_InGameSaveManagementMenu::tick() +{ + UIScene::tick(); + + if(m_bExitScene) // navigate forward or back + { + if(!m_bRetrievingSaveThumbnails) + { + // need to wait for any callback retrieving thumbnail to complete + navigateBack(); + } + } + // Stop loading thumbnails if we navigate forwards + if(hasFocus(m_iPad)) + { + if(m_bUpdateSaveSize) + { + m_spaceIndicatorSaves.selectSave(m_iSaveListIndex); + m_bUpdateSaveSize = false; + } + + // Display the saves if we have them + if(!m_bSavesDisplayed) + { + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + if(m_pSaveDetails!=NULL) + { + m_spaceIndicatorSaves.reset(); + + m_bSavesDisplayed=true; + + if(m_saveDetails!=NULL) + { + for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i) + { + if(m_saveDetails[i].pbThumbnailData!=NULL) + { + delete m_saveDetails[i].pbThumbnailData; + } + } + delete m_saveDetails; + } + m_saveDetails = new SaveListDetails[m_pSaveDetails->iSaveC]; + + m_iSaveDetailsCount = m_pSaveDetails->iSaveC; + for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i) + { +#if defined(_XBOX_ONE) + m_spaceIndicatorSaves.addSave( m_pSaveDetails->SaveInfoA[i].totalSize ); +#elif defined(__ORBIS__) + m_spaceIndicatorSaves.addSave( m_pSaveDetails->SaveInfoA[i].blocksUsed * (32 * 1024) ); +#endif +#ifdef _DURANGO + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, L""); + + m_saveDetails[i].saveId = i; + memcpy(m_saveDetails[i].UTF16SaveName, m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, 128); + memcpy(m_saveDetails[i].UTF16SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#else + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, L""); + + m_saveDetails[i].saveId = i; + memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, 128); + memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); +#endif + } + m_controlSavesTimer.setVisible( false ); + + // set focus on the first button + + } + } + + if(!m_bExitScene && m_bSavesDisplayed && !m_bRetrievingSaveThumbnails && !m_bAllLoaded) + { + if( m_iRequestingThumbnailId < (m_buttonListSaves.getItemCount() )) + { + m_bRetrievingSaveThumbnails = true; + app.DebugPrintf("Requesting the first thumbnail\n"); + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + { + // something went wrong + m_bRetrievingSaveThumbnails=false; + m_bAllLoaded = true; + } + } + } + else if (m_bSavesDisplayed && m_bSaveThumbnailReady) + { + m_bSaveThumbnailReady = false; + + // check we're not waiting to exit the scene + if(!m_bExitScene) + { + // convert to utf16 + uint16_t u16Message[MAX_SAVEFILENAME_LENGTH]; +#ifdef _DURANGO + // Already utf16 on durango + memcpy(u16Message, m_saveDetails[m_iRequestingThumbnailId].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#elif defined(_WINDOWS64) + int result = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + MB_ERR_INVALID_CHARS, // error on invalid chars + m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename, // source UTF-8 string + MAX_SAVEFILENAME_LENGTH, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + (wchar_t *)u16Message, // destination buffer + MAX_SAVEFILENAME_LENGTH // size of destination buffer, in WCHAR's + ); +#else +#ifdef __PS3 + size_t srcmax,dstmax; +#else + uint32_t srcmax,dstmax; + uint32_t srclen,dstlen; +#endif + srcmax=MAX_SAVEFILENAME_LENGTH; + dstmax=MAX_SAVEFILENAME_LENGTH; + +#if defined(__PS3__) + L10nResult lres= UTF8stoUTF16s((uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,&srcmax,u16Message,&dstmax); +#else + SceCesUcsContext context; + sceCesUcsContextInit(&context); + + sceCesUtf8StrToUtf16Str(&context, (uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,srcmax,&srclen,u16Message,dstmax,&dstlen); +#endif +#endif + if( m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData ) + { + registerSubstitutionTexture((wchar_t *)u16Message,m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData,m_saveDetails[m_iRequestingThumbnailId].dwThumbnailSize); + } + m_buttonListSaves.setTextureName(m_iRequestingThumbnailId, (wchar_t *)u16Message); + + ++m_iRequestingThumbnailId; + if( m_iRequestingThumbnailId < (m_buttonListSaves.getItemCount() )) + { + app.DebugPrintf("Requesting another thumbnail\n"); + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + { + // something went wrong + m_bRetrievingSaveThumbnails=false; + m_bAllLoaded = true; + } + } + else + { + m_bRetrievingSaveThumbnails = false; + m_bAllLoaded = true; + } + } + else + { + // stop retrieving thumbnails, and exit + m_bRetrievingSaveThumbnails = false; + } + } + } + + switch(m_iState) + { + case e_SavesIdle: + break; + case e_SavesRepopulateAfterDelete: + m_bIgnoreInput = false; + m_iRequestingThumbnailId = 0; + m_bAllLoaded=false; + m_bRetrievingSaveThumbnails=false; + m_bSavesDisplayed=false; + m_iSaveInfoC=0; + m_buttonListSaves.clearList(); + //StorageManager.ClearSavesInfo(); + //GetSaveInfo(); + m_iState=e_SavesIdle; + break; + } +} + +void UIScene_InGameSaveManagementMenu::GetSaveInfo( ) +{ + unsigned int uiSaveC=0; + + // This will return with the number retrieved in uiSaveC + + // clear the saves list + m_bSavesDisplayed = false; // we're blocking the exit from this scene until complete + m_buttonListSaves.clearList(); + m_iSaveInfoC=0; + m_controlSavesTimer.setVisible(true); + + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + if(m_pSaveDetails==NULL) + { + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(m_iPad,NULL,this,"save"); + } + + + return; +} + +void UIScene_InGameSaveManagementMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + // if we're retrieving save info, ignore key presses + if(!m_bSavesDisplayed) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + m_bExitScene=true; +#else + navigateBack(); +#endif + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +void UIScene_InGameSaveManagementMenu::handleInitFocus(F64 controlId, F64 childId) +{ + app.DebugPrintf(app.USER_SR, "UIScene_InGameSaveManagementMenu::handleInitFocus - %d , %d\n", (int)controlId, (int)childId); +} + +void UIScene_InGameSaveManagementMenu::handleFocusChange(F64 controlId, F64 childId) +{ + app.DebugPrintf(app.USER_SR, "UIScene_InGameSaveManagementMenu::handleFocusChange - %d , %d\n", (int)controlId, (int)childId); + m_iSaveListIndex = childId; + if(m_bSavesDisplayed) m_bUpdateSaveSize = true; + updateTooltips(); +} + +void UIScene_InGameSaveManagementMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_SavesList: + { + m_bIgnoreInput = true; + + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2,m_iPad,&UIScene_InGameSaveManagementMenu::DeleteSaveDialogReturned,this, app.GetStringTable(),NULL,0,true); + + ui.PlayUISFX(eSFX_Press); + break; + } + } +} + +int UIScene_InGameSaveManagementMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_InGameSaveManagementMenu* pClass = (UIScene_InGameSaveManagementMenu*)pParam; + // results switched for this dialog + + if(result==C4JStorage::EMessage_ResultDecline) + { + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + pClass->m_bIgnoreInput=false; + } + else + { + StorageManager.DeleteSaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex],UIScene_InGameSaveManagementMenu::DeleteSaveDataReturned,pClass); + pClass->m_controlSavesTimer.setVisible( true ); + } + } + else + { + pClass->m_bIgnoreInput=false; + } + + return 0; +} + +int UIScene_InGameSaveManagementMenu::DeleteSaveDataReturned(LPVOID lpParam,bool bRes) +{ + UIScene_InGameSaveManagementMenu* pClass = (UIScene_InGameSaveManagementMenu*)lpParam; + + if(bRes) + { + // wipe the list and repopulate it + pClass->m_iState=e_SavesRepopulateAfterDelete; + } + else pClass->m_bIgnoreInput=false; + + pClass->updateTooltips(); + + return 0; +} + +bool UIScene_InGameSaveManagementMenu::hasFocus(int iPad) +{ + return bHasFocus && (iPad == m_iPad || m_iPad == XUSER_INDEX_ANY); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.h b/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.h new file mode 100644 index 0000000..3f9ace3 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InGameSaveManagementMenu.h @@ -0,0 +1,104 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_InGameSaveManagementMenu : public UIScene +{ +private: + enum EControls + { + eControl_SavesList, +#if defined(_XBOX_ONE) || defined(__ORBIS__) + eControl_SpaceIndicator, +#endif + }; + + enum EState + { + e_SavesIdle, + e_SavesRepopulate, + e_SavesRepopulateAfterDelete + }; + + static const int JOIN_LOAD_CREATE_BUTTON_INDEX = 0; + + SaveListDetails *m_saveDetails; + int m_iSaveDetailsCount; + +protected: + UIControl_SaveList m_buttonListSaves; + UIControl_Label m_labelSavesListTitle; + UIControl m_controlSavesTimer; +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UIControl_SpaceIndicatorBar m_spaceIndicatorSaves; +#endif + +private: + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListSaves, "SavesList") + + UI_MAP_ELEMENT( m_labelSavesListTitle, "SavesListTitle") + + UI_MAP_ELEMENT( m_controlSavesTimer, "SavesTimer") + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UI_MAP_ELEMENT( m_spaceIndicatorSaves, "SaveSizeBar") +#endif + UI_END_MAP_ELEMENTS_AND_NAMES() + + int m_iState; + + vector *m_saves; + + bool m_bIgnoreInput; + bool m_bAllLoaded; + bool m_bRetrievingSaveThumbnails; + bool m_bSaveThumbnailReady; + int m_iRequestingThumbnailId; + SAVE_DETAILS *m_pSaveDetails; + bool m_bSavesDisplayed; + bool m_bExitScene; + int m_iSaveInfoC; + int m_iSaveListIndex; + //int *m_iConfigA; // track the texture packs that we don't have installed + + bool m_bUpdateSaveSize; + +public: + UIScene_InGameSaveManagementMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_InGameSaveManagementMenu(); + + virtual void updateTooltips(); + + virtual void handleReload(); + virtual void handleGainFocus(bool navBack); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleInitFocus(F64 controlId, F64 childId); + + virtual EUIScene getSceneType() { return eUIScene_LoadOrJoinMenu;} + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return true; } + + virtual bool hasFocus(int iPad); + + virtual void tick(); + +private: + void Initialise(); + void GetSaveInfo(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + + static int LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes); + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes); +protected: + void handlePress(F64 controlId, F64 childId); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_Intro.cpp b/Minecraft.Client/Common/UI/UIScene_Intro.cpp new file mode 100644 index 0000000..2c50612 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Intro.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_Intro.h" + + +UIScene_Intro::UIScene_Intro(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + m_bIgnoreNavigate = false; + m_bAnimationEnded = false; + + bool bSkipESRB = false; +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + bSkipESRB = app.GetProductSKU() != e_sku_SCEA; +#elif defined(_XBOX) || defined(_DURANGO) + bSkipESRB = !ProfileManager.LocaleIsUSorCanada(); +#endif + + // 4J Stu - These map to values in the Actionscript +#ifdef _WINDOWS64 + int platformIdx = 0; +#elif defined(_XBOX) + int platformIdx = 1; +#elif defined(_DURANGO) + int platformIdx = 2; +#elif defined(__PS3__) + int platformIdx = 3; +#elif defined(__ORBIS__) + int platformIdx = 4; +#elif defined(__PSVITA__) + int platformIdx = 5; +#endif + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = platformIdx; + + value[1].type = IGGY_DATATYPE_boolean; + value[1].boolval = bSkipESRB; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetIntroPlatform , 2 , value ); + +#ifdef __PSVITA__ + // initialise vita touch controls with ids + m_TouchToSkip.init(0); +#endif +} + +wstring UIScene_Intro::getMoviePath() +{ + return L"Intro"; +} + +void UIScene_Intro::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(!m_bIgnoreNavigate) + { + m_bIgnoreNavigate = true; + //ui.NavigateToHomeMenu(); +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + + // has the user seen the EULA already ? We need their options file loaded for this + C4JStorage::eOptionsCallback eStatus=app.GetOptionsCallbackStatus(0); + switch(eStatus) + { + case C4JStorage::eOptions_Callback_Read: + case C4JStorage::eOptions_Callback_Read_FileNotFound: + // we've either read it, or it wasn't found + if(app.GetGameSettings(0,eGameSetting_PS3_EULA_Read)==0) + { + ui.NavigateToScene(0,eUIScene_EULA); + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } + break; + default: + ui.NavigateToScene(0,eUIScene_EULA); + break; + } +#elif defined _XBOX_ONE + ui.NavigateToScene(0,eUIScene_MainMenu); +#else + ui.NavigateToScene(0,eUIScene_SaveMessage); +#endif + } + break; + } +} + +#ifdef __PSVITA__ +void UIScene_Intro::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) +{ + if(bReleased) + { + bool handled = false; + handleInput(iPad, ACTION_MENU_OK, false, true, false, handled); + } +} +#endif + +void UIScene_Intro::handleAnimationEnd() +{ + if(!m_bIgnoreNavigate) + { + m_bIgnoreNavigate = true; + //ui.NavigateToHomeMenu(); +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // has the user seen the EULA already ? We need their options file loaded for this + C4JStorage::eOptionsCallback eStatus=app.GetOptionsCallbackStatus(0); + switch(eStatus) + { + case C4JStorage::eOptions_Callback_Read: + case C4JStorage::eOptions_Callback_Read_FileNotFound: + // we've either read it, or it wasn't found + if(app.GetGameSettings(0,eGameSetting_PS3_EULA_Read)==0) + { + ui.NavigateToScene(0,eUIScene_EULA); + } + else + { + ui.NavigateToScene(0,eUIScene_SaveMessage); + } + break; + default: + ui.NavigateToScene(0,eUIScene_EULA); + break; + } + + +#elif defined _XBOX_ONE + // Don't navigate to the main menu if we don't have focus, as we could have the quadrant sign-in or a join game timer screen running, and then when Those finish they'll + // give the main menu focus which clears the signed in players and therefore breaks transitioning into the game + if( hasFocus( m_iPad ) ) + { + ui.NavigateToScene(0,eUIScene_MainMenu); + } + else + { + m_bAnimationEnded = true; + } +#else + ui.NavigateToScene(0,eUIScene_SaveMessage); +#endif + } +} + +void UIScene_Intro::handleGainFocus(bool navBack) +{ + // Only relevant on xbox one - if we didn't navigate to the main menu at animation end due to the timer or quadrant sign-in being up, then we'll need to + // do it now in case the user has cancelled or joining a game failed + if( m_bAnimationEnded ) + { + ui.NavigateToScene(0,eUIScene_MainMenu); + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_Intro.h b/Minecraft.Client/Common/UI/UIScene_Intro.h new file mode 100644 index 0000000..8bdc030 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Intro.h @@ -0,0 +1,52 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_Intro : public UIScene +{ +private: + bool m_bIgnoreNavigate; + bool m_bAnimationEnded; + + IggyName m_funcSetIntroPlatform; +#ifdef __PSVITA__ + UIControl_Touch m_TouchToSkip; +#endif + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) +#ifdef __PSVITA__ + UI_MAP_ELEMENT( m_TouchToSkip, "TouchToSkip" ) +#endif + UI_MAP_NAME( m_funcSetIntroPlatform, L"SetIntroPlatform") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_Intro(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_Intro;} + + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + +protected: + + + virtual wstring getMoviePath(); + +#ifdef _DURANGO + virtual long long getDefaultGtcButtons() { return 0; } +#endif + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleAnimationEnd(); + virtual void handleGainFocus(bool navBack); + +#ifdef __PSVITA__ + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); +#endif + +}; diff --git a/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp new file mode 100644 index 0000000..723937d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.cpp @@ -0,0 +1,334 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_InventoryMenu.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\EntityRenderDispatcher.h" +#include "..\..\Lighting.h" +#include "..\Tutorial\Tutorial.h" +#include "..\Tutorial\TutorialMode.h" +#include "..\Tutorial\TutorialEnum.h" + +#define INVENTORY_UPDATE_EFFECTS_TIMER_ID (10) +#define INVENTORY_UPDATE_EFFECTS_TIMER_TIME (1000) // 1 second + +UIScene_InventoryMenu::UIScene_InventoryMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + InventoryScreenInput *initData = (InventoryScreenInput *)_initData; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Inventory_Menu, this); + } + + InventoryMenu *menu = (InventoryMenu *)initData->player->inventoryMenu; + + initData->player->awardStat(GenericStats::openInventory(),GenericStats::param_openInventory()); + + Initialize( initData->iPad, menu, false, InventoryMenu::INV_SLOT_START, eSectionInventoryUsing, eSectionInventoryMax, initData->bNavigateBack ); + + m_slotListArmor.addSlots(InventoryMenu::ARMOR_SLOT_START, InventoryMenu::ARMOR_SLOT_END - InventoryMenu::ARMOR_SLOT_START); + + if(initData) delete initData; + + for(unsigned int i = 0; i < MobEffect::NUM_EFFECTS; ++i) + { + m_bEffectTime[i] = 0; + } + + updateEffectsDisplay(); + addTimer(INVENTORY_UPDATE_EFFECTS_TIMER_ID,INVENTORY_UPDATE_EFFECTS_TIMER_TIME); +} + +wstring UIScene_InventoryMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"InventoryMenuSplit"; + } + else + { + return L"InventoryMenu"; + } +} + +void UIScene_InventoryMenu::handleReload() +{ + Initialize( m_iPad, m_menu, false, InventoryMenu::INV_SLOT_START, eSectionInventoryUsing, eSectionInventoryMax, m_bNavigateBack ); + + m_slotListArmor.addSlots(InventoryMenu::ARMOR_SLOT_START, InventoryMenu::ARMOR_SLOT_END - InventoryMenu::ARMOR_SLOT_START); + + for(unsigned int i = 0; i < MobEffect::NUM_EFFECTS; ++i) + { + m_bEffectTime[i] = 0; + } +} + +int UIScene_InventoryMenu::getSectionColumns(ESceneSection eSection) +{ + int cols = 0; + switch( eSection ) + { + case eSectionInventoryArmor: + cols = 1; + break; + case eSectionInventoryInventory: + cols = 9; + break; + case eSectionInventoryUsing: + cols = 9; + break; + default: + assert( false ); + break; + } + return cols; +} + +int UIScene_InventoryMenu::getSectionRows(ESceneSection eSection) +{ + int rows = 0; + switch( eSection ) + { + case eSectionInventoryArmor: + rows = 4; + break; + case eSectionInventoryInventory: + rows = 3; + break; + case eSectionInventoryUsing: + rows = 1; + break; + default: + assert( false ); + break; + } + return rows; +} + +void UIScene_InventoryMenu::GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ) +{ + switch( eSection ) + { + case eSectionInventoryArmor: + pPosition->x = m_slotListArmor.getXPos(); + pPosition->y = m_slotListArmor.getYPos(); + break; + case eSectionInventoryInventory: + pPosition->x = m_slotListInventory.getXPos(); + pPosition->y = m_slotListInventory.getYPos(); + break; + case eSectionInventoryUsing: + pPosition->x = m_slotListHotbar.getXPos(); + pPosition->y = m_slotListHotbar.getYPos(); + break; + default: + assert( false ); + break; + } +} + +void UIScene_InventoryMenu::GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ) +{ + UIVec2D sectionSize; + + switch( eSection ) + { + case eSectionInventoryArmor: + sectionSize.x = m_slotListArmor.getWidth(); + sectionSize.y = m_slotListArmor.getHeight(); + break; + case eSectionInventoryInventory: + sectionSize.x = m_slotListInventory.getWidth(); + sectionSize.y = m_slotListInventory.getHeight(); + break; + case eSectionInventoryUsing: + sectionSize.x = m_slotListHotbar.getWidth(); + sectionSize.y = m_slotListHotbar.getHeight(); + break; + default: + assert( false ); + break; + } + + int rows = getSectionRows(eSection); + int cols = getSectionColumns(eSection); + + pSize->x = sectionSize.x/cols; + pSize->y = sectionSize.y/rows; + + int itemCol = iItemIndex % cols; + int itemRow = iItemIndex/cols; + + pPosition->x = itemCol * pSize->x; + pPosition->y = itemRow * pSize->y; +} + +void UIScene_InventoryMenu::setSectionSelectedSlot(ESceneSection eSection, int x, int y) +{ + int cols = getSectionColumns(eSection); + + int index = (y * cols) + x; + + UIControl_SlotList *slotList = NULL; + switch( eSection ) + { + case eSectionInventoryArmor: + slotList = &m_slotListArmor; + break; + case eSectionInventoryInventory: + slotList = &m_slotListInventory; + break; + case eSectionInventoryUsing: + slotList = &m_slotListHotbar; + break; + } + + slotList->setHighlightSlot(index); +} + +UIControl *UIScene_InventoryMenu::getSection(ESceneSection eSection) +{ + UIControl *control = NULL; + switch( eSection ) + { + case eSectionInventoryArmor: + control = &m_slotListArmor; + break; + case eSectionInventoryInventory: + control = &m_slotListInventory; + break; + case eSectionInventoryUsing: + control = &m_slotListHotbar; + break; + } + return control; +} + +void UIScene_InventoryMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + if(wcscmp((wchar_t *)region->name,L"player")==0) + { + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + delete customDrawRegion; + + m_playerPreview.render(region); + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + } + else + { + UIScene_AbstractContainerMenu::customDraw(region); + } +} + +void UIScene_InventoryMenu::handleTimerComplete(int id) +{ + if(id == INVENTORY_UPDATE_EFFECTS_TIMER_ID) + { + updateEffectsDisplay(); + } +} + +void UIScene_InventoryMenu::updateEffectsDisplay() +{ + // Update with the current effects + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + + if(player == NULL) return; + + vector *activeEffects = player->getActiveEffects(); + + // 4J - TomK setup time update value array size to update the active effects + int iValue = 0; + IggyDataValue *UpdateValue = new IggyDataValue[activeEffects->size()*2]; + + for(AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it) + { + MobEffectInstance *effect = *it; + + if(effect->getDuration() >= m_bEffectTime[effect->getId()]) + { + wstring effectString = app.GetString( effect->getDescriptionId() );//I18n.get(effect.getDescriptionId()).trim(); + if (effect->getAmplifier() > 0) + { + wstring potencyString = L""; + switch(effect->getAmplifier()) + { + case 1: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_1 ); + break; + case 2: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_2 ); + break; + case 3: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_3 ); + break; + default: + potencyString = app.GetString( IDS_POTION_POTENCY_0 ); + break; + } + effectString += potencyString; + } + int icon = 0; + MobEffect *mobEffect = MobEffect::effects[effect->getId()]; + if (mobEffect->hasIcon()) + { + icon = mobEffect->getIcon(); + } + IggyDataValue result; + IggyDataValue value[3]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = icon; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)effectString.c_str(); + stringVal.length = effectString.length(); + value[1].type = IGGY_DATATYPE_string_UTF16; + value[1].string16 = stringVal; + + int seconds = effect->getDuration() / SharedConstants::TICKS_PER_SECOND; + value[2].type = IGGY_DATATYPE_number; + value[2].number = seconds; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAddEffect , 3 , value ); + } + + if(MobEffect::effects[effect->getId()]->hasIcon()) + { + // 4J - TomK set ids and remaining duration so we can update the timers accurately in one call! (this prevents performance related timer sync issues, especially on PSVita) + UpdateValue[iValue].type = IGGY_DATATYPE_number; + UpdateValue[iValue].number = MobEffect::effects[effect->getId()]->getIcon(); + UpdateValue[iValue + 1].type = IGGY_DATATYPE_number; + UpdateValue[iValue + 1].number = (int)(effect->getDuration() / SharedConstants::TICKS_PER_SECOND); + iValue+=2; + } + + m_bEffectTime[effect->getId()] = effect->getDuration(); + } + + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcUpdateEffects , activeEffects->size()*2 , UpdateValue ); + + delete activeEffects; +} diff --git a/Minecraft.Client/Common/UI/UIScene_InventoryMenu.h b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.h new file mode 100644 index 0000000..fb8d57a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_InventoryMenu.h @@ -0,0 +1,51 @@ +#pragma once + +#include "UIScene_AbstractContainerMenu.h" +#include "IUIScene_InventoryMenu.h" + +#include "..\..\..\Minecraft.World\MobEffect.h" + +class InventoryMenu; + +class UIScene_InventoryMenu : public UIScene_AbstractContainerMenu, public IUIScene_InventoryMenu +{ + friend class UIControl_MinecraftPlayer; +private: + int m_bEffectTime[MobEffect::NUM_EFFECTS]; +public: + UIScene_InventoryMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_InventoryMenu;} + +protected: + UIControl_SlotList m_slotListArmor; + UIControl_MinecraftPlayer m_playerPreview; + IggyName m_funcUpdateEffects, m_funcAddEffect; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene_AbstractContainerMenu) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListArmor, "armorList") + UI_MAP_ELEMENT( m_playerPreview, "iggy_player") + + UI_MAP_NAME( m_funcUpdateEffects, L"UpdateEffects") + UI_MAP_NAME( m_funcAddEffect, L"AddEffect") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void handleReload(); + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + virtual void handleSectionClick(ESceneSection eSection) {} + virtual void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + + virtual UIControl *getSection(ESceneSection eSection); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + virtual void handleTimerComplete(int id); + +private: + void updateEffectsDisplay(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp new file mode 100644 index 0000000..cad86dc --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp @@ -0,0 +1,586 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_JoinMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\Options.h" +#include "..\..\MinecraftServer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.h" + +#define UPDATE_PLAYERS_TIMER_ID 0 +#define UPDATE_PLAYERS_TIMER_TIME 30000 + +UIScene_JoinMenu::UIScene_JoinMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + JoinMenuInitData *initData = (JoinMenuInitData *)_initData; + m_selectedSession = initData->selectedSession; + m_friendInfoUpdatedOK = false; + m_friendInfoUpdatedERROR = false; + m_friendInfoRequestIssued = false; +} + +void UIScene_JoinMenu::updateTooltips() +{ + int iA = -1; + int iY = -1; + if (getControlFocus() == eControl_GamePlayers) + { +#ifdef _DURANGO + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; +#endif + } + else + { + iA = IDS_TOOLTIPS_SELECT; + } + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, iA, IDS_TOOLTIPS_BACK, -1, iY ); + +} + +void UIScene_JoinMenu::tick() +{ + if( !m_friendInfoRequestIssued ) + { + ui.NavigateToScene(m_iPad, eUIScene_Timer); + g_NetworkManager.GetFullFriendSessionInfo(m_selectedSession, &friendSessionUpdated, this); + m_friendInfoRequestIssued = true; + } + + if( m_friendInfoUpdatedOK ) + { + m_friendInfoUpdatedOK = false; + + m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME),eControl_JoinGame); + + m_buttonListPlayers.init(eControl_GamePlayers); + +#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ + for( int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++ ) + { + if( m_selectedSession->data.players[i] != NULL ) + { + #ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<data.players[i].getOnlineID()); + + #ifndef __PSVITA__ + // Append guest number (any players in an online game not signed into PSN are guests) + if( m_selectedSession->data.players[i].isSignedIntoPSN() == false ) + { + char suffix[5]; + sprintf(suffix, " (%d)", m_selectedSession->data.players[i].getQuadrant() + 1); + playerName.append(suffix); + } + #endif + m_buttonListPlayers.addItem(playerName); + } + } + else + { + // Leave the loop when we hit the first NULL player + break; + } + } +#elif defined(_DURANGO) + for( int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++ ) + { + if ( m_selectedSession->searchResult.m_playerNames[i].size() ) + { + m_buttonListPlayers.addItem(m_selectedSession->searchResult.m_playerNames[i]); + } + else + { + // Leave the loop when we hit the first empty player name + break; + } + } +#endif + + m_labelLabels[eLabel_Difficulty].init(app.GetString(IDS_LABEL_DIFFICULTY)); + m_labelLabels[eLabel_GameType].init(app.GetString(IDS_LABEL_GAME_TYPE)); + m_labelLabels[eLabel_GamertagsOn].init(app.GetString(IDS_LABEL_GAMERTAGS)); + m_labelLabels[eLabel_Structures].init(app.GetString(IDS_LABEL_STRUCTURES)); + m_labelLabels[eLabel_LevelType].init(app.GetString(IDS_LABEL_LEVEL_TYPE)); + m_labelLabels[eLabel_PVP].init(app.GetString(IDS_LABEL_PvP)); + m_labelLabels[eLabel_Trust].init(app.GetString(IDS_LABEL_TRUST)); + m_labelLabels[eLabel_TNTOn].init(app.GetString(IDS_LABEL_TNT)); + m_labelLabels[eLabel_FireOn].init(app.GetString(IDS_LABEL_FIRE_SPREADS)); + + unsigned int uiGameHostSettings = m_selectedSession->data.m_uiGameHostSettings; + switch(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Difficulty)) + { + case Difficulty::EASY: + m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_EASY) ); + break; + case Difficulty::NORMAL: + m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_NORMAL) ); + break; + case Difficulty::HARD: + m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_HARD) ); + break; + case Difficulty::PEACEFUL: + default: + m_labelValues[eLabel_Difficulty].init( app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL) ); + break; + } + + int option = app.GetGameHostOption(uiGameHostSettings,eGameHostOption_GameType); + if(option == GameType::CREATIVE->getId()) + { + m_labelValues[eLabel_GameType].init( app.GetString(IDS_CREATIVE) ); + } + else + { + m_labelValues[eLabel_GameType].init( app.GetString(IDS_SURVIVAL) ); + } + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Gamertags)) m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Structures)) m_labelValues[eLabel_Structures].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_Structures].init( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_LevelType)) m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_SUPERFLAT) ); + else m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_NORMAL) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_PvP))m_labelValues[eLabel_PVP].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_PVP].init( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TrustPlayers)) m_labelValues[eLabel_Trust].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_Trust].init( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TNT)) m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_FireSpreads)) m_labelValues[eLabel_FireOn].init( app.GetString(IDS_ON) ); + else m_labelValues[eLabel_FireOn].init( app.GetString(IDS_OFF) ); + + m_bIgnoreInput = false; + + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_JoinMenu, 0); + + addTimer(UPDATE_PLAYERS_TIMER_ID,UPDATE_PLAYERS_TIMER_TIME); + } + + if( m_friendInfoUpdatedERROR ) + { + m_buttonJoinGame.init(app.GetString(IDS_JOIN_GAME),eControl_JoinGame); + + m_buttonListPlayers.init(eControl_GamePlayers); + + m_labelLabels[eLabel_Difficulty].init(app.GetString(IDS_LABEL_DIFFICULTY)); + m_labelLabels[eLabel_GameType].init(app.GetString(IDS_LABEL_GAME_TYPE)); + m_labelLabels[eLabel_GamertagsOn].init(app.GetString(IDS_LABEL_GAMERTAGS)); + m_labelLabels[eLabel_Structures].init(app.GetString(IDS_LABEL_STRUCTURES)); + m_labelLabels[eLabel_LevelType].init(app.GetString(IDS_LABEL_LEVEL_TYPE)); + m_labelLabels[eLabel_PVP].init(app.GetString(IDS_LABEL_PvP)); + m_labelLabels[eLabel_Trust].init(app.GetString(IDS_LABEL_TRUST)); + m_labelLabels[eLabel_TNTOn].init(app.GetString(IDS_LABEL_TNT)); + m_labelLabels[eLabel_FireOn].init(app.GetString(IDS_LABEL_FIRE_SPREADS)); + + m_labelValues[eLabel_Difficulty].init(app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL)); + m_labelValues[eLabel_GameType].init( app.GetString(IDS_CREATIVE) ); + m_labelValues[eLabel_GamertagsOn].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_Structures].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_LevelType].init( app.GetString(IDS_LEVELTYPE_NORMAL) ); + m_labelValues[eLabel_PVP].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_Trust].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_TNTOn].init( app.GetString(IDS_OFF) ); + m_labelValues[eLabel_FireOn].init( app.GetString(IDS_OFF) ); + + m_friendInfoUpdatedERROR = false; + + // Show a generic network error message, not always safe to assume the error was host quitting + // without bubbling more info up from the network manager so this is the best we can do + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; +#ifdef _XBOX_ONE + ui.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_DISCONNECTED_SERVER_QUIT, uiIDA,1,m_iPad,ErrorDialogReturned,this, app.GetStringTable()); +#else + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA,1,m_iPad,ErrorDialogReturned,this, app.GetStringTable()); +#endif + } + + UIScene::tick(); +} + +void UIScene_JoinMenu::friendSessionUpdated(bool success, void *pParam) +{ + UIScene_JoinMenu *scene = (UIScene_JoinMenu *)pParam; + ui.NavigateBack(scene->m_iPad); + if( success ) + { + scene->m_friendInfoUpdatedOK = true; + } + else + { + scene->m_friendInfoUpdatedERROR = true; + } +} + +int UIScene_JoinMenu::ErrorDialogReturned(void *pParam, int iPad, const C4JStorage::EMessageResult) +{ + UIScene_JoinMenu *scene = (UIScene_JoinMenu *)pParam; + ui.NavigateBack(scene->m_iPad); + + return 0; +} + +void UIScene_JoinMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); +} + +wstring UIScene_JoinMenu::getMoviePath() +{ + return L"JoinMenu"; +} + +void UIScene_JoinMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; +#ifdef _DURANGO + case ACTION_MENU_Y: + if(m_selectedSession != NULL && getControlFocus() == eControl_GamePlayers && m_buttonListPlayers.getItemCount() > 0) + { + PlayerUID uid = m_selectedSession->searchResult.m_playerXuids[m_buttonListPlayers.getCurrentSelection()]; + if( uid != INVALID_XUID ) ProfileManager.ShowProfileCard(ProfileManager.GetLockedProfile(),uid); + } + break; +#endif + case ACTION_MENU_OK: + if (getControlFocus() != eControl_GamePlayers) + { + sendInputToMovie(key, repeat, pressed, released); + } + handled = true; + break; +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +void UIScene_JoinMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_JoinGame: + { + m_bIgnoreInput = true; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + +#ifdef _DURANGO + ProfileManager.CheckMultiplayerPrivileges(m_iPad, true, &checkPrivilegeCallback, this); +#else + StartSharedLaunchFlow(); +#endif + } + break; + case eControl_GamePlayers: + break; + }; +} + +void UIScene_JoinMenu::handleFocusChange(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_GamePlayers: + m_buttonListPlayers.updateChildFocus( (int) childId ); + }; + updateTooltips(); +} + +#ifdef _DURANGO +void UIScene_JoinMenu::checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad) +{ + UIScene_JoinMenu* pClass = (UIScene_JoinMenu*)lpParam; + + if(hasPrivilege) + { + pClass->StartSharedLaunchFlow(); +} + else + { + pClass->m_bIgnoreInput = false; + } +} +#endif + +void UIScene_JoinMenu::StartSharedLaunchFlow() +{ + if(!app.IsLocalMultiplayerAvailable()) + { + JoinGame(this); + } + else + { + //ProfileManager.RequestSignInUI(false, false, false, true, false,&UIScene_JoinMenu::StartGame_SignInReturned, this,ProfileManager.GetPrimaryPad()); + SignInInfo info; + info.Func = &UIScene_JoinMenu::StartGame_SignInReturned; + info.lpParam = this; + info.requireOnline = true; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); + } +} + +int UIScene_JoinMenu::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_JoinMenu* pClass = (UIScene_JoinMenu*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad)) + { + JoinGame(pClass); + } + else + { + pClass->m_bIgnoreInput=false; + } + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +// Shared function to join the game that is the same whether we used the sign-in UI or not +void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass) +{ + DWORD dwSignedInUsers = 0; + bool noPrivileges = false; + DWORD dwLocalUsersMask = 0; + bool isSignedInLive = true; + int iPadNotSignedInLive = -1; + + ProfileManager.SetLockedProfile(0); // TEMP! + + // If we're in SD mode, then only the primary player gets to play + if (app.IsLocalMultiplayerAvailable()) + { + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(index)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = index; + } + + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index); + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(index); + } + } + } + else + { + if(ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) + { + if( !ProfileManager.AllowedToPlayMultiplayer(ProfileManager.GetPrimaryPad()) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + isSignedInLive = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()); +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() && SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + isSignedInLive = true; +#endif + + } + } + + // If this is an online game but not all players are signed in to Live, stop! + if (!isSignedInLive) + { +#ifdef __ORBIS__ + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + pClass->m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else +#endif + { + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + return; + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + +#if defined(__PS3__) || defined(__PSVITA__) + if(isSignedInLive) + { + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&noUGC,NULL,NULL); + } +#else + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; +#endif + + +#ifdef __PSVITA__ + if( CGameNetworkManager::usingAdhocMode() ) + { + noPrivileges = false; + noUGC = false; + } +#endif + + if(noUGC) + { + pClass->setVisible( true ); + pClass->m_bIgnoreInput=false; + + int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + if(dwSignedInUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + + ui.RequestUGCMessageBox(IDS_CONNECTION_FAILED, messageText); + } + else if(noPrivileges) + { + pClass->setVisible( true ); + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { +#if defined(__ORBIS__) || defined(__PSVITA__) + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() ); + } +#endif + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( pClass->m_selectedSession, dwLocalUsersMask ); + + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); + + if( result != CGameNetworkManager::JOINGAME_SUCCESS ) + { + int exitReasonStringId = -1; + switch(result) + { + case CGameNetworkManager::JOINGAME_FAIL_SERVER_FULL: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + break; + } + + if( exitReasonStringId == -1 ) + { + ui.NavigateBack(pClass->m_iPad); + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + + ui.NavigateToHomeMenu(); + } + } + } +} + +void UIScene_JoinMenu::handleTimerComplete(int id) +{ + switch(id) + { + case UPDATE_PLAYERS_TIMER_ID: + { +#if TO_BE_IMPLEMENTED + PlayerUID selectedPlayerXUID = m_selectedSession->data.players[playersList.GetCurSel()]; + + bool success = g_NetworkManager.GetGameSessionInfo(m_iPad, m_selectedSession->sessionId,m_selectedSession); + + if( success ) + { + playersList.DeleteItems(0, playersList.GetItemCount()); + int selectedIndex = 0; + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if( m_selectedSession->data.players[i] != NULL ) + { + if(m_selectedSession->data.players[i] == selectedPlayerXUID) selectedIndex = i; + playersList.InsertItems(i,1); +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<data.szPlayers[i] ).c_str() ); + } + } + else + { + // Leave the loop when we hit the first NULL player + break; + } + } + playersList.SetCurSel(selectedIndex); + } +#endif + } + break; + }; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.h b/Minecraft.Client/Common/UI/UIScene_JoinMenu.h new file mode 100644 index 0000000..817360e --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.h @@ -0,0 +1,98 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_JoinMenu : public UIScene +{ +private: + enum EControls + { + eControl_JoinGame, + eControl_GamePlayers + }; + + enum ELabels + { + eLabel_Difficulty, + eLabel_GameType, + eLabel_GamertagsOn, + eLabel_Structures, + eLabel_LevelType, + eLabel_PVP, + eLabel_Trust, + eLabel_TNTOn, + eLabel_FireOn, + + eLabel_COUNT + }; + + UIControl_Button m_buttonJoinGame; + UIControl_ButtonList m_buttonListPlayers; + + UIControl_Label m_labelLabels[eLabel_COUNT]; + UIControl_Label m_labelValues[eLabel_COUNT]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonJoinGame, "JoinGame") + UI_MAP_ELEMENT( m_buttonListPlayers, "GamePlayers") + + UI_MAP_ELEMENT( m_labelLabels[0], "Label0") + UI_MAP_ELEMENT( m_labelLabels[1], "Label1") + UI_MAP_ELEMENT( m_labelLabels[2], "Label2") + UI_MAP_ELEMENT( m_labelLabels[3], "Label3") + UI_MAP_ELEMENT( m_labelLabels[4], "Label4") + UI_MAP_ELEMENT( m_labelLabels[5], "Label5") + UI_MAP_ELEMENT( m_labelLabels[6], "Label6") + UI_MAP_ELEMENT( m_labelLabels[7], "Label7") + UI_MAP_ELEMENT( m_labelLabels[8], "Label8") + + UI_MAP_ELEMENT( m_labelValues[0], "Value0") + UI_MAP_ELEMENT( m_labelValues[1], "Value1") + UI_MAP_ELEMENT( m_labelValues[2], "Value2") + UI_MAP_ELEMENT( m_labelValues[3], "Value3") + UI_MAP_ELEMENT( m_labelValues[4], "Value4") + UI_MAP_ELEMENT( m_labelValues[5], "Value5") + UI_MAP_ELEMENT( m_labelValues[6], "Value6") + UI_MAP_ELEMENT( m_labelValues[7], "Value7") + UI_MAP_ELEMENT( m_labelValues[8], "Value8") + UI_END_MAP_ELEMENTS_AND_NAMES() + + FriendSessionInfo *m_selectedSession; + bool m_bIgnoreInput; + bool m_friendInfoRequestIssued; + bool m_friendInfoUpdatedOK; + bool m_friendInfoUpdatedERROR; + +public: + UIScene_JoinMenu(int iPad, void *initData, UILayer *parentLayer); + void tick(); + static void friendSessionUpdated(bool success, void *pParam); + static int ErrorDialogReturned(void *pParam, int iPad, const C4JStorage::EMessageResult); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_LoadMenu;} + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleTimerComplete(int id); + +protected: + void handlePress(F64 controlId, F64 childId); + + + void StartSharedLaunchFlow(); + +#ifdef _DURANGO + static void checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad); +#endif + + static int StartGame_SignInReturned(void *pParam, bool, int); + static void JoinGame(UIScene_JoinMenu* pClass); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp b/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp new file mode 100644 index 0000000..fb1cc30 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Keyboard.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_Keyboard.h" + +#define KEYBOARD_DONE_TIMER_ID 0 +#define KEYBOARD_DONE_TIMER_TIME 100 + +UIScene_Keyboard::UIScene_Keyboard(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_EnterTextLabel.init(L"Enter Sign Text"); + + m_KeyboardTextInput.init(L"", -1); + m_KeyboardTextInput.SetCharLimit(15); + + m_ButtonSpace.init(L"Space", -1); + m_ButtonCursorLeft.init(L"Cursor Left", -1); + m_ButtonCursorRight.init(L"Cursor Right", -1); + m_ButtonCaps.init(L"Caps", -1); + m_ButtonDone.init(L"Done", 0); // only the done button needs an id, the others will never call back! + m_ButtonSymbols.init(L"Symbols", -1); + m_ButtonBackspace.init(L"Backspace", -1); + + // Initialise function keyboard Buttons and set alternative symbol button string + wstring label = L"Abc"; + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcInitFunctionButtons , 1 , value ); + + m_bKeyboardDonePressed = false; + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); +} + +UIScene_Keyboard::~UIScene_Keyboard() +{ + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +wstring UIScene_Keyboard::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1 && !m_parentLayer->IsFullscreenGroup()) + { + return L"KeyboardSplit"; + } + else + { + return L"Keyboard"; + } +} + +void UIScene_Keyboard::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1); +} + +bool UIScene_Keyboard::allowRepeat(int key) +{ + // 4J - TomK - we want to allow X and Y repeats! + switch(key) + { + case ACTION_MENU_OK: + case ACTION_MENU_CANCEL: + case ACTION_MENU_A: + case ACTION_MENU_B: + case ACTION_MENU_PAUSEMENU: + //case ACTION_MENU_X: + //case ACTION_MENU_Y: + return false; + } + return true; +} + +void UIScene_Keyboard::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + IggyDataValue result; + IggyResult out; + + if(repeat || pressed) + { + switch(key) + { + case ACTION_MENU_CANCEL: + navigateBack(); + handled = true; + break; + case ACTION_MENU_X: // X + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcBackspaceButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_PAGEUP: // LT + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSymbolButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_Y: // Y + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSpaceButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_STICK_PRESS: // LS + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcCapsButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_LEFT_SCROLL: // LB + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcCursorLeftButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_RIGHT_SCROLL: // RB + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcCursorRightButtonPressed, 0 , NULL ); + handled = true; + break; + case ACTION_MENU_PAUSEMENU: // Start + if(!m_bKeyboardDonePressed) + { + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcDoneButtonPressed, 0 , NULL ); + + // kick off done timer + addTimer(KEYBOARD_DONE_TIMER_ID,KEYBOARD_DONE_TIMER_TIME); + m_bKeyboardDonePressed = true; + } + handled = true; + break; + } + } + + switch(key) + { + case ACTION_MENU_OK: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +void UIScene_Keyboard::handlePress(F64 controlId, F64 childId) +{ + if((int)controlId == 0) + { + // Done has been pressed. At this point we can query for the input string and pass it on to wherever it is needed. + // we can not query for m_KeyboardTextInput.getLabel() here because we're in an iggy callback so we need to wait a frame. + if(!m_bKeyboardDonePressed) + { + // kick off done timer + addTimer(KEYBOARD_DONE_TIMER_ID,KEYBOARD_DONE_TIMER_TIME); + m_bKeyboardDonePressed = true; + } + } +} + +void UIScene_Keyboard::handleTimerComplete(int id) +{ + if(id == KEYBOARD_DONE_TIMER_ID) + { + // remove timer + killTimer(KEYBOARD_DONE_TIMER_ID); + + // we're done here! + KeyboardDonePressed(); + } +} + +void UIScene_Keyboard::KeyboardDonePressed() +{ + // Debug + app.DebugPrintf("UI Keyboard - DONE - [%ls]\n", m_KeyboardTextInput.getLabel()); + + // ToDo: Keyboard can now pass on its final string value and close itself down + navigateBack(); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_Keyboard.h b/Minecraft.Client/Common/UI/UIScene_Keyboard.h new file mode 100644 index 0000000..f4e4c89 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Keyboard.h @@ -0,0 +1,79 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_Keyboard : public UIScene +{ +private: + bool m_bKeyboardDonePressed; + +protected: + UIControl_Label m_EnterTextLabel; + UIControl_TextInput m_KeyboardTextInput; + UIControl_Button m_ButtonSpace, m_ButtonCursorLeft, m_ButtonCursorRight, m_ButtonCaps, m_ButtonDone, m_ButtonSymbols, m_ButtonBackspace; + + IggyName m_funcInitFunctionButtons; + IggyName m_funcCursorRightButtonPressed, m_funcCursorLeftButtonPressed, m_funcCapsButtonPressed, m_funcBackspaceButtonPressed; + IggyName m_funcSpaceButtonPressed, m_funcSymbolButtonPressed, m_funcDoneButtonPressed; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_EnterTextLabel, "EnterTextLabel") + UI_MAP_ELEMENT(m_KeyboardTextInput, "KeyboardTextInput") + + UI_MAP_ELEMENT(m_ButtonSpace, "Button_space") + UI_MAP_ELEMENT(m_ButtonCursorLeft, "Button_CursorLeft") + UI_MAP_ELEMENT(m_ButtonCursorRight, "Button_CursorRight") + UI_MAP_ELEMENT(m_ButtonCaps, "Button_Caps") + UI_MAP_ELEMENT(m_ButtonDone, "Button_Done") + UI_MAP_ELEMENT(m_ButtonSymbols, "Button_symbols") + UI_MAP_ELEMENT(m_ButtonBackspace, "Button_bspace") + + UI_MAP_NAME(m_funcInitFunctionButtons, L"InitFunctionButtons"); + + UI_MAP_NAME(m_funcCursorRightButtonPressed, L"CursorRightButtonPressed"); + UI_MAP_NAME(m_funcCursorLeftButtonPressed, L"CursorLeftButtonPressed"); + UI_MAP_NAME(m_funcCapsButtonPressed, L"CapsButtonPressed"); + UI_MAP_NAME(m_funcBackspaceButtonPressed, L"BackspaceButtonPressed"); + UI_MAP_NAME(m_funcSpaceButtonPressed, L"SpaceButtonPressed"); + UI_MAP_NAME(m_funcSymbolButtonPressed, L"SymbolButtonPressed"); + UI_MAP_NAME(m_funcDoneButtonPressed, L"DoneButtonPressed"); + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_Keyboard(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_Keyboard(); + + virtual void updateTooltips(); + + virtual bool allowRepeat(int key); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleTimerComplete(int id); + +protected: + void handlePress(F64 controlId, F64 childId); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +private: + void KeyboardDonePressed(); + +public: + virtual EUIScene getSceneType() { return eUIScene_Keyboard;} + + // Returns true if this scene handles input + //virtual bool stealsFocus() { return false; } + + // Returns true if this scene has focus for the pad passed in + //virtual bool hasFocus(int iPad) { return false; } + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp new file mode 100644 index 0000000..6d472b5 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp @@ -0,0 +1,462 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_LaunchMoreOptionsMenu.h" + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 + +#ifdef _LARGE_WORLDS +int m_iWorldSizeTitleA[4] = +{ + IDS_WORLD_SIZE_TITLE_CLASSIC, + IDS_WORLD_SIZE_TITLE_SMALL, + IDS_WORLD_SIZE_TITLE_MEDIUM, + IDS_WORLD_SIZE_TITLE_LARGE, +}; +#endif + +UIScene_LaunchMoreOptionsMenu::UIScene_LaunchMoreOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_params = (LaunchMoreOptionsMenuInitData *)initData; + + m_labelWorldOptions.init(app.GetString(IDS_WORLD_OPTIONS)); + + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_params->bGenerateOptions?0:1; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetMenuType , 1 , value ); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_params->iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); + + bool bOnlineGame, bInviteOnly, bAllowFriendsOfFriends; + bOnlineGame = m_params->bOnlineGame; + bInviteOnly = m_params->bInviteOnly; + bAllowFriendsOfFriends = m_params->bAllowFriendsOfFriends; + + // 4J-PB - to stop an offline game being able to select the online flag + if(ProfileManager.IsSignedInLive(m_params->iPad) == false) + { + m_checkboxes[eLaunchCheckbox_Online].SetEnable(false); + } + + if ( m_params->bOnlineSettingChangedBySystem && !m_bMultiplayerAllowed ) + { + // 4J-JEV: Disable and uncheck these boxes if they can't play multiplayer. + m_checkboxes[eLaunchCheckbox_Online].SetEnable(false); + m_checkboxes[eLaunchCheckbox_InviteOnly].SetEnable(false); + m_checkboxes[eLaunchCheckbox_AllowFoF].SetEnable(false); + + bOnlineGame = bInviteOnly = bAllowFriendsOfFriends = false; + } + else if(!m_params->bOnlineGame) + { + + m_checkboxes[eLaunchCheckbox_InviteOnly].SetEnable(false); + m_checkboxes[eLaunchCheckbox_AllowFoF].SetEnable(false); + } + + m_checkboxes[eLaunchCheckbox_Online].init(app.GetString(IDS_ONLINE_GAME),eLaunchCheckbox_Online,bOnlineGame); + m_checkboxes[eLaunchCheckbox_InviteOnly].init(app.GetString(IDS_INVITE_ONLY),eLaunchCheckbox_InviteOnly,bInviteOnly); + m_checkboxes[eLaunchCheckbox_AllowFoF].init(app.GetString(IDS_ALLOWFRIENDSOFFRIENDS),eLaunchCheckbox_AllowFoF,bAllowFriendsOfFriends); + m_checkboxes[eLaunchCheckbox_PVP].init(app.GetString(IDS_PLAYER_VS_PLAYER),eLaunchCheckbox_PVP,m_params->bPVP); + m_checkboxes[eLaunchCheckbox_TrustSystem].init(app.GetString(IDS_TRUST_PLAYERS),eLaunchCheckbox_TrustSystem,m_params->bTrust); + m_checkboxes[eLaunchCheckbox_FireSpreads].init(app.GetString(IDS_FIRE_SPREADS),eLaunchCheckbox_FireSpreads,m_params->bFireSpreads); + m_checkboxes[eLaunchCheckbox_TNT].init(app.GetString(IDS_TNT_EXPLODES),eLaunchCheckbox_TNT,m_params->bTNT); + m_checkboxes[eLaunchCheckbox_HostPrivileges].init(app.GetString(IDS_HOST_PRIVILEGES),eLaunchCheckbox_HostPrivileges,m_params->bHostPrivileges); + m_checkboxes[eLaunchCheckbox_ResetNether].init(app.GetString(IDS_RESET_NETHER),eLaunchCheckbox_ResetNether,m_params->bResetNether); + m_checkboxes[eLaunchCheckbox_Structures].init(app.GetString(IDS_GENERATE_STRUCTURES),eLaunchCheckbox_Structures,m_params->bStructures); + m_checkboxes[eLaunchCheckbox_FlatWorld].init(app.GetString(IDS_SUPERFLAT_WORLD),eLaunchCheckbox_FlatWorld,m_params->bFlatWorld); + m_checkboxes[eLaunchCheckbox_BonusChest].init(app.GetString(IDS_BONUS_CHEST),eLaunchCheckbox_BonusChest,m_params->bBonusChest); + + if(m_loadedResolution == eSceneResolution_1080) + { +#ifdef _LARGE_WORLDS + m_labelGameOptions.init( app.GetString(IDS_GAME_OPTIONS) ); + m_labelSeed.init(app.GetString(IDS_CREATE_NEW_WORLD_SEED)); + m_labelRandomSeed.init(app.GetString(IDS_CREATE_NEW_WORLD_RANDOM_SEED)); + m_editSeed.init(m_params->seed, eControl_EditSeed); + m_labelWorldSize.init(app.GetString(IDS_WORLD_SIZE)); + m_sliderWorldSize.init(app.GetString(m_iWorldSizeTitleA[m_params->worldSize]),eControl_WorldSize,0,3,m_params->worldSize); + + m_checkboxes[eLaunchCheckbox_DisableSaving].init( app.GetString(IDS_DISABLE_SAVING), eLaunchCheckbox_DisableSaving, m_params->bDisableSaving ); +#endif + } + + // Only the Xbox 360 needs a reset nether + // 4J-PB - PS3 needs it now + // #ifndef _XBOX + // if(!m_params->bGenerateOptions) removeControl( &m_checkboxes[eLaunchCheckbox_ResetNether], false ); + // #endif + + // set the default text +#ifdef _LARGE_WORLDS + wstring wsText=L""; + if(m_params->bGenerateOptions) + { + wsText = app.GetString(IDS_GAMEOPTION_SEED); + } + else + { + wsText = app.GetString(IDS_GAMEOPTION_ONLINE); + } +#else + wstring wsText=app.GetString(IDS_GAMEOPTION_ONLINE); +#endif + EHTMLFontSize size = eHTMLSize_Normal; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = eHTMLSize_Splitscreen; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText; + m_labelDescription.init(wsText); + + addTimer(GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME); + +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif + + m_bIgnoreInput = false; + m_tabIndex = 0; +} + +void UIScene_LaunchMoreOptionsMenu::updateTooltips() +{ + int changeTabTooltip = -1; + +#ifdef _LARGE_WORLDS + if (m_loadedResolution == eSceneResolution_1080 && m_params->bGenerateOptions) + { + // Set tooltip for change tab (only two tabs) + if (m_tabIndex == 0) + { + changeTabTooltip = IDS_GAME_OPTIONS; + } + else + { + changeTabTooltip = IDS_WORLD_OPTIONS; + } + } +#endif + + // If there's a change tab tooltip, left bumper symbol should show but not the text (-2) + int lb = changeTabTooltip == -1 ? -1 : -2; + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, -1, -1, -1, lb, changeTabTooltip); +} + +void UIScene_LaunchMoreOptionsMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); +#ifdef _LARGE_WORLDS + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); +#else + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +#endif +} + +wstring UIScene_LaunchMoreOptionsMenu::getMoviePath() +{ + return L"LaunchMoreOptionsMenu"; +} + +void UIScene_LaunchMoreOptionsMenu::tick() +{ + UIScene::tick(); + + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive(m_params->iPad) && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); + + if (bMultiplayerAllowed != m_bMultiplayerAllowed) + { + m_checkboxes[ eLaunchCheckbox_Online].SetEnable(bMultiplayerAllowed); + m_checkboxes[eLaunchCheckbox_InviteOnly].SetEnable(bMultiplayerAllowed); + m_checkboxes[ eLaunchCheckbox_AllowFoF].SetEnable(bMultiplayerAllowed); + + if (bMultiplayerAllowed) + { + m_checkboxes[ eLaunchCheckbox_Online].setChecked(true); + m_checkboxes[eLaunchCheckbox_AllowFoF].setChecked(true); + } + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } +} + +void UIScene_LaunchMoreOptionsMenu::handleDestroy() +{ +#ifdef __PSVITA__ + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); +#endif + + // so shut down the keyboard if it is displayed +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + InputManager.DestroyKeyboard(); +#endif +} + +void UIScene_LaunchMoreOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + // 4J-JEV: Inform user why their game must be offline. +#if defined _XBOX_ONE + { + UIControl_CheckBox *checkboxOnline = &m_checkboxes[eLaunchCheckbox_Online]; + if ( pressed && controlHasFocus( checkboxOnline->getId()) && !checkboxOnline->IsEnabled() ) + { + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + } +#endif + + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + case ACTION_MENU_OTHER_STICK_UP: + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + case ACTION_MENU_LEFT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: + if(pressed && m_loadedResolution == eSceneResolution_1080) + { + // Toggle tab index + m_tabIndex = m_tabIndex == 0 ? 1 : 0; + updateTooltips(); + IggyDataValue result; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcChangeTab , 0 , NULL ); + } + break; + } +} + +void UIScene_LaunchMoreOptionsMenu::handleCheckboxToggled(F64 controlId, bool selected) +{ + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + switch((EControls)((int)controlId)) + { + case eLaunchCheckbox_Online: + m_params->bOnlineGame = selected; + break; + case eLaunchCheckbox_InviteOnly: + m_params->bInviteOnly = selected; + break; + case eLaunchCheckbox_AllowFoF: + m_params->bAllowFriendsOfFriends = selected; + break; + case eLaunchCheckbox_PVP: + m_params->bPVP = selected; + break; + case eLaunchCheckbox_TrustSystem: + m_params->bTrust = selected; + break; + case eLaunchCheckbox_FireSpreads: + m_params->bFireSpreads = selected; + break; + case eLaunchCheckbox_TNT: + m_params->bTNT = selected; + break; + case eLaunchCheckbox_HostPrivileges: + m_params->bHostPrivileges = selected; + break; + case eLaunchCheckbox_ResetNether: + m_params->bResetNether = selected; + break; + case eLaunchCheckbox_Structures: + m_params->bStructures = selected; + break; + case eLaunchCheckbox_FlatWorld: + m_params->bFlatWorld = selected; + break; + case eLaunchCheckbox_BonusChest: + m_params->bBonusChest = selected; + break; + case eLaunchCheckbox_DisableSaving: + m_params->bDisableSaving = selected; + break; + }; +} + +void UIScene_LaunchMoreOptionsMenu::handleFocusChange(F64 controlId, F64 childId) +{ + int stringId = 0; + switch((int)controlId) + { + case eLaunchCheckbox_Online: + stringId = IDS_GAMEOPTION_ONLINE; + break; + case eLaunchCheckbox_InviteOnly: + stringId = IDS_GAMEOPTION_INVITEONLY; + break; + case eLaunchCheckbox_AllowFoF: + stringId = IDS_GAMEOPTION_ALLOWFOF; + break; + case eLaunchCheckbox_PVP: + stringId = IDS_GAMEOPTION_PVP; + break; + case eLaunchCheckbox_TrustSystem: + stringId = IDS_GAMEOPTION_TRUST; + break; + case eLaunchCheckbox_FireSpreads: + stringId = IDS_GAMEOPTION_FIRE_SPREADS; + break; + case eLaunchCheckbox_TNT: + stringId = IDS_GAMEOPTION_TNT_EXPLODES; + break; + case eLaunchCheckbox_HostPrivileges: + stringId = IDS_GAMEOPTION_HOST_PRIVILEGES; + break; + case eLaunchCheckbox_ResetNether: + stringId = IDS_GAMEOPTION_RESET_NETHER; + break; + case eLaunchCheckbox_Structures: + stringId = IDS_GAMEOPTION_STRUCTURES; + break; + case eLaunchCheckbox_FlatWorld: + stringId = IDS_GAMEOPTION_SUPERFLAT; + break; + case eLaunchCheckbox_BonusChest: + stringId = IDS_GAMEOPTION_BONUS_CHEST; + break; +#ifdef _LARGE_WORLDS + case eControl_EditSeed: + stringId = IDS_GAMEOPTION_SEED; + break; + case eControl_WorldSize: + stringId = IDS_GAMEOPTION_WORLD_SIZE; + break; + case eLaunchCheckbox_DisableSaving: + stringId = IDS_GAMEOPTION_DISABLE_SAVING; + break; +#endif + }; + + wstring wsText=app.GetString(stringId); + EHTMLFontSize size = eHTMLSize_Normal; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = eHTMLSize_Splitscreen; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText; + + m_labelDescription.setLabel(wsText); +} + +void UIScene_LaunchMoreOptionsMenu::handleTimerComplete(int id) +{ + /*switch(id) //4J-JEV: Moved this over to the tick. + { + case GAME_CREATE_ONLINE_TIMER_ID: + { + bool bMultiplayerAllowed + = ProfileManager.IsSignedInLive(m_params->iPad) + && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); + + if (bMultiplayerAllowed != m_bMultiplayerAllowed) + { + m_checkboxes[ eLaunchCheckbox_Online].SetEnable(bMultiplayerAllowed); + m_checkboxes[eLaunchCheckbox_InviteOnly].SetEnable(bMultiplayerAllowed); + m_checkboxes[ eLaunchCheckbox_AllowFoF].SetEnable(bMultiplayerAllowed); + + m_checkboxes[eLaunchCheckbox_Online].setChecked(bMultiplayerAllowed); + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + };*/ +} + +int UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback(LPVOID lpParam,bool bRes) +{ + UIScene_LaunchMoreOptionsMenu *pClass=(UIScene_LaunchMoreOptionsMenu *)lpParam; + pClass->m_bIgnoreInput=false; + // 4J HEG - No reason to set value if keyboard was cancelled + if (bRes) + { + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); + InputManager.GetText(pchText); + pClass->m_editSeed.setLabel((wchar_t *)pchText); + pClass->m_params->seed = (wchar_t *)pchText; + } + return 0; +} + +void UIScene_LaunchMoreOptionsMenu::handlePress(F64 controlId, F64 childId) +{ + if(m_bIgnoreInput) return; + + switch((int)controlId) + { + case eControl_EditSeed: + { + m_bIgnoreInput=true; +#ifdef __PS3__ + int language = XGetLanguage(); + switch(language) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_TCHINESE: + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); + break; + default: + // 4J Stu - Use a different keyboard for non-asian languages so we don't have prediction on + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Alphabet_Extended); + break; + } +#else + InputManager.RequestKeyboard(app.GetString(IDS_CREATE_NEW_WORLD_SEED),m_editSeed.getLabel(),(DWORD)0,60,&UIScene_LaunchMoreOptionsMenu::KeyboardCompleteSeedCallback,this,C_4JInput::EKeyboardMode_Default); +#endif + } + break; + } +} + + +void UIScene_LaunchMoreOptionsMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_WorldSize: +#ifdef _LARGE_WORLDS + m_sliderWorldSize.handleSliderMove(value); + m_params->worldSize = value; + m_sliderWorldSize.setLabel(app.GetString(m_iWorldSizeTitleA[value])); +#endif + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h new file mode 100644 index 0000000..62d7511 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h @@ -0,0 +1,125 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_LaunchMoreOptionsMenu : public UIScene +{ +private: + enum EControls + { + // Add all checkboxes at the start as they also index into a checkboxes array + eLaunchCheckbox_Online, + eLaunchCheckbox_InviteOnly, + eLaunchCheckbox_AllowFoF, + eLaunchCheckbox_PVP, + eLaunchCheckbox_TrustSystem, + eLaunchCheckbox_FireSpreads, + eLaunchCheckbox_TNT, + eLaunchCheckbox_HostPrivileges, + eLaunchCheckbox_ResetNether, + eLaunchCheckbox_Structures, + eLaunchCheckbox_FlatWorld, + eLaunchCheckbox_BonusChest, + eLaunchCheckbox_DisableSaving, + + eLaunchCheckboxes_Count, + + eControl_EditSeed, + eControl_WorldSize, + }; + + UIControl m_gameOptions, m_worldOptions; + UIControl_CheckBox m_checkboxes[eLaunchCheckboxes_Count]; + UIControl_Label m_labelWorldOptions, m_labelGameOptions, m_labelDescription; + UIControl_Label m_labelSeed, m_labelRandomSeed, m_labelWorldSize; + UIControl_TextInput m_editSeed; + UIControl_Slider m_sliderWorldSize; + IggyName m_funcSetMenuType, m_funcChangeTab; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + if(m_loadedResolution == eSceneResolution_1080) + { + UI_MAP_ELEMENT( m_labelGameOptions, "LabelGame") + UI_MAP_ELEMENT( m_labelWorldOptions, "LabelWorld") + + UI_MAP_ELEMENT( m_gameOptions, "GameOptions") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_gameOptions) + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_Online], "CheckboxOnline") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_InviteOnly], "CheckboxInviteOnly") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_AllowFoF], "CheckboxAllowFoF") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_PVP], "CheckboxPVP") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_TrustSystem], "CheckboxTrustSystem") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_FireSpreads], "CheckboxFireSpreads") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_TNT], "CheckboxTNT") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_HostPrivileges], "CheckboxHostPrivileges") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_ResetNether], "CheckboxResetNether") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_DisableSaving], "CheckboxDisableSaving") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT(m_worldOptions, "WorldOptions") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_worldOptions) + UI_MAP_ELEMENT( m_labelSeed, "Seed") + UI_MAP_ELEMENT( m_editSeed, "EditSeed") + UI_MAP_ELEMENT( m_labelRandomSeed, "RandomSeed") + UI_MAP_ELEMENT( m_labelWorldSize, "WorldSize") + UI_MAP_ELEMENT( m_sliderWorldSize, "WorldSizeSlider") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_Structures], "CheckboxStructures") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_BonusChest], "CheckboxBonusChest") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_FlatWorld], "CheckboxFlatWorld") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME( m_funcChangeTab, L"ChangeTab") + } + else + { + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_Online], "CheckboxOnline") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_InviteOnly], "CheckboxInviteOnly") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_AllowFoF], "CheckboxAllowFoF") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_PVP], "CheckboxPVP") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_TrustSystem], "CheckboxTrustSystem") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_FireSpreads], "CheckboxFireSpreads") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_TNT], "CheckboxTNT") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_HostPrivileges], "CheckboxHostPrivileges") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_ResetNether], "CheckboxResetNether") + + UI_MAP_ELEMENT( m_labelWorldOptions, "WorldOptions") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_Structures], "CheckboxStructures") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_FlatWorld], "CheckboxFlatWorld") + UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_BonusChest], "CheckboxBonusChest") + } + + UI_MAP_ELEMENT( m_labelDescription, "Description") + + UI_MAP_NAME( m_funcSetMenuType, L"SetMenuType") + UI_END_MAP_ELEMENTS_AND_NAMES() + + LaunchMoreOptionsMenuInitData *m_params; + bool m_bMultiplayerAllowed; + bool m_bIgnoreInput; + bool m_tabIndex; + +public: + UIScene_LaunchMoreOptionsMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_LaunchMoreOptionsMenu;} + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + virtual void tick(); + virtual void handleDestroy(); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleTimerComplete(int id); + static int KeyboardCompleteSeedCallback(LPVOID lpParam,const bool bRes); + virtual void handlePress(F64 controlId, F64 childId); + virtual void handleSliderMove(F64 sliderId, F64 currentValue); + +protected: + void handleCheckboxToggled(F64 controlId, bool selected); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.cpp new file mode 100644 index 0000000..80db57f --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.cpp @@ -0,0 +1,1045 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_LeaderboardsMenu.h" +#include "..\Leaderboards\LeaderboardManager.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +#define PLAYER_ONLINE_TIMER_ID 0 +#define PLAYER_ONLINE_TIMER_TIME 100 + +// if the value is greater than 32000, it's an xzp icon that needs displayed, rather than the game icon +const int UIScene_LeaderboardsMenu::TitleIcons[UIScene_LeaderboardsMenu::NUM_LEADERBOARDS][7] = +{ + { UIControl_LeaderboardList::e_ICON_TYPE_WALKED, UIControl_LeaderboardList::e_ICON_TYPE_FALLEN, Item::minecart_Id, Item::boat_Id, NULL }, + { Tile::dirt_Id, Tile::stoneBrick_Id, Tile::sand_Id, Tile::rock_Id, Tile::gravel_Id, Tile::clay_Id, Tile::obsidian_Id }, + { Item::egg_Id, Item::wheat_Id, Tile::mushroom1_Id, Tile::reeds_Id, Item::milk_Id, Tile::pumpkin_Id, NULL }, + { UIControl_LeaderboardList::e_ICON_TYPE_ZOMBIE, UIControl_LeaderboardList::e_ICON_TYPE_SKELETON, UIControl_LeaderboardList::e_ICON_TYPE_CREEPER, UIControl_LeaderboardList::e_ICON_TYPE_SPIDER, UIControl_LeaderboardList::e_ICON_TYPE_SPIDERJOKEY, UIControl_LeaderboardList::e_ICON_TYPE_ZOMBIEPIGMAN, UIControl_LeaderboardList::e_ICON_TYPE_SLIME }, +}; +const UIScene_LeaderboardsMenu::LeaderboardDescriptor UIScene_LeaderboardsMenu::LEADERBOARD_DESCRIPTORS[UIScene_LeaderboardsMenu::NUM_LEADERBOARDS][4] = { + { + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 4, true, IDS_LEADERBOARD_TRAVELLING_PEACEFUL), // Travelling Peaceful + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 4, true, IDS_LEADERBOARD_TRAVELLING_EASY), // Travelling Easy + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 4, true, IDS_LEADERBOARD_TRAVELLING_NORMAL), // Travelling Normal + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 4, true, IDS_LEADERBOARD_TRAVELLING_HARD), // Travelling Hard + }, + { + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_MINING_BLOCKS_PEACEFUL), // Mining Peaceful + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_MINING_BLOCKS_EASY), // Mining Easy + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_MINING_BLOCKS_NORMAL), // Mining Normal + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_MINING_BLOCKS_HARD), // Mining Hard + }, + { + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 6, false, IDS_LEADERBOARD_FARMING_PEACEFUL), // Farming Peaceful + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 6, false, IDS_LEADERBOARD_FARMING_EASY), // Farming Easy + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 6, false, IDS_LEADERBOARD_FARMING_NORMAL), // Farming Normal + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 6, false, IDS_LEADERBOARD_FARMING_HARD), // Farming Hard + }, + { + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 0, false, -1), // + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_KILLS_EASY), // Kills Easy + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_KILLS_NORMAL), // Kills Normal + UIScene_LeaderboardsMenu::LeaderboardDescriptor( 7, false, IDS_LEADERBOARD_KILLS_HARD), // Kills Hard + }, +}; + +UIScene_LeaderboardsMenu::UIScene_LeaderboardsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bReady=false; + + m_bPopulatedOnce = false; + + m_newTop = m_newSel = -1; + m_isProcessingStatsRead = false; + // Ignore input until we're retrieved stats, or functions will be called in here after we've backed out of the scene + m_bIgnoreInput=true; + + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + LeaderboardManager::Instance()->OpenSession(); + + //GetFriends(); + + m_currentLeaderboard = 0; + m_currentDifficulty = 2; + SetLeaderboardHeader(); + m_currentFilter = LeaderboardManager::eFM_Friends; + + wchar_t filterBuffer[40]; + swprintf(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_FRIENDS)); + m_labelFilter.init(filterBuffer); + + wchar_t entriesBuffer[40]; + swprintf(entriesBuffer, 40, L"%ls%i", app.GetString(IDS_LEADERBOARD_ENTRIES), 0); + m_labelEntries.init(entriesBuffer); + + ReadStats(-1); + +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + addTimer( PLAYER_ONLINE_TIMER_ID, PLAYER_ONLINE_TIMER_TIME ); +#endif +} + +UIScene_LeaderboardsMenu::~UIScene_LeaderboardsMenu() +{ + LeaderboardManager::Instance()->CancelOperation(); + LeaderboardManager::Instance()->CloseSession(); + + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); +} + +void UIScene_LeaderboardsMenu::updateTooltips() +{ + int iTooltipFriendRequest=-1; + int iTooltipGamerCardOrProfile=-1; + +#ifdef _DURANGO + //if( m_leaderboard.m_entries.size() > 0 ) + if(m_leaderboard.m_totalEntryCount > 0) + { + unsigned int selection = m_newSel; + + // If the selected user is me, don't show Send Friend Request, and show the gamer profile, not the gamer card + + // Check that the index is actually within range of the data we've got before accessing the m_leaderboard.m_entries array + int idx = selection - GetEntryStartIndex(); + if( ( idx < 0 ) || ( idx >= m_leaderboard.m_entries.size() ) ) + { + return; + } + if(m_leaderboard.m_entries[idx].m_bPlayer) + { + iTooltipGamerCardOrProfile=IDS_TOOLTIPS_VIEW_GAMERPROFILE; + } + else + { + iTooltipGamerCardOrProfile=IDS_TOOLTIPS_VIEW_GAMERCARD; + +#ifdef _XBOX + // if we're on the friends filter, then don't show the Send Friend Request + if(!m_currentFilter == LeaderboardManager::eFM_Friends) +#endif + { + // check the entry we're on + if( m_leaderboard.m_entries.size() > 0 ) + { + if( selection >= GetEntryStartIndex() && + selection < (GetEntryStartIndex() + m_leaderboard.m_entries.size()) ) + { +#ifdef _XBOX + if( (m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1)].m_bFriend==false) + && (m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1)].m_bRequestedFriend==false)) +#endif + { + iTooltipFriendRequest=IDS_TOOLTIPS_SEND_FRIEND_REQUEST; + } + } + } + } + } + } +#endif + + ui.SetTooltips(m_iPad, iTooltipFriendRequest, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGE_FILTER, iTooltipGamerCardOrProfile); +} + +void UIScene_LeaderboardsMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,!app.GetGameStarted()); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +} + +wstring UIScene_LeaderboardsMenu::getMoviePath() +{ + return L"LeaderboardMenu"; +} + +void UIScene_LeaderboardsMenu::handleReload() +{ + // We don't allow this in splitscreen, so just go back + navigateBack(); +} + +void UIScene_LeaderboardsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput && key != ACTION_MENU_CANCEL) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + // If this is not a press, do not action + if (!pressed) return; + + + /*app.DebugPrintf( + " m_newSel = %i [bottomId] = %i [topId] = %i, [size] = %i\n", + m_newSel, + m_leaderboard.m_entries.size() == 0 ? 0 : m_leaderboard.m_entries[m_leaderboard.m_entries.size()-1].m_row, + GetEntryStartIndex(), + m_leaderboard.m_entries.size() + );*/ + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_UP: + --m_newSel; + if(m_newSel<0)m_newSel = 0; + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_DOWN: + ++m_newSel; + if(m_newSel>=m_leaderboard.m_totalEntryCount) m_newSel = m_leaderboard.m_totalEntryCount - 1; + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_LEFT_SCROLL: + case ACTION_MENU_RIGHT_SCROLL: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if( pressed && m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Scroll); + + if( key == ACTION_MENU_RIGHT_SCROLL ) + { + ++m_currentDifficulty; + if( m_currentDifficulty == 4 ) + m_currentDifficulty = 0; + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 1; + } + else + { + if( m_currentDifficulty == 0 ) + m_currentDifficulty = 4; + --m_currentDifficulty; + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 3; + } + + SetLeaderboardHeader(); + + ReadStats(-1); + ui.PlayUISFX(eSFX_Press); + } + + handled = true; + } + break; + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if ( pressed && m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Scroll); + + m_bReady=false; + if(key == ACTION_MENU_RIGHT) + { + ++m_currentLeaderboard; + if( m_currentLeaderboard == NUM_LEADERBOARDS ) + m_currentLeaderboard = 0; + } + else + { + if( m_currentLeaderboard == 0 ) + m_currentLeaderboard = NUM_LEADERBOARDS; + --m_currentLeaderboard; + } + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 1; + + SetLeaderboardHeader(); + + ReadStats(-1); + ui.PlayUISFX(eSFX_Press); + } + handled = true; + } + break; + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if( pressed && m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Scroll); + + if( m_leaderboard.m_totalEntryCount <= 10 ) + break; + + sendInputToMovie(key, repeat, pressed, released); + +#if 0 + if( key == ACTION_MENU_PAGEUP ) + { + m_newTop = m_listGamers.GetTopItem() - 10; + + if( m_newTop < 0 ) + m_newTop = 0; + + m_newSel = m_newTop; + } + else + { + + m_newTop = m_listGamers.GetTopItem() + 10; + + if( m_newTop+10 > (int)m_leaderboard.m_totalEntryCount ) + { + m_newTop = m_leaderboard.m_totalEntryCount - 10; + if( m_newTop < 0 ) + m_newTop = 0; + } + + m_newSel = m_newTop; + } +#endif + } + handled = true; + } + break; + case ACTION_MENU_X: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if( pressed && m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Scroll); + + switch( m_currentFilter ) + { + case LeaderboardManager::eFM_Friends: + { + m_currentFilter = LeaderboardManager::eFM_MyScore; + wchar_t filterBuffer[40]; + swprintf(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_MYSCORE)); + m_labelFilter.setLabel(filterBuffer); + } + break; + case LeaderboardManager::eFM_MyScore: + { + m_currentFilter = LeaderboardManager::eFM_TopRank; + wchar_t filterBuffer[40]; + swprintf(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_OVERALL)); + m_labelFilter.setLabel(filterBuffer); + } + break; + case LeaderboardManager::eFM_TopRank: + { + m_currentFilter = LeaderboardManager::eFM_Friends; + wchar_t filterBuffer[40]; + swprintf(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_FRIENDS)); + m_labelFilter.setLabel(filterBuffer); + } + break; + } + + ReadStats(-1); + ui.PlayUISFX(eSFX_Press); + } + handled = true; + } + break; + case ACTION_MENU_Y: + { +#ifdef _DURANGO + //Show gamercard + //if( m_leaderboard.m_entries.size() > 0 ) + if(m_leaderboard.m_totalEntryCount > 0) + { + unsigned int selection = m_newSel; + if( selection >= GetEntryStartIndex() && + selection < (GetEntryStartIndex() + m_leaderboard.m_entries.size()) ) + { + PlayerUID uid = m_leaderboard.m_entries[selection - GetEntryStartIndex()].m_xuid; + if( uid != INVALID_XUID ) + { + ProfileManager.ShowProfileCard(ProfileManager.GetLockedProfile(),uid); + ui.PlayUISFX(eSFX_Press); + } + } + } +#endif + handled = true; + } + break; + case ACTION_MENU_A: + { +#ifdef _DURANGO + //Send friend request if the filter mode is not friend, and they're not a friend or a pending friend +#ifdef _XBOX + if( m_currentFilter != LeaderboardManager::eFM_Friends ) +#endif + { + if( m_leaderboard.m_entries.size() > 0 ) + { + unsigned int selection = m_newSel; + if( selection >= GetEntryStartIndex() && + selection < (GetEntryStartIndex() + m_leaderboard.m_entries.size()) ) + { + //If not the player and neither currently a friend or requested to be a friend + if( !m_leaderboard.m_entries[selection - GetEntryStartIndex()].m_bPlayer +#ifdef _XBOX + && !m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_bFriend + && !m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_bRequestedFriend +#endif + ) + { + PlayerUID xuid = m_leaderboard.m_entries[selection - GetEntryStartIndex()].m_xuid; + if( xuid != INVALID_XUID ) + { + ProfileManager.ShowAddFriend(m_iPad,xuid); + ui.PlayUISFX(eSFX_Press); + } + } + } + } + } +#endif + handled = true; + } + break; + } +} + +void UIScene_LeaderboardsMenu::ReadStats(int startIndex) +{ + //If startIndex == -1, then use default values + if( startIndex == -1 ) + { + m_newEntryIndex = 1; + m_newReadSize = READ_SIZE; + + m_newEntriesCount = 0; + + m_leaderboard.m_totalEntryCount = 0; + + m_listEntries.clearList(); + } + else + { + m_newEntryIndex = (unsigned int)startIndex; + // m_newReadSize = min((int)READ_SIZE, (int)m_leaderboard.m_totalEntryCount-(startIndex-1)); + } + + //app.DebugPrintf("Requesting stats read %d - %d - %d\n", m_currentLeaderboard, startIndex == -1 ? m_currentFilter : LeaderboardManager::eFM_TopRank, m_currentDifficulty); + + LeaderboardManager::EFilterMode filtermode; + if ( m_currentFilter == LeaderboardManager::eFM_MyScore + || m_currentFilter == LeaderboardManager::eFM_TopRank ) + { + filtermode = (startIndex == -1 ? m_currentFilter : LeaderboardManager::eFM_TopRank); + } + else + { + // 4J-JEV: Friends filter shouldn't switch to toprank. + filtermode = m_currentFilter; + } + + switch (filtermode) + { + case LeaderboardManager::eFM_TopRank: + LeaderboardManager::Instance()->ReadStats_TopRank( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + m_newEntryIndex, m_newReadSize + ); + break; + case LeaderboardManager::eFM_MyScore: + { + PlayerUID uid; + ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(),&uid, true); + LeaderboardManager::Instance()->ReadStats_MyScore( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + uid /*ignored on PS3*/, + m_newReadSize + ); + } + break; + case LeaderboardManager::eFM_Friends: + { + PlayerUID uid; + ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(),&uid, true); + LeaderboardManager::Instance()->ReadStats_Friends( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + uid /*ignored on PS3*/, + m_newEntryIndex, m_newReadSize + ); + } + break; + } + + //Show the loading message + m_labelInfo.setLabel(app.GetString(IDS_LEADERBOARD_LOADING)); + m_labelInfo.setVisible(true); +} + +bool UIScene_LeaderboardsMenu::OnStatsReadComplete(LeaderboardManager::eStatsReturn retIn, int numResults, LeaderboardManager::ViewOut results) +{ + //CScene_Leaderboards* scene = reinterpret_cast(userdata); + + m_isProcessingStatsRead = true; + + //bool noResults = LeaderboardManager::Instance()->GetStatsState() != XboxLeaderboardManager::eStatsState_Ready; + bool ret; + + //app.DebugPrintf("Leaderboards read %d stats\n", numResults); + if (retIn == LeaderboardManager::eStatsReturn_Success) + { + m_numStats = numResults; + m_stats = results; + ret = RetrieveStats(); + } + else ret = true; + + //else LeaderboardManager::Instance()->SetStatsRetrieved(false); + + PopulateLeaderboard(retIn); + + updateTooltips(); + + m_isProcessingStatsRead = false; + + // allow user input now + m_bIgnoreInput=false; + + return ret; +} + +bool UIScene_LeaderboardsMenu::RetrieveStats() +{ + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<SetStatsRetrieved(true); + + m_newEntryIndex = 0; + m_newEntriesCount = NUM_ENTRIES; + + return true; + } + + //assert( LeaderboardManager::Instance()->GetStats() != NULL ); + //PXUSER_STATS_READ_RESULTS stats = LeaderboardManager::Instance()->GetStats(); + //if( m_currentFilter == LeaderboardManager::eFM_Friends ) LeaderboardManager::Instance()->SortFriendStats(); + + bool isDistanceLeaderboard = LEADERBOARD_DESCRIPTORS[m_currentLeaderboard][m_currentDifficulty].m_isDistanceLeaderboard; + + m_newEntriesCount = m_stats.m_numQueries; + + // First read + if( m_leaderboard.m_totalEntryCount == 0 ) + { + m_leaderboard.m_entries.clear(); + +#if _DURANGO + m_leaderboard.m_totalEntryCount = m_numStats; +#else + m_leaderboard.m_totalEntryCount = (m_currentFilter == LeaderboardManager::eFM_Friends) ? m_newEntriesCount : m_numStats; +#endif + + if( m_leaderboard.m_totalEntryCount == 0 || m_newEntriesCount == 0 ) + { + //LeaderboardManager::Instance()->SetStatsRetrieved(false); + return false; + } + + m_leaderboard.m_numColumns = m_stats.m_queries[0].m_statsSize; + + for( unsigned int entryIndex=0 ; entryIndex < m_newEntriesCount; ++entryIndex ) + { + m_leaderboard.m_entries.push_back(LeaderboardEntry()); + CopyLeaderboardEntry(&(m_stats.m_queries[entryIndex]), entryIndex, isDistanceLeaderboard); + } + + m_newEntryIndex = 0; + + // Clear these values so that we know whether or not they are set in the next block + m_newTop = -1; + m_newSel = -1; + + // If the filter mode is "My Score" then centre the list around the entries and select the player's score + if( m_currentFilter == LeaderboardManager::eFM_MyScore) + { + //Centre the leaderboard list on the entries + m_newTop = GetEntryStartIndex(); + + //Select the player entry + for( unsigned int i = GetEntryStartIndex(); i< GetEntryStartIndex() + m_leaderboard.m_entries.size(); ++i ) + { + if( m_leaderboard.m_entries[i - GetEntryStartIndex()].m_bPlayer ) + { + m_newSel = i; // this might be off the screen! + // and reposition the top one + if(m_newSel-m_newTop>9) + { + m_newTop=m_newSel-9; + } + break; + } + } + } + + // If not set, default to start index + if (m_newSel < 0) m_newTop = m_newSel = GetEntryStartIndex(); + } + // Additional read + else + { + if(m_newEntryIndex < GetEntryStartIndex() && m_newEntryIndex == 1) + { + // If we're at the top the new entries count is incorrect, so amend + m_newEntriesCount = GetEntryStartIndex(); + } + + bool deleteFront = false; + bool deleteBack = false; + + bool trim = m_leaderboard.m_entries.size() + m_newEntriesCount >= NUM_ENTRIES; + + unsigned int insertPosition = 0; + + // If the first new entry is at a smaller index than the current first entry + if(m_newEntryIndex < GetEntryStartIndex()) + { + insertPosition = 0; + if (trim) deleteBack = true; + } + else + { + insertPosition = m_leaderboard.m_entries.size(); + if (trim) deleteFront = true; + } + + m_newEntryIndex = insertPosition; + + // Copy results to entries list + for( unsigned int i=0 ; i < m_newEntriesCount ; ++i ) + { + m_leaderboard.m_entries.insert(m_leaderboard.m_entries.begin() + insertPosition, LeaderboardEntry()); + CopyLeaderboardEntry(&(m_stats.m_queries[i]), insertPosition, isDistanceLeaderboard); + + insertPosition++; + } + + if (deleteFront) + { + // Delete front x entries + m_leaderboard.m_entries.erase(m_leaderboard.m_entries.begin(), m_leaderboard.m_entries.begin() + READ_SIZE); + m_newEntryIndex -= m_newReadSize; + } + else if (deleteBack) + { + // Delete back x entries + m_leaderboard.m_entries.erase(m_leaderboard.m_entries.end() - READ_SIZE, m_leaderboard.m_entries.end()); + } + } + + return true; +} + +void UIScene_LeaderboardsMenu::CopyLeaderboardEntry(LeaderboardManager::ReadScore *statsRow, int leaderboardEntryIndex, bool isDistanceLeaderboard) +{ + LeaderboardEntry* leaderboardEntry = &(m_leaderboard.m_entries[leaderboardEntryIndex]); + + ZeroMemory(leaderboardEntry, sizeof(LeaderboardEntry)); + leaderboardEntry->m_xuid = statsRow->m_uid; + + // Copy the rank + leaderboardEntry->m_rank = statsRow->m_rank; + DWORD displayRank = leaderboardEntry->m_rank; + if(displayRank > 9999999) displayRank = 9999999; + swprintf(leaderboardEntry->m_wcRank, 12, L"%u", displayRank); + + leaderboardEntry->m_idsErrorMessage = statsRow->m_idsErrorMessage; + + // Build a row ID + if (m_currentFilter == LeaderboardManager::eFM_Friends) + { + // If friends don't ID rows by rank + leaderboardEntry->m_row = leaderboardEntryIndex; + } + else + { + leaderboardEntry->m_row = statsRow->m_rank - 1; + if (leaderboardEntryIndex > 0) { + // Check this row ID (/rank) against the last one, it might be the same + // (this happens on PS3 when players have the same score, i.e. if they share 76th position there'll be two rank 76 + // and the following entry will be rank 78) + LeaderboardEntry* prevEntry = &(m_leaderboard.m_entries[leaderboardEntryIndex - 1]); + if (leaderboardEntry->m_row <= prevEntry->m_row) + { + leaderboardEntry->m_row = prevEntry->m_row + 1; + } + } + } + +#ifdef __PS3__ + // m_name can be unicode characters somehow for Japan - should use m_onlineID + wstring wstr=convStringToWstring(statsRow->m_uid.getOnlineID()); + swprintf(leaderboardEntry->m_gamerTag, XUSER_NAME_SIZE, L"%ls",wstr.c_str()); +#else + memcpy(leaderboardEntry->m_gamerTag, statsRow->m_name.data(), statsRow->m_name.size() * sizeof(wchar_t)); +#endif + + // Copy the other columns + for( unsigned int i=0 ; im_statsSize ; i++ ) + { + leaderboardEntry->m_columns[i] = statsRow->m_statsData[i]; + ZeroMemory(leaderboardEntry->m_wcColumns[i],12*sizeof(WCHAR)); + if( !isDistanceLeaderboard ) + { + DWORD displayValue = leaderboardEntry->m_columns[i]; + if(displayValue > 99999) displayValue = 99999; + swprintf(leaderboardEntry->m_wcColumns[i], 12, L"%u",displayValue); +#ifdef _DEBUG + //app.DebugPrintf("Value - %d\n",leaderboardEntry->m_columns[i]); +#endif + } + else + { + // check how many digits we have + int iDigitC=0; + unsigned int uiVal=leaderboardEntry->m_columns[i]; +// uiVal=0xFFFFFFFF; +// leaderboardEntry->m_columns[i-1]=uiVal; + + while(uiVal!=0) + { + uiVal/=10; + iDigitC++; + } + +#ifdef _DEBUG + //app.DebugPrintf("Value - %d\n",leaderboardEntry->m_columns[i]); +#endif + if(iDigitC<4) + { + // m + swprintf(leaderboardEntry->m_wcColumns[i], 12, L"%um", leaderboardEntry->m_columns[i]); +#ifdef _DEBUG + //app.DebugPrintf("Display - %um\n", leaderboardEntry->m_columns[i]); +#endif + } + else if(iDigitC<8) + { + // km with a .X + swprintf(leaderboardEntry->m_wcColumns[i], 12, L"%.1fkm", ((float)leaderboardEntry->m_columns[i])/1000.f); +#ifdef _DEBUG + //app.DebugPrintf("Display - %.1fkm\n", ((float)leaderboardEntry->m_columns[i])/1000.f); +#endif + } + else + { + // bigger than that, so no decimal point + swprintf(leaderboardEntry->m_wcColumns[i], 12, L"%.0fkm", ((float)leaderboardEntry->m_columns[i])/1000.f); +#ifdef _DEBUG + //app.DebugPrintf("Display - %.0fkm\n", ((float)leaderboardEntry->m_columns[i])/1000.f); +#endif + } + } + } + +#ifdef _DURANGO + //Is the player + PlayerUID myXuid; + ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(),&myXuid,true); + if( statsRow->m_uid == myXuid ) + { + leaderboardEntry->m_bPlayer = true; + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = false; + } + else + { + leaderboardEntry->m_bPlayer = false; + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = false; + +#ifdef _XBOX + //Check for friend status + for( unsigned int friendIndex=0 ; friendIndexm_uid ) + { + if( ( m_friends[friendIndex].dwFriendState & ( XONLINE_FRIENDSTATE_FLAG_SENTREQUEST | XONLINE_FRIENDSTATE_FLAG_RECEIVEDREQUEST ) ) == 0 ) + { + //Is friend, might be online + leaderboardEntry->m_bFriend = true; + leaderboardEntry->m_bOnline = ( m_friends[friendIndex].dwFriendState & XONLINE_FRIENDSTATE_FLAG_ONLINE ); + leaderboardEntry->m_bRequestedFriend = false; + } + else + { + //Friend request sent but not accepted yet + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = true; + } + + break; + } + } +#endif + } +#endif +} + +void UIScene_LeaderboardsMenu::PopulateLeaderboard(LeaderboardManager::eStatsReturn ret) +{ + int iValidSlots=SetLeaderboardTitleIcons(); + if( ret == LeaderboardManager::eStatsReturn_Success && m_leaderboard.m_totalEntryCount > 0 ) + { + m_listEntries.setupTitles( app.GetString( IDS_LEADERBOARD_RANK ), app.GetString( IDS_LEADERBOARD_GAMERTAG ) ); + + //Update entries display + wchar_t entriesBuffer[40]; + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L< 0) + { + m_listEntries.addDataSet( + isLast, + m_leaderboard.m_entries[i].m_row, + m_leaderboard.m_entries[i].m_rank, + m_leaderboard.m_entries[i].m_gamerTag, + + true, // 4J-JEV: Has error message to display. + + app.GetString(idsErrorMessage), + L"", L"", L"", L"", L"", L"" + ); + } + else + { + m_listEntries.addDataSet( + isLast, + m_leaderboard.m_entries[i].m_row, + m_leaderboard.m_entries[i].m_rank, + m_leaderboard.m_entries[i].m_gamerTag, + + // 4J-TomK | The bDisplayMessage Flag defines if Leaderboard Data should be + // displayed (false) or if a specific message (true - when data is private for example) + // should be displayed. The message itself should be passed on in col0! + false, + + m_leaderboard.m_entries[i].m_wcColumns[0], + m_leaderboard.m_entries[i].m_wcColumns[1], + m_leaderboard.m_entries[i].m_wcColumns[2], + m_leaderboard.m_entries[i].m_wcColumns[3], + m_leaderboard.m_entries[i].m_wcColumns[4], + m_leaderboard.m_entries[i].m_wcColumns[5], + m_leaderboard.m_entries[i].m_wcColumns[6] + ); + } + } + } + else + { + m_listEntries.setupTitles( L"", L"" ); + + //Update entries display (to zero) + wchar_t entriesBuffer[40]; + swprintf(entriesBuffer, 40, L"%ls0", app.GetString(IDS_LEADERBOARD_ENTRIES)); + m_labelEntries.setLabel(entriesBuffer); + + //Show the no results message +#if !(defined(_XBOX) || defined(_WINDOWS64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + if (ret == LeaderboardManager::eStatsReturn_NetworkError) + m_labelInfo.setLabel(app.GetString(IDS_ERROR_NETWORK)); + else +#endif + m_labelInfo.setLabel(app.GetString(IDS_LEADERBOARD_NORESULTS)); + m_labelInfo.setVisible(true); + } + m_bPopulatedOnce = true; +} + +void UIScene_LeaderboardsMenu::SetLeaderboardHeader() +{ + m_labelLeaderboard.setLabel(app.GetString(LEADERBOARD_DESCRIPTORS[m_currentLeaderboard][m_currentDifficulty].m_title)); +} + +int UIScene_LeaderboardsMenu::SetLeaderboardTitleIcons() +{ + int iValidIcons=0; + + for(int i=0;i<7;i++) + { + if(TitleIcons[m_currentLeaderboard][i]==0) + { + //m_pHTitleIconSlots[i]->SetShow(FALSE); + } + else + { + iValidIcons++; + m_listEntries.setColumnIcon(i,TitleIcons[m_currentLeaderboard][i]); + } + } + + return iValidIcons; +} + +void UIScene_LeaderboardsMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + int slotId = -1; + swscanf((wchar_t*)region->name,L"slot_%d",&slotId); + if (slotId == -1) + { + //app.DebugPrintf("This is not the control we are looking for\n"); + } + else + { + shared_ptr item = shared_ptr( new ItemInstance(TitleIcons[m_currentLeaderboard][slotId], 1, 0) ); + customDrawSlotControl(region,m_iPad,item,1.0f,false,false); + } +} + +void UIScene_LeaderboardsMenu::handleSelectionChanged(F64 selectedId) +{ + ui.PlayUISFX(eSFX_Focus); + m_newSel = (int)selectedId; + updateTooltips(); +} + +// Handle a request from Iggy for more data +void UIScene_LeaderboardsMenu::handleRequestMoreData(F64 startIndex, bool up) +{ + unsigned int item = (int)startIndex; + + if( m_leaderboard.m_totalEntryCount > 0 && (item+1) < GetEntryStartIndex() ) + { + if( LeaderboardManager::Instance()->isIdle() ) + { + int readIndex = (GetEntryStartIndex() + 1) - READ_SIZE; + if( readIndex <= 0 ) + readIndex = 1; + assert( readIndex >= 1 && readIndex <= (int)m_leaderboard.m_totalEntryCount ); + ReadStats(readIndex); + } + } + else if( m_leaderboard.m_totalEntryCount > 0 && (item+1) >= (GetEntryStartIndex() + m_leaderboard.m_entries.size()) ) + { + if( LeaderboardManager::Instance()->isIdle() ) + { + int readIndex = (GetEntryStartIndex() + 1) + m_leaderboard.m_entries.size(); + assert( readIndex >= 1 && readIndex <= (int)m_leaderboard.m_totalEntryCount ); + ReadStats(readIndex); + } + } +} + +void UIScene_LeaderboardsMenu::handleTimerComplete(int id) +{ +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) + switch(id) + { + case PLAYER_ONLINE_TIMER_ID: +#ifndef _WINDOWS64 + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())==false) + { + // check the player hasn't gone offline + // If they have, bring up the PSN warning and exit from the leaderboards + unsigned int uiIDA[1]; + uiIDA[0]=IDS_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_CONNECTION_LOST, g_NetworkManager.CorrectErrorIDS(IDS_CONNECTION_LOST_LIVE_NO_EXIT), uiIDA,1,ProfileManager.GetPrimaryPad(),UIScene_LeaderboardsMenu::ExitLeaderboards,this, app.GetStringTable()); + } +#endif + break; + } +#endif +} + +int UIScene_LeaderboardsMenu::ExitLeaderboards(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LeaderboardsMenu* pClass = (UIScene_LeaderboardsMenu*)pParam; + + pClass->navigateBack(); + + return 0; +} + +// Get entry start size, if no entries returns 0 +int UIScene_LeaderboardsMenu::GetEntryStartIndex() +{ + return m_leaderboard.m_entries.size() == 0 ? 0 : m_leaderboard.m_entries[0].m_row; +} diff --git a/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.h b/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.h new file mode 100644 index 0000000..20bf000 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LeaderboardsMenu.h @@ -0,0 +1,143 @@ +#pragma once + +#include "UIScene.h" +#include "..\Leaderboards\LeaderboardManager.h" + +class UIScene_LeaderboardsMenu : public UIScene, public LeaderboardReadListener +{ +private: + // 4J Stu - Because the kills leaderboard doesn't a peaceful entry there are some special + // handling to make it skip that. We have re-arranged the order of the leaderboards so + // I am making this in case we do it again. + // 4J Stu - Made it a member of the class, rather than a #define + static const int LEADERBOARD_KILLS_POSITION = 3; + + static const int NUM_LEADERBOARDS = 4;//6; //Number of leaderboards + static const int NUM_ENTRIES = 101; //Cache up to this many entries + static const int READ_SIZE = 15; //Read this many entries at a time + + struct LeaderboardDescriptor { + unsigned int m_columnCount; + bool m_isDistanceLeaderboard; + unsigned int m_title; + + LeaderboardDescriptor(unsigned int columnCount, bool isDistanceLeaderboard, unsigned int title) + { + m_columnCount = columnCount; + m_isDistanceLeaderboard = isDistanceLeaderboard; + m_title = title; + } + }; + + static const LeaderboardDescriptor LEADERBOARD_DESCRIPTORS[NUM_LEADERBOARDS][4]; + static const int TitleIcons[NUM_LEADERBOARDS][7]; + + struct LeaderboardEntry { + PlayerUID m_xuid; + unsigned int m_row; // Row identifier for passing to Iggy as a unique identifier + DWORD m_rank; + WCHAR m_wcRank[12]; + WCHAR m_gamerTag[XUSER_NAME_SIZE+1]; + //int m_locale; + unsigned int m_columns[7]; + WCHAR m_wcColumns[7][12]; + bool m_bPlayer; //Is the player + bool m_bOnline; //Is online + bool m_bFriend; //Is friend + bool m_bRequestedFriend; //Friend request sent but not answered + int m_idsErrorMessage; // 4J-JEV: Non-zero if this entry has an error message instead of results. + }; + + struct Leaderboard { + DWORD m_totalEntryCount; //Either total number of entries in leaderboard, or total number of results for a friends query + vector m_entries; + DWORD m_numColumns; + }; + + Leaderboard m_leaderboard; //All leaderboard data for the currently selected filter + + unsigned int m_currentLeaderboard; //The current leaderboard selected for view + LeaderboardManager::EFilterMode m_currentFilter; //The current filter selected + unsigned int m_currentDifficulty; //The current difficulty selected + + unsigned int m_newEntryIndex; //Index of the first entry being read + unsigned int m_newReadSize; //Number of entries in the current read operation + + unsigned int m_newEntriesCount; // Number of new entries in this update + + int m_newTop; //Index of the element that should be at the top of the list + int m_newSel; //Index of the element that should be selected in the list + + bool m_isProcessingStatsRead; + bool m_bPopulatedOnce; + bool m_bReady; + + UIControl_LeaderboardList m_listEntries; + UIControl_Label m_labelFilter, m_labelLeaderboard, m_labelEntries, m_labelInfo; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_listEntries, "Gamers") + + UI_MAP_ELEMENT( m_labelFilter, "Filter") + UI_MAP_ELEMENT( m_labelLeaderboard, "Leaderboard") + UI_MAP_ELEMENT( m_labelEntries, "Entries") + UI_MAP_ELEMENT( m_labelInfo, "Info") + UI_END_MAP_ELEMENTS_AND_NAMES() + + static int ExitLeaderboards(void *pParam,int iPad,C4JStorage::EMessageResult result); + +public: + UIScene_LeaderboardsMenu(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_LeaderboardsMenu(); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_LeaderboardsMenu;} + + // Returns true if this scene has focus for the pad passed in + virtual bool hasFocus(int iPad) { return bHasFocus; } + virtual void handleTimerComplete(int id); + +private: + int GetEntryStartIndex(); + +protected: + virtual wstring getMoviePath(); + +public: + void handleReload(); + + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +private: + //Start a read request with the current parameters + void ReadStats(int startIndex); + + //Copy the stats from the raw m_stats structure into the m_leaderboards structure + int m_numStats; + LeaderboardManager::ViewOut m_stats; + bool RetrieveStats(); + + // Copy a leaderboard entry from the stats row + void CopyLeaderboardEntry(LeaderboardManager::ReadScore *statsRow, int leaderboardEntryIndex, bool isDistanceLeaderboard); + + //Populate the XUI leaderboard with the contents of m_leaderboards + void PopulateLeaderboard(LeaderboardManager::eStatsReturn ret); + + //Set the header text of the leaderboard + void SetLeaderboardHeader(); + + // Set the title icons + int SetLeaderboardTitleIcons(); + + //Callback function called when stats read completes, userdata contains pointer to instance of CScene_Leaderboards + virtual bool OnStatsReadComplete(LeaderboardManager::eStatsReturn ret, int numResults, LeaderboardManager::ViewOut results); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + + virtual void handleSelectionChanged(F64 selectedId); + virtual void handleRequestMoreData(F64 startIndex, bool up); + + bool m_bIgnoreInput; +}; diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp new file mode 100644 index 0000000..f08fc72 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp @@ -0,0 +1,1804 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_LoadMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\Options.h" +#include "..\..\MinecraftServer.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) +#include "Common\Network\Sony\SonyHttp.h" +#endif +#include "..\..\DLCTexturePack.h" +#if defined(__ORBIS__) || defined(__PSVITA__) +#include +#endif + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 +// 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 1 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 50 +#endif + +int UIScene_LoadMenu::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + +int UIScene_LoadMenu::LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes) +{ + UIScene_LoadMenu *pClass= (UIScene_LoadMenu *)lpParam; + + app.DebugPrintf("Received data for a thumbnail\n"); + + if(pbThumbnail && dwThumbnailBytes) + { + pClass->registerSubstitutionTexture(pClass->m_thumbnailName,pbThumbnail,dwThumbnailBytes); + + pClass->m_pbThumbnailData = pbThumbnail; + pClass->m_uiThumbnailSize = dwThumbnailBytes; + pClass->m_bSaveThumbnailReady = true; + } + else + { + app.DebugPrintf("Thumbnail data is NULL, or has size 0\n"); + pClass->m_bThumbnailGetFailed = true; + } + pClass->m_bRetrievingSaveThumbnail = false; + + return 0; +} + +UIScene_LoadMenu::UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLayer) : IUIScene_StartGame(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + LoadMenuInitData *params = (LoadMenuInitData *)initData; + + //m_labelGameName.init(app.GetString(IDS_WORLD_NAME)); + m_labelSeed.init(L""); + m_labelCreatedMode.init(app.GetString(IDS_CREATED_IN_SURVIVAL)); + + m_buttonGamemode.init(app.GetString(IDS_GAMEMODE_SURVIVAL),eControl_GameMode); + m_buttonMoreOptions.init(app.GetString(IDS_MORE_OPTIONS),eControl_MoreOptions); + m_buttonLoadWorld.init(app.GetString(IDS_LOAD),eControl_LoadWorld); + m_texturePackList.init(app.GetString(IDS_DLC_MENU_TEXTUREPACKS), eControl_TexturePackList); + + m_labelTexturePackName.init(L""); + m_labelTexturePackDescription.init(L""); + + m_CurrentDifficulty=app.GetGameSettings(m_iPad,eGameSetting_Difficulty); + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)])); + m_sliderDifficulty.init(TempString,eControl_Difficulty,0,3,app.GetGameSettings(m_iPad,eGameSetting_Difficulty)); + + m_MoreOptionsParams.bGenerateOptions=FALSE; + m_MoreOptionsParams.bPVP = TRUE; + m_MoreOptionsParams.bTrust = TRUE; + m_MoreOptionsParams.bFireSpreads = TRUE; + m_MoreOptionsParams.bHostPrivileges = FALSE; + m_MoreOptionsParams.bTNT = TRUE; + m_MoreOptionsParams.iPad = iPad; + + m_iSaveGameInfoIndex=params->iSaveGameInfoIndex; + m_levelGen = params->levelGen; + + m_bGameModeSurvival=true; + m_bHasBeenInCreative = false; + + m_bSaveThumbnailReady = false; + m_bRetrievingSaveThumbnail = true; + m_bShowTimer = false; + m_pDLCPack = NULL; + m_bAvailableTexturePacksChecked=false; + m_bRequestQuadrantSignin = false; + m_iTexturePacksNotInstalled=0; + m_bRebuildTouchBoxes = false; + m_bThumbnailGetFailed = false; + m_seed = 0; + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineSettingChangedBySystem=false; + + // Set the text for friends of friends, and default to on + if( m_bMultiplayerAllowed) + { + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + if(bGameSetting_Online) + { + // The profile settings say Online, but either the player is offline, or they are not allowed to play online + m_MoreOptionsParams.bOnlineSettingChangedBySystem=true; + } + } + + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + // Set up online game checkbox + bool bOnlineGame = m_MoreOptionsParams.bOnlineGame; + m_checkboxOnline.SetEnable(true); + + // 4J-PB - to stop an offline game being able to select the online flag + if(ProfileManager.IsSignedInLive(m_iPad) == false) + { + m_checkboxOnline.SetEnable(false); + } + + if(m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + m_checkboxOnline.SetEnable(false); + bOnlineGame = false; + } + + m_checkboxOnline.init(app.GetString(IDS_ONLINE_GAME), eControl_OnlineGame, bOnlineGame); + } +#endif + + // Level gen + if(m_levelGen) + { + m_labelGameName.init(m_levelGen->getDisplayName()); + if(m_levelGen->requiresTexturePack()) + { + m_MoreOptionsParams.dwTexturePack = m_levelGen->getRequiredTexturePackId(); + + m_texturePackList.setEnabled(false); + + + // retrieve the save icon from the texture pack, if there is one + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + wchar_t textureName[64]; + swprintf(textureName,64,L"loadsave"); + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + m_bitmapIcon.setTextureName( textureName ); + } + } + // Set this level as created in creative mode, so that people can't use the themed worlds as an easy way to get achievements + m_bHasBeenInCreative = true; + m_labelCreatedMode.setLabel( app.GetString(IDS_CREATED_IN_CREATIVE) ); + } + else + { + +#if defined(__PS3__) || defined(__ORBIS__)|| defined(_DURANGO) || defined (__PSVITA__) + // convert to utf16 + uint16_t u16Message[MAX_SAVEFILENAME_LENGTH]; + size_t srclen,dstlen; + srclen=MAX_SAVEFILENAME_LENGTH; + dstlen=MAX_SAVEFILENAME_LENGTH; +#ifdef __PS3__ + L10nResult lres= UTF8stoUTF16s((uint8_t *)params->saveDetails->UTF8SaveFilename,&srclen,u16Message,&dstlen); +#elif defined(_DURANGO) + // Already utf16 on durango + memcpy(u16Message,params->saveDetails->UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#else // __ORBIS__ + { + SceCesUcsContext Context; + sceCesUcsContextInit( &Context ); + uint32_t utf8Len, utf16Len; + sceCesUtf8StrToUtf16Str(&Context, (uint8_t *)params->saveDetails->UTF8SaveFilename, srclen, &utf8Len, u16Message, dstlen, &utf16Len); + } +#endif + m_thumbnailName = (wchar_t *)u16Message; + if(params->saveDetails->pbThumbnailData) + { + m_pbThumbnailData = params->saveDetails->pbThumbnailData; + m_uiThumbnailSize = params->saveDetails->dwThumbnailSize; + m_bSaveThumbnailReady = true; + } + else + { + app.DebugPrintf("Requesting the save thumbnail\n"); + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); +#ifdef _DURANGO + // On Durango, we have an extra flag possible with LoadSaveDataThumbnail, which if true will force the loading of this thumbnail even if the save data isn't sync'd from + // the cloud at this stage. This could mean that there could be a pretty large delay before the callback happens, in this case. + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex],&LoadSaveDataThumbnailReturned,this,true); +#else + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex],&LoadSaveDataThumbnailReturned,this); +#endif + m_bShowTimer = true; + } +#if defined(_DURANGO) + m_labelGameName.init(params->saveDetails->UTF16SaveName); +#else + + m_labelGameName.init(params->saveDetails->UTF8SaveName); +#endif +#endif + } + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_LoadMenu, 0); + m_iTexturePacksNotInstalled=0; + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true) + { + // not doing a mount, so enable input + m_bIgnoreInput=true; + } + else + { + m_bIgnoreInput = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + wchar_t imageName[64]; + swprintf(imageName,64,L"tpack%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + m_texturePackList.addPack(i,imageName); + } + } + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(m_MoreOptionsParams.dwTexturePack); + UpdateTexturePackDescription(m_currentTexturePackIndex); + m_texturePackList.selectSlot(m_currentTexturePackIndex); + + // 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + + // 4J-PB - Any texture packs available that we don't have installed? +#if defined(__PS3__) || defined(__ORBIS__) + if(!m_bAvailableTexturePacksChecked && app.GetCommerceProductListRetrieved()&& app.GetCommerceProductListInfoRetrieved()) +#else + if(!m_bAvailableTexturePacksChecked) +#endif + { + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; +#if defined(__PS3__) || defined(__ORBIS__) + char *pchName=app.GetDLCInfoTextures(i); + pDLCInfo=app.GetDLCInfo(pchName); +#else + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); +#endif + + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo && pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; +#if defined(__PS3__) || defined(__ORBIS__) + char *pchName=app.GetDLCInfoTextures(i); + pDLCInfo=app.GetDLCInfo(pchName); +#else + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); +#endif + + if(pDLCInfo) + { + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo && pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + } + } +#endif + } + +#ifdef _XBOX + addTimer(CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); +#endif + + if(params) delete params; + addTimer(GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME); +} + +void UIScene_LoadMenu::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1); +} + +void UIScene_LoadMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + + if(RenderManager.IsWidescreen()) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + } +} + +wstring UIScene_LoadMenu::getMoviePath() +{ + return L"LoadMenu"; +} + +UIControl* UIScene_LoadMenu::GetMainPanel() +{ + return &m_controlMainPanel; +} + +void UIScene_LoadMenu::tick() +{ + if(m_bShowTimer) + { + m_bShowTimer = false; + ui.NavigateToScene(m_iPad, eUIScene_Timer); + } + + if( m_bThumbnailGetFailed ) + { + // On Durango, this can happen if a save is still not been synchronised (user cancelled, or some error). Return back to give them a choice to pick another save. + ui.NavigateBack(m_iPad, false, eUIScene_LoadOrJoinMenu); + return; + } + + if( m_bSaveThumbnailReady ) + { + m_bSaveThumbnailReady = false; + + m_bitmapIcon.setTextureName( m_thumbnailName.c_str() ); + + // retrieve the seed value from the image metadata + bool bHostOptionsRead = false; + unsigned int uiHostOptions = 0; + + char szSeed[50]; + ZeroMemory(szSeed,50); + app.GetImageTextData(m_pbThumbnailData,m_uiThumbnailSize,(unsigned char *)&szSeed,uiHostOptions,bHostOptionsRead,m_MoreOptionsParams.dwTexturePack); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + sscanf_s(szSeed, "%I64d", &m_seed); +#endif + + // #ifdef _DEBUG + // // dump out the thumbnail + // HANDLE hThumbnail = CreateFile("GAME:\\thumbnail.png", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL); + // DWORD dwBytes; + // WriteFile(hThumbnail,pbImageData,dwImageBytes,&dwBytes,NULL); + // XCloseHandle(hThumbnail); + // #endif + + if(szSeed[0]!=0) + { + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"%ls: %hs", app.GetString( IDS_SEED ),szSeed); + m_labelSeed.setLabel(TempString); + } + else + { + m_labelSeed.setLabel(L""); + } + + // Setup all the text and checkboxes to match what the game was saved with on + if(bHostOptionsRead) + { + m_MoreOptionsParams.bPVP = app.GetGameHostOption(uiHostOptions,eGameHostOption_PvP)>0?TRUE:FALSE; + m_MoreOptionsParams.bTrust = app.GetGameHostOption(uiHostOptions,eGameHostOption_TrustPlayers)>0?TRUE:FALSE; + m_MoreOptionsParams.bFireSpreads = app.GetGameHostOption(uiHostOptions,eGameHostOption_FireSpreads)>0?TRUE:FALSE; + m_MoreOptionsParams.bTNT = app.GetGameHostOption(uiHostOptions,eGameHostOption_TNT)>0?TRUE:FALSE; + m_MoreOptionsParams.bHostPrivileges = app.GetGameHostOption(uiHostOptions,eGameHostOption_CheatsEnabled)>0?TRUE:FALSE; + m_MoreOptionsParams.bDisableSaving = app.GetGameHostOption(uiHostOptions,eGameHostOption_DisableSaving)>0?TRUE:FALSE; + + // turn off creative mode on the save + // #ifdef _DEBUG + // uiHostOptions&=~GAME_HOST_OPTION_BITMASK_BEENINCREATIVE; + // app.SetGameHostOption(eGameHostOption_HasBeenInCreative, 0); + // #endif + + m_bHasBeenInCreative = app.GetGameHostOption(uiHostOptions,eGameHostOption_HasBeenInCreative)>0; + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_HasBeenInCreative)>0) + { + m_labelCreatedMode.setLabel( app.GetString(IDS_CREATED_IN_CREATIVE) ); + } + else + { + m_labelCreatedMode.setLabel( app.GetString(IDS_CREATED_IN_SURVIVAL) ); + } + + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_GameType)>0) + { + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_FriendsOfFriends) && !(m_bMultiplayerAllowed && bGameSetting_Online)) + { + m_MoreOptionsParams.bAllowFriendsOfFriends = TRUE; + } + } + + Minecraft *pMinecraft = Minecraft::GetInstance(); + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(m_MoreOptionsParams.dwTexturePack); + + UpdateTexturePackDescription(m_currentTexturePackIndex); + + m_texturePackList.selectSlot(m_currentTexturePackIndex); + + //m_labelGameName.setLabel(m_XContentData.szDisplayName); + + ui.NavigateBack(m_iPad, false, getSceneType() ); + } + + if(m_iSetTexturePackDescription >= 0 ) + { + UpdateTexturePackDescription( m_iSetTexturePackDescription ); + m_iSetTexturePackDescription = -1; + } + if(m_bShowTexturePackDescription) + { + slideLeft(); + m_texturePackDescDisplayed = true; + + m_bShowTexturePackDescription = false; + } + + if(m_bRequestQuadrantSignin) + { + m_bRequestQuadrantSignin = false; + SignInInfo info; + info.Func = &UIScene_LoadMenu::StartGame_SignInReturned; + info.lpParam = this; + info.requireOnline = m_MoreOptionsParams.bOnlineGame; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_QuadrantSignin,&info); + } + +#ifdef __ORBIS__ + // check the status of the PSPlus common dialog + switch (sceNpCommerceDialogUpdateStatus()) + { + case SCE_COMMON_DIALOG_STATUS_FINISHED: + { + SceNpCommerceDialogResult Result; + sceNpCommerceDialogGetResult(&Result); + sceNpCommerceDialogTerminate(); + + if(Result.authorized) + { + ProfileManager.PsPlusUpdate(ProfileManager.GetPrimaryPad(), &Result); + // they just became a PSPlus member + LoadDataComplete(this); + } + else + { + // continue offline? + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_DECLINE; + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_PLAY_OFFLINE,IDS_NO_PLAYSTATIONPLUS, uiIDA, 1, ProfileManager.GetPrimaryPad(),&UIScene_LoadMenu::ContinueOffline,this,app.GetStringTable(), 0, 0, false); + } + } + break; + default: + break; + } +#endif + + UIScene::tick(); +} + +#ifdef __ORBIS__ +int UIScene_LoadMenu::ContinueOffline(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultAccept) + { + pClass->m_MoreOptionsParams.bOnlineGame=false; + pClass->LoadDataComplete(pClass); + } + return 0; +} + +#endif + +void UIScene_LoadMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + app.SetCorruptSaveDeleted(false); + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + + // 4J-JEV: Inform user why their game must be offline. +#if defined _XBOX_ONE + if ( pressed && controlHasFocus(m_checkboxOnline.getId()) && !m_checkboxOnline.IsEnabled() ) + { + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } +#endif + + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + case ACTION_MENU_OTHER_STICK_UP: + case ACTION_MENU_OTHER_STICK_DOWN: + sendInputToMovie(key, repeat, pressed, released); + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + bool bOnlineGame = m_checkboxOnline.IsChecked(); + if (m_MoreOptionsParams.bOnlineGame != bOnlineGame) + { + m_MoreOptionsParams.bOnlineGame = bOnlineGame; + + if (!m_MoreOptionsParams.bOnlineGame) + { + m_MoreOptionsParams.bInviteOnly = false; + m_MoreOptionsParams.bAllowFriendsOfFriends = false; + } + } + } +#endif + handled = true; + break; + } +} + +void UIScene_LoadMenu::handlePress(F64 controlId, F64 childId) +{ + if(m_bIgnoreInput) return; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + switch((int)controlId) + { + case eControl_GameMode: + if(m_bGameModeSurvival) + { + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + else + { + m_buttonGamemode.setLabel(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + } + break; + case eControl_MoreOptions: + ui.NavigateToScene(m_iPad, eUIScene_LaunchMoreOptionsMenu, &m_MoreOptionsParams); + break; + case eControl_TexturePackList: + { + UpdateCurrentTexturePack((int)childId); + } + break; + case eControl_LoadWorld: + { +#ifdef _DURANGO + if(m_MoreOptionsParams.bOnlineGame) + { + m_bIgnoreInput = true; + ProfileManager.CheckMultiplayerPrivileges(m_iPad, true, &checkPrivilegeCallback, this); + } + else +#endif + { + StartSharedLaunchFlow(); + } + } + break; + }; +} + +#ifdef _DURANGO +void UIScene_LoadMenu::checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)lpParam; + + if(hasPrivilege) + { + pClass->StartSharedLaunchFlow(); + } + else + { + pClass->m_bIgnoreInput = false; + } +} +#endif + +void UIScene_LoadMenu::StartSharedLaunchFlow() +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + // Check if we need to upsell the texture pack + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { +#if TO_BE_IMPLEMENTED + // They've selected a texture pack they don't have yet + // upsell + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(m_MoreOptionsParams.dwTexturePack,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + //uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&TexturePackDialogReturned,this,app.GetStringTable(),NULL,0,false); + return; + } + } + m_bIgnoreInput = true; + + // if the profile data has been changed, then force a profile write (we save the online/invite/friends of friends settings) + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + // check the checkboxes + + // Only save the online setting if the user changed it - we may change it because we're offline, but don't want that saved + if(!m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + app.SetGameSettings(m_iPad,eGameSetting_Online,m_MoreOptionsParams.bOnlineGame?1:0); + } + app.SetGameSettings(m_iPad,eGameSetting_InviteOnly,m_MoreOptionsParams.bInviteOnly?1:0); + app.SetGameSettings(m_iPad,eGameSetting_FriendsOfFriends,m_MoreOptionsParams.bAllowFriendsOfFriends?1:0); + + app.CheckGameSettingsChanged(true,m_iPad); + + // Check that we have the rights to use a texture pack we have selected. + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)pTexturePack; + m_pDLCPack=pDLCTexPack->getDLCInfoParentPack(); + + // do we have a license? + if(m_pDLCPack && !m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // no + + // We need to allow people to use a trial texture pack if they are offline - we only need them online if they want to buy it. + + /* + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + if(!ProfileManager.IsSignedInLive(m_iPad)) + { + // need to be signed in to live + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + m_bIgnoreInput = false; + return; + } + else */ + { + // upsell +#ifdef _XBOX + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_pDLCPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=pTexturePack->getDLCPack()->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the texture pack + TelemetryManager->RecordUpsellPresented(m_iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + +#if defined(_WINDOWS64) || defined(_DURANGO) + // trial pack warning + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_USING_TRIAL_TEXUREPACK_WARNING, uiIDA, 1, m_iPad,&TrialTexturePackWarningReturned,this,app.GetStringTable(),NULL,0,false); +#elif defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // trial pack warning + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_USING_TRIAL_TEXUREPACK_WARNING, uiIDA, 2, m_iPad,&TrialTexturePackWarningReturned,this,app.GetStringTable(),NULL,0,false); +#endif + +#if defined _XBOX_ONE || defined __ORBIS__ + StorageManager.SetSaveDisabled(true); +#endif + return; + } + } + } + +#if defined _XBOX_ONE || defined __ORBIS__ + app.SetGameHostOption(eGameHostOption_DisableSaving, m_MoreOptionsParams.bDisableSaving?1:0); + + StorageManager.SetSaveDisabled(m_MoreOptionsParams.bDisableSaving); +#endif + +#if TO_BE_IMPLEMENTED + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); +#endif + + // Check if they have the Reset Nether flag set, and confirm they want to do this + if(m_MoreOptionsParams.bResetNether==TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_DONT_RESET_NETHER; + uiIDA[1]=IDS_RESET_NETHER; + + ui.RequestMessageBox(IDS_RESETNETHER_TITLE, IDS_RESETNETHER_TEXT, uiIDA, 2, m_iPad,&UIScene_LoadMenu::CheckResetNetherReturned,this,app.GetStringTable(),NULL,0,false); + } + else + { + LaunchGame(); + } +} + +void UIScene_LoadMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_Difficulty: + m_sliderDifficulty.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[value])); + m_sliderDifficulty.setLabel(TempString); + break; + } +} + +void UIScene_LoadMenu::handleTouchBoxRebuild() +{ + m_bRebuildTouchBoxes = true; +} + + +void UIScene_LoadMenu::handleTimerComplete(int id) +{ +#ifdef __PSVITA__ + // we cannot rebuild touch boxes in an iggy callback because it requires further iggy calls + if(m_bRebuildTouchBoxes) + { + GetMainPanel()->UpdateControl(); + ui.TouchBoxRebuild(this); + m_bRebuildTouchBoxes = false; + } +#endif + + switch(id) + { + case GAME_CREATE_ONLINE_TIMER_ID: + { + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + m_checkboxOnline.SetEnable(bMultiplayerAllowed); + m_checkboxOnline.setChecked(m_MoreOptionsParams.bOnlineGame); + } +#endif + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + // 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + +#if defined(__PS3__) || defined(__ORBIS__) + for(int i=0;ichImageURL); + + if(hasRegisteredSubstitutionTexture(textureName)==false) + { + PBYTE pbImageData; + int iImageDataBytes=0; + SonyHttp::getDataFromURL(pDLCInfo->chImageURL,(void **)&pbImageData,&iImageDataBytes); + + if(iImageDataBytes!=0) + { + // set the image + registerSubstitutionTexture(textureName,pbImageData,iImageDataBytes,true); + // add an item in + m_texturePackList.addPack(m_iConfigA[i],textureName); + m_iConfigA[i]=-1; + } + } + else + { + // already have the image, so add an item in + m_texturePackList.addPack(m_iConfigA[i],textureName); + m_iConfigA[i]=-1; + } + } + } + } + + bool bAllDone=true; + for(int i=0;iSaveInfoA[(int)m_iSaveGameInfoIndex].UTF8SaveTitle,pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex].UTF8SaveFilename); +#endif + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex],&LoadSaveDataReturned,this); + +#if TO_BE_IMPLEMENTED + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,this); + + } +#endif + } + } + else + { + // ask if they're sure they want to turn this into a creative map + ui.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_CREATIVE, uiIDA, 2, m_iPad,&UIScene_LoadMenu::ConfirmLoadReturned,this,app.GetStringTable(),NULL,0,false); + } + } + } + else + { + ui.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_HOST_PRIVILEGES, uiIDA, 2, m_iPad,&UIScene_LoadMenu::ConfirmLoadReturned,this,app.GetStringTable(),NULL,0,false); + } + } + else + { + if(m_levelGen != NULL) + { + LoadLevelGen(m_levelGen); + } + else + { + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); +#ifndef _DURANGO + app.DebugPrintf("Loading save %s [%s]\n",pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex].UTF8SaveTitle,pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex].UTF8SaveFilename); +#endif + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[(int)m_iSaveGameInfoIndex],&LoadSaveDataReturned,this); + +#if TO_BE_IMPLEMENTED + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,this); + } +#endif + } + } + //return 0; +} + +int UIScene_LoadMenu::CheckResetNetherReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // continue and reset the nether + pClass->LaunchGame(); + } + else if(result==C4JStorage::EMessage_ResultAccept) + { + // turn off the reset nether and continue + pClass->m_MoreOptionsParams.bResetNether=FALSE; + pClass->LaunchGame(); + } + else + { + // else they chose cancel + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int UIScene_LoadMenu::ConfirmLoadReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(pClass->m_levelGen != NULL) + { + pClass->LoadLevelGen(pClass->m_levelGen); + } + else + { + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); +#ifndef _DURANGO + app.DebugPrintf("Loading save %s [%s]\n",pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex].UTF8SaveTitle,pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex].UTF8SaveFilename); +#endif + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex],&LoadSaveDataReturned,pClass); + +#if TO_BE_IMPLEMENTED + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,this); + } +#endif + } + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int UIScene_LoadMenu::LoadDataComplete(void *pParam) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + if(!pClass->m_bIsCorrupt) + { + int iPrimaryPad = ProfileManager.GetPrimaryPad(); + bool isSignedInLive = true; + bool isOnlineGame = pClass->m_MoreOptionsParams.bOnlineGame; + int iPadNotSignedInLive = -1; + bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable(); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if (ProfileManager.IsSignedIn(i) && ((i == iPrimaryPad) || isLocalMultiplayerAvailable)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + } + } + + // If this is an online game but not all players are signed in to Live, stop! + if (isOnlineGame && !isSignedInLive) + { +#ifdef __ORBIS__ + assert(iPadNotSignedInLive != -1); + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + pClass->m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + pClass->m_bIgnoreInput=true; + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_CANCEL; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPadNotSignedInLive, &UIScene_LoadMenu::MustSignInReturnedPSN, pClass, app.GetStringTable(), NULL, 0, false); + } + return 0; +#else + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + return 0; +#endif + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + bool bContentRestricted = false; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); +#if defined(__PS3__) || defined(__PSVITA__) + if(isOnlineGame) + { + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,NULL,&bContentRestricted,NULL); + } +#endif + +#ifdef __ORBIS__ + bool bPlayStationPlus=true; + int iPadWithNoPlaystationPlus=0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(ProfileManager.IsSignedIn(i) && ((i == iPrimaryPad) || isLocalMultiplayerAvailable)) + { + if(!ProfileManager.HasPlayStationPlus(i)) + { + bPlayStationPlus=false; + iPadWithNoPlaystationPlus=i; + break; + } + } + } +#endif + noUGC = !pccAllowed && !pccFriendsAllowed; + + if(!isOnlineGame || !isLocalMultiplayerAvailable) + { + if(isOnlineGame && noUGC ) + { + pClass->setVisible( true ); + + ui.RequestUGCMessageBox(); + + pClass->m_bIgnoreInput=false; + } + else if(isOnlineGame && bContentRestricted ) + { + pClass->setVisible( true ); + + ui.RequestContentRestrictedMessageBox(); + pClass->m_bIgnoreInput=false; + } +#ifdef __ORBIS__ + else if(isOnlineGame && (bPlayStationPlus==false)) + { + pClass->setVisible( true ); + pClass->m_bIgnoreInput=false; + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); + +// UINT uiIDA[2]; +// uiIDA[0]=IDS_PLAY_OFFLINE; +// uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; +// ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_LoadMenu::PSPlusReturned,pClass, app.GetStringTable(),NULL,0,false); + } + +#endif + else + { + +#if defined(__ORBIS__) || defined(__PSVITA__) + if(isOnlineGame) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() ); + } + } +#endif + DWORD dwLocalUsersMask = CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + // No guest problems so we don't need to force a sign-in of players here + StartGameFromSave(pClass, dwLocalUsersMask); + } + } + else + { + // 4J-PB not sure why we aren't checking the content restriction for the main player here when multiple controllers are connected - adding now + if(isOnlineGame && noUGC ) + { + pClass->setVisible( true ); + ui.RequestUGCMessageBox(); + pClass->m_bIgnoreInput=false; + } + else if(isOnlineGame && bContentRestricted ) + { + pClass->setVisible( true ); + ui.RequestContentRestrictedMessageBox(); + pClass->m_bIgnoreInput=false; + } +#ifdef __ORBIS__ + else if(bPlayStationPlus==false) + { + pClass->setVisible( true ); + pClass->m_bIgnoreInput=false; + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); + +// UINT uiIDA[2]; +// uiIDA[0]=IDS_PLAY_OFFLINE; +// uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; +// ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_LoadMenu::PSPlusReturned,pClass, app.GetStringTable(),NULL,0,false); + } +#endif + else + { + pClass->m_bRequestQuadrantSignin = true; + } + } + } + else + { + // the save is corrupt! + pClass->m_bIgnoreInput=false; + + // give the option to delete the save + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, pClass->m_iPad,&UIScene_LoadMenu::DeleteSaveDialogReturned,pClass, app.GetStringTable(),NULL,0,false); + + } + + return 0; +} + +int UIScene_LoadMenu::LoadSaveDataReturned(void *pParam,bool bIsCorrupt, bool bIsOwner) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + pClass->m_bIsCorrupt=bIsCorrupt; + if(bIsOwner) + { + LoadDataComplete(pClass); + } + else + { + // messagebox + pClass->m_bIgnoreInput=false; + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + // show the message that trophies are disabled + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_SAVEDATA_COPIED_TITLE, IDS_SAVEDATA_COPIED_TEXT, uiIDA, 1, + pClass->m_iPad,&UIScene_LoadMenu::TrophyDialogReturned,pClass, app.GetStringTable()); +#endif + } + + + return 0; +} + +int UIScene_LoadMenu::TrophyDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + return LoadDataComplete(pClass); +} + +int UIScene_LoadMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + StorageManager.DeleteSaveData(&pSaveDetails->SaveInfoA[(int)pClass->m_iSaveGameInfoIndex],UIScene_LoadMenu::DeleteSaveDataReturned,pClass); + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int UIScene_LoadMenu::DeleteSaveDataReturned(void *pParam,bool bSuccess) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + app.SetCorruptSaveDeleted(true); + pClass->navigateBack(); + + return 0; +} + +// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not +void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocalUsersMask) +{ + INT saveOrCheckpointId = 0; + bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + TelemetryManager->RecordLevelResume(pClass->m_iPad, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, app.GetGameSettings(pClass->m_iPad,eGameSetting_Difficulty), app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount(), saveOrCheckpointId); + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode()) + { + if(SQRNetworkManager_AdHoc_Vita::GetAdhocStatus())// && pClass->m_MoreOptionsParams.bOnlineGame) + isClientSide = true; + } +#endif // __PSVITA__ + + bool isPrivate = (app.GetGameSettings(pClass->m_iPad,eGameSetting_InviteOnly)>0)?true:false; + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = pClass->m_seed; + param->saveData = NULL; + param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack); + //pMinecraft->skins->updateUI(); + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,app.GetGameSettings(pClass->m_iPad,eGameSetting_FriendsOfFriends)); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(pClass->m_iPad,eGameSetting_GamertagsVisible)); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(pClass->m_iPad,eGameSetting_BedrockFog)?1:0); + + app.SetGameHostOption(eGameHostOption_PvP,pClass->m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,pClass->m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,pClass->m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,pClass->m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,pClass->m_MoreOptionsParams.bHostPrivileges ); + + // flag if the user wants to reset the Nether to force a Fortress with netherwart etc. + app.SetResetNether((pClass->m_MoreOptionsParams.bResetNether==TRUE)?true:false); + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + app.SetGameHostOption(eGameHostOption_GameType,pClass->m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId() ); + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave time + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +void UIScene_LoadMenu::checkStateAndStartGame() +{ + // Check if they have the Reset Nether flag set, and confirm they want to do this + if(m_MoreOptionsParams.bResetNether==TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_DONT_RESET_NETHER; + uiIDA[1]=IDS_RESET_NETHER; + + ui.RequestMessageBox(IDS_RESETNETHER_TITLE, IDS_RESETNETHER_TEXT, uiIDA, 2, m_iPad,&UIScene_LoadMenu::CheckResetNetherReturned,this,app.GetStringTable(),NULL,0,false); + } + else + { + LaunchGame(); + } +} + +void UIScene_LoadMenu::LoadLevelGen(LevelGenerationOptions *levelGen) +{ + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && m_MoreOptionsParams.bOnlineGame; + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(!isClientSide || connectedControllers == 1 || !RenderManager.IsHiDef()) + { + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + return; + } + + } + + DWORD dwLocalUsersMask = 0; + + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + // Load data from disc + //File saveFile( L"Tutorial\\Tutorial" ); + //LoadSaveFromDisk(&saveFile); + + StorageManager.ResetSaveData(); + // Make our next save default to the name of the level + StorageManager.SetSaveTitle(levelGen->getDefaultSaveName().c_str()); + + bool isPrivate = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)>0)?true:false; + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = NULL; + param->levelGen = levelGen; + + if(levelGen->requiresTexturePack()) + { + param->texturePackId = levelGen->getRequiredTexturePackId(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(param->texturePackId); + //pMinecraft->skins->updateUI(); + } + + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(m_iPad,eGameSetting_GamertagsVisible)); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(m_iPad,eGameSetting_BedrockFog)?1:0); + + app.SetGameHostOption(eGameHostOption_PvP,m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,m_MoreOptionsParams.bHostPrivileges ); + + // flag if the user wants to reset the Nether to force a Fortress with netherwart etc. + app.SetResetNether((m_MoreOptionsParams.bResetNether==TRUE)?true:false); + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + app.SetGameHostOption(eGameHostOption_GameType,m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId() ); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave time + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +int UIScene_LoadMenu::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(pClass->m_iPad)) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + bool noPrivileges = false; + DWORD dwLocalUsersMask = 0; + bool isSignedInLive = ProfileManager.IsSignedInLive(primaryPad); + bool isOnlineGame = pClass->m_MoreOptionsParams.bOnlineGame; + int iPadNotSignedInLive = -1; + bool isLocalMultiplayerAvailable = app.IsLocalMultiplayerAvailable(); + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if (ProfileManager.IsSignedIn(i) && ((i == primaryPad) || isLocalMultiplayerAvailable)) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + if( !ProfileManager.AllowedToPlayMultiplayer(i) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(i); + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + } + } + + // If this is an online game but not all players are signed in to Live, stop! + if (isOnlineGame && !isSignedInLive) + { +#ifdef __ORBIS__ + assert(iPadNotSignedInLive != -1); + + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + pClass->m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + pClass->m_bIgnoreInput=true; + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_CANCEL; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPadNotSignedInLive, &UIScene_LoadMenu::MustSignInReturnedPSN, pClass, app.GetStringTable(), NULL, 0, false); + } + return 0; +#else + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + return 0; +#endif + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isSignedInLive && isOnlineGame && (noPrivileges || noUGC) ) + { + if( noUGC ) + { + pClass->m_bIgnoreInput = false; + pClass->setVisible( true ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + } + else + { + pClass->m_bIgnoreInput = false; + pClass->setVisible( true ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(),NULL,0,false); + } + } + else + { +#if defined( __ORBIS__) || defined(__PSVITA__) + if(isOnlineGame) + { + // show the chat restriction message for all users that it applies to + for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++) + { + if(ProfileManager.IsSignedInLive(i)) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(i,false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, i ); + } + } + } + } +#endif + // This is NOT called from a storage manager thread, and is in fact called from the main thread in the Profile library tick. Therefore we use the main threads IntCache. + StartGameFromSave(pClass, dwLocalUsersMask); + } + } + } + else + { + pClass->m_bIgnoreInput=false; + } + + return 0; +} + +void UIScene_LoadMenu::handleGainFocus(bool navBack) +{ + if(navBack) + { + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + if(getSceneResolution() == eSceneResolution_1080) + { + m_checkboxOnline.setChecked(m_MoreOptionsParams.bOnlineGame == TRUE); + } +#endif + } +} + +#ifdef __ORBIS__ +int UIScene_LoadMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadMenu* pClass = (UIScene_LoadMenu *)pParam; + pClass->m_bIgnoreInput = false; + + if(result==C4JStorage::EMessage_ResultAccept) + { + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_LoadMenu::StartGame_SignInReturned, pClass, false, iPad); + } + + return 0; +} + +// int UIScene_LoadMenu::PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +// { +// int32_t iResult; +// UIScene_LoadMenu *pClass = (UIScene_LoadMenu *)pParam; +// +// // continue offline, or upsell PS Plus? +// if(result==C4JStorage::EMessage_ResultDecline) +// { +// // upsell psplus +// iResult=sceNpCommerceDialogInitialize(); +// +// SceNpCommerceDialogParam param; +// sceNpCommerceDialogParamInitialize(¶m); +// param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; +// param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; +// param.userId = ProfileManager.getUserID(pClass->m_iPad); +// +// +// iResult=sceNpCommerceDialogOpen(¶m); +// } +// else if(result==C4JStorage::EMessage_ResultAccept) +// { +// // continue offline +// pClass->m_MoreOptionsParams.bOnlineGame=false; +// pClass->LoadDataComplete(pClass); +// } +// +// pClass->m_bIgnoreInput=false; +// return 0; +// } +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.h b/Minecraft.Client/Common/UI/UIScene_LoadMenu.h new file mode 100644 index 0000000..e45fa09 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.h @@ -0,0 +1,131 @@ +#pragma once + +#include "IUIScene_StartGame.h" + +class UIScene_LoadMenu : public IUIScene_StartGame +{ +private: + enum EControls + { + eControl_GameMode, + eControl_Difficulty, + eControl_MoreOptions, + eControl_LoadWorld, + eControl_TexturePackList, + eControl_OnlineGame, + }; + + static int m_iDifficultyTitleSettingA[4]; + + UIControl m_controlMainPanel; + UIControl_Label m_labelGameName, m_labelSeed, m_labelCreatedMode; + UIControl_Button m_buttonGamemode, m_buttonMoreOptions, m_buttonLoadWorld; + UIControl_Slider m_sliderDifficulty; + UIControl_BitmapIcon m_bitmapIcon; + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + UIControl_CheckBox m_checkboxOnline; +#endif + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(IUIScene_StartGame) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_labelGameName, "GameName") + UI_MAP_ELEMENT( m_labelCreatedMode, "CreatedMode") + UI_MAP_ELEMENT( m_labelSeed, "Seed") + UI_MAP_ELEMENT( m_texturePackList, "TexturePackSelector") + UI_MAP_ELEMENT( m_buttonGamemode, "GameModeToggle") + +#if defined _XBOX_ONE || defined __ORBIS__ || defined _WINDOWS64 + UI_MAP_ELEMENT( m_checkboxOnline, "CheckboxOnline") +#endif + UI_MAP_ELEMENT( m_buttonMoreOptions, "MoreOptions") + UI_MAP_ELEMENT( m_buttonLoadWorld, "LoadSettings") + UI_MAP_ELEMENT( m_sliderDifficulty, "Difficulty") + UI_MAP_ELEMENT( m_bitmapIcon, "LevelIcon") + UI_END_MAP_CHILD_ELEMENTS() + UI_END_MAP_ELEMENTS_AND_NAMES() + + LevelGenerationOptions *m_levelGen; + DLCPack * m_pDLCPack; + + int m_iSaveGameInfoIndex; + int m_CurrentDifficulty; + bool m_bGameModeSurvival; + bool m_bHasBeenInCreative; + bool m_bRetrievingSaveThumbnail; + bool m_bSaveThumbnailReady; + bool m_bMultiplayerAllowed; + bool m_bShowTimer; + bool m_bAvailableTexturePacksChecked; + bool m_bRequestQuadrantSignin; + bool m_bIsCorrupt; + bool m_bThumbnailGetFailed; + __int64 m_seed; + +#ifdef __PS3__ + std::vector*m_pvProductInfo; +#endif + //int *m_iConfigA; // track the texture packs that we don't have installed + + PBYTE m_pbThumbnailData; + unsigned int m_uiThumbnailSize; + wstring m_thumbnailName; + + bool m_bRebuildTouchBoxes; +public: + UIScene_LoadMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_LoadMenu;} + + virtual void tick(); + + virtual UIControl* GetMainPanel(); + + virtual void handleTouchBoxRebuild(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleTimerComplete(int id); + +protected: + void handlePress(F64 controlId, F64 childId); + void handleSliderMove(F64 sliderId, F64 currentValue); + virtual void handleGainFocus(bool navBack); + +private: + void StartSharedLaunchFlow(); + virtual void checkStateAndStartGame(); + void LoadLevelGen(LevelGenerationOptions *levelGen); + void LaunchGame(void); + +#ifdef _DURANGO + static void checkPrivilegeCallback(LPVOID lpParam, bool hasPrivilege, int iPad); +#endif + + static int ConfirmLoadReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static void StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocalUsersMask); + static int LoadSaveDataReturned(void *pParam,bool bIsCorrupt, bool bIsOwner); + static int TrophyDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int LoadDataComplete(void *pParam); + static int LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes); + static int CheckResetNetherReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDataReturned(void *pParam,bool bSuccess); + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); +#ifdef __ORBIS__ + //static int PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ContinueOffline(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +public: + static int StartGame_SignInReturned(LPVOID pParam, bool, int); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp new file mode 100644 index 0000000..ef5a339 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.cpp @@ -0,0 +1,3531 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_LoadOrJoinMenu.h" + +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFile.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileOriginal.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileSplit.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\MinecraftServer.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\Network\SessionInfo.h" +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) +#include "Common\Network\Sony\SonyHttp.h" +#include "Common\Network\Sony\SonyRemoteStorage.h" +#endif +#if defined(__ORBIS__) || defined(__PSVITA__) +#include +#endif +#ifdef __PSVITA__ +#include "message_dialog.h" +#endif + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD +unsigned long UIScene_LoadOrJoinMenu::m_ulFileSize=0L; +wstring UIScene_LoadOrJoinMenu::m_wstrStageText=L""; +#endif + + +#define JOIN_LOAD_ONLINE_TIMER_ID 0 +#define JOIN_LOAD_ONLINE_TIMER_TIME 100 + +#ifdef _XBOX +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 3 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 50 +#endif + +#ifdef _XBOX_ONE +UIScene_LoadOrJoinMenu::ESaveTransferFiles UIScene_LoadOrJoinMenu::s_eSaveTransferFile; +unsigned long UIScene_LoadOrJoinMenu::s_ulFileSize=0L; +byteArray UIScene_LoadOrJoinMenu::s_transferData = byteArray(); +wstring UIScene_LoadOrJoinMenu::m_wstrStageText=L""; + +#ifdef _DEBUG_MENUS_ENABLED +C4JStorage::SAVETRANSFER_FILE_DETAILS UIScene_LoadOrJoinMenu::m_debugTransferDetails; +#endif +#endif + +int UIScene_LoadOrJoinMenu::LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes) +{ + UIScene_LoadOrJoinMenu *pClass= (UIScene_LoadOrJoinMenu *)lpParam; + + app.DebugPrintf("Received data for save thumbnail\n"); + + if(pbThumbnail && dwThumbnailBytes) + { + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = new BYTE[dwThumbnailBytes]; + memcpy(pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData, pbThumbnail, dwThumbnailBytes); + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = dwThumbnailBytes; + } + else + { + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = NULL; + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = 0; + app.DebugPrintf("Save thumbnail data is NULL, or has size 0\n"); + } + pClass->m_bSaveThumbnailReady = true; + + return 0; +} + +int UIScene_LoadOrJoinMenu::LoadSaveCallback(LPVOID lpParam,bool bRes) +{ + //UIScene_LoadOrJoinMenu *pClass= (UIScene_LoadOrJoinMenu *)lpParam; + // Get the save data now + if(bRes) + { + app.DebugPrintf("Loaded save OK\n"); + } + return 0; +} + +UIScene_LoadOrJoinMenu::UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + app.SetLiveLinkRequired( true ); + + m_iRequestingThumbnailId = 0; + m_iSaveInfoC=0; + m_bIgnoreInput = false; + m_bShowingPartyGamesOnly = false; + m_bInParty = false; + m_currentSessions = NULL; + m_iState=e_SavesIdle; + //m_bRetrievingSaveInfo=false; + + m_buttonListSaves.init(eControl_SavesList); + m_buttonListGames.init(eControl_GamesList); + + m_labelSavesListTitle.init( app.GetString(IDS_START_GAME) ); + m_labelJoinListTitle.init( app.GetString(IDS_JOIN_GAME) ); + m_labelNoGames.init( app.GetString(IDS_NO_GAMES_FOUND) ); + m_labelNoGames.setVisible( false ); + m_controlSavesTimer.setVisible( true ); + m_controlJoinTimer.setVisible( true ); + + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + m_spaceIndicatorSaves.init(L"",eControl_SpaceIndicator,0, (4LL *1024LL * 1024LL * 1024LL) ); +#endif + m_bUpdateSaveSize = false; + + m_bAllLoaded = false; + m_bRetrievingSaveThumbnails = false; + m_bSaveThumbnailReady = false; + m_bExitScene=false; + m_pSaveDetails=NULL; + m_bSavesDisplayed=false; + m_saveDetails = NULL; + m_iSaveDetailsCount = 0; + m_iTexturePacksNotInstalled = 0; + m_bCopying = false; + m_bCopyingCancelled = false; + +#ifndef _XBOX_ONE + m_bSaveTransferCancelled=false; + m_bSaveTransferInProgress=false; +#endif + m_eAction = eAction_None; + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + +#ifdef _XBOX_ONE + // 4J-PB - in order to buy the skin packs & texture packs, we need the signed offer ids for them, which we get in the availability info + // we need to retrieve this info though, so do it here + app.AddDLCRequest(e_Marketplace_Content); // content is skin packs, texture packs and mash-up packs +#endif + + + int iLB = -1; + +#ifdef _XBOX + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } +#endif + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) + // Always clear the saves when we enter this menu + StorageManager.ClearSavesInfo(); +#endif + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true || app.DLCInstallPending()) + { + // if we're waiting for DLC to mount, don't fill the save list. The custom message on end of dlc mounting will do that + m_bIgnoreInput = true; + } + else + { + Initialise(); + } + +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() && SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + { + g_NetworkManager.startAdhocMatching(); // create the client matching context and clear out the friends list + } + +#endif + + UpdateGamesList(); + + g_NetworkManager.SetSessionsUpdatedCallback( &UpdateGamesListCallback, this ); + + m_initData= new JoinMenuInitData(); + + // 4J Stu - Fix for #12530 -TCR 001 BAS Game Stability: Title will crash if the player disconnects while starting a new world and then opts to play the tutorial once they have been returned to the Main Menu. + MinecraftServer::resetFlags(); + + // If we're not ignoring input, then we aren't still waiting for the DLC to mount, and can now check for corrupt dlc. Otherwise this will happen when the dlc has finished mounting. + if( !m_bIgnoreInput) + { + app.m_dlcManager.checkForCorruptDLCAndAlert(); + } + + // 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; +#if defined(__PS3__) || defined(__ORBIS__) + char *pchDLCName=app.GetDLCInfoTextures(i); + pDLCInfo=app.GetDLCInfo(pchDLCName); +#else + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); +#endif + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo && pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; +#if defined(__PS3__) || defined(__ORBIS__) + char *pchDLCName=app.GetDLCInfoTextures(i); + pDLCInfo=app.GetDLCInfo(pchDLCName); +#else + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); +#endif + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + + addTimer(CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); +#endif + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + m_eSaveTransferState = eSaveTransfer_Idle; +#endif +} + + +UIScene_LoadOrJoinMenu::~UIScene_LoadOrJoinMenu() +{ + g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL ); + app.SetLiveLinkRequired( false ); + + delete m_currentSessions; + m_currentSessions = NULL; + +#if TO_BE_IMPLEMENTED + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); +#endif + + if(m_saveDetails) + { + for(int i = 0; i < m_iSaveDetailsCount; ++i) + { + delete m_saveDetails[i].pbThumbnailData; + } + delete [] m_saveDetails; + } +} + +void UIScene_LoadOrJoinMenu::updateTooltips() +{ + // update the tooltips + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should the the View Gamercard tooltip + int iRB=-1; + int iY = -1; + int iLB = -1; + int iX=-1; + if (DoesGamesListHaveFocus() && m_buttonListGames.getItemCount() > 0) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if (DoesSavesListHaveFocus()) + { + if((m_iDefaultButtonsC > 0) && (m_iSaveListIndex >= m_iDefaultButtonsC)) + { + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(m_iPad == ProfileManager.GetPrimaryPad() ) iY = IDS_TOOLTIPS_GAME_INVITES; +#endif + + if(ProfileManager.IsFullVersion()==false ) + { + iRB = -1; + } + else if(StorageManager.GetSaveDisabled()) + { +#ifdef _XBOX + iX = IDS_TOOLTIPS_SELECTDEVICE; +#endif + } + else + { +#if defined _XBOX_ONE + if(ProfileManager.IsSignedInLive( m_iPad )) + { + // Is there a save from 360 on TMS? + iX=IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD; + } +#elif defined SONY_REMOTE_STORAGE_DOWNLOAD + // Is there a save from PS3 or PSVita available? + // Sony asked that this be displayed at all times so users are aware of the functionality. We'll display some text when there's no save available + //if(app.getRemoteStorage()->saveIsAvailable()) + { + bool bSignedInLive = ProfileManager.IsSignedInLive(m_iPad); + if(bSignedInLive) + { + iX=IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD; + } + } +#else + iX = IDS_TOOLTIPS_CHANGEDEVICE; +#endif + } + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, iX, iY,-1,-1,iLB,iRB); +} + +// +void UIScene_LoadOrJoinMenu::Initialise() +{ + m_iSaveListIndex = 0; + m_iGameListIndex = 0; + + m_iDefaultButtonsC = 0; + m_iMashUpButtonsC=0; + + // Check if we're in the trial version + if(ProfileManager.IsFullVersion()==false) + { + + + AddDefaultButtons(); + +#if TO_BE_IMPLEMENTED + m_pSavesList->SetCurSelVisible(0); +#endif + } + else if(StorageManager.GetSaveDisabled()) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + GetSaveInfo(); +#else + +#if TO_BE_IMPLEMENTED + if(StorageManager.GetSaveDeviceSelected(m_iPad)) +#endif + { + // saving is disabled, but we should still be able to load from a selected save device + + + + GetSaveInfo(); + } +#if TO_BE_IMPLEMENTED + else + { + AddDefaultButtons(); + m_controlSavesTimer.setVisible( false ); + } +#endif +#endif // __PS3__ || __ORBIS + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame(); + + GetSaveInfo(); + } + + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); +} + +void UIScene_LoadOrJoinMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); +} + +void UIScene_LoadOrJoinMenu::handleDestroy() +{ +#ifdef __PSVITA__ + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); +#endif + + // shut down the keyboard if it is displayed +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + InputManager.DestroyKeyboard(); +#endif +} + +void UIScene_LoadOrJoinMenu::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + + updateTooltips(); + + // Add load online timer + addTimer(JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME); + + if(navBack) + { + app.SetLiveLinkRequired( true ); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + // re-enable button presses + m_bIgnoreInput=false; + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_buttonListSaves.clearList(); + m_controlSavesTimer.setVisible(true); + } + + if( m_bMultiplayerAllowed ) + { +#if TO_BE_IMPLEMENTED + HXUICLASS hClassFullscreenProgress = XuiFindClass( L"CScene_FullscreenProgress" ); + HXUICLASS hClassConnectingProgress = XuiFindClass( L"CScene_ConnectingProgress" ); + + // If we are navigating back from a full screen progress scene, then that means a connection attempt failed + if( XuiIsInstanceOf( hSceneFrom, hClassFullscreenProgress ) || XuiIsInstanceOf( hSceneFrom, hClassConnectingProgress ) ) + { + UpdateGamesList(); + } +#endif + } + else + { + m_buttonListGames.clearList(); + m_controlJoinTimer.setVisible(true); + m_labelNoGames.setVisible(false); +#if TO_BE_IMPLEMENTED + m_SavesList.InitFocus(m_iPad); +#endif + } + + // are we back here because of a delete of a corrupt save? + + if(app.GetCorruptSaveDeleted()) + { + // wipe the list and repopulate it + m_iState=e_SavesRepopulateAfterDelete; + app.SetCorruptSaveDeleted(false); + } + } +} + +void UIScene_LoadOrJoinMenu::handleLoseFocus() +{ + // Kill load online timer + killTimer(JOIN_LOAD_ONLINE_TIMER_ID); +} + +wstring UIScene_LoadOrJoinMenu::getMoviePath() +{ + return L"LoadOrJoinMenu"; +} + +void UIScene_LoadOrJoinMenu::tick() +{ + UIScene::tick(); + +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 || defined __PSVITA__) + if(m_bExitScene) // navigate forward or back + { + if(!m_bRetrievingSaveThumbnails) + { + // need to wait for any callback retrieving thumbnail to complete + navigateBack(); + } + } + // Stop loading thumbnails if we navigate forwards + if(hasFocus(m_iPad)) + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + if(m_bUpdateSaveSize) + { + if((m_iDefaultButtonsC > 0) && (m_iSaveListIndex >= m_iDefaultButtonsC)) + { + m_spaceIndicatorSaves.selectSave(m_iSaveListIndex-m_iDefaultButtonsC); + } + else + { + m_spaceIndicatorSaves.selectSave(-1); + } + m_bUpdateSaveSize = false; + } +#endif + // Display the saves if we have them + if(!m_bSavesDisplayed) + { + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + if(m_pSaveDetails!=NULL) + { + //CD - Fix - Adding define for ORBIS/XBOXONE +#if defined(_XBOX_ONE) || defined(__ORBIS__) + m_spaceIndicatorSaves.reset(); +#endif + + AddDefaultButtons(); + m_bSavesDisplayed=true; + UpdateGamesList(); + + if(m_saveDetails!=NULL) + { + for(unsigned int i = 0; i < m_iSaveDetailsCount; ++i) + { + if(m_saveDetails[i].pbThumbnailData!=NULL) + { + delete m_saveDetails[i].pbThumbnailData; + } + } + delete m_saveDetails; + } + m_saveDetails = new SaveListDetails[m_pSaveDetails->iSaveC]; + + m_iSaveDetailsCount = m_pSaveDetails->iSaveC; + for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i) + { +#if defined(_XBOX_ONE) + m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].totalSize); +#elif defined(__ORBIS__) + m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].blocksUsed * (32 * 1024) ); +#endif +#ifdef _DURANGO + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, L""); + + m_saveDetails[i].saveId = i; + memcpy(m_saveDetails[i].UTF16SaveName, m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, 128); + memcpy(m_saveDetails[i].UTF16SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#else + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, L""); + + m_saveDetails[i].saveId = i; + memcpy(m_saveDetails[i].UTF8SaveName, m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, 128); + memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); +#endif + } + m_controlSavesTimer.setVisible( false ); + + // set focus on the first button + + } + } + + if(!m_bExitScene && m_bSavesDisplayed && !m_bRetrievingSaveThumbnails && !m_bAllLoaded) + { + if( m_iRequestingThumbnailId < (m_buttonListSaves.getItemCount() - m_iDefaultButtonsC )) + { + m_bRetrievingSaveThumbnails = true; + app.DebugPrintf("Requesting the first thumbnail\n"); + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + { + // something went wrong + m_bRetrievingSaveThumbnails=false; + m_bAllLoaded = true; + } + } + } + else if (m_bSavesDisplayed && m_bSaveThumbnailReady) + { + m_bSaveThumbnailReady = false; + + // check we're not waiting to exit the scene + if(!m_bExitScene) + { + // convert to utf16 + uint16_t u16Message[MAX_SAVEFILENAME_LENGTH]; +#ifdef _DURANGO + // Already utf16 on durango + memcpy(u16Message, m_saveDetails[m_iRequestingThumbnailId].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); +#elif defined(_WINDOWS64) + int result = ::MultiByteToWideChar( + CP_UTF8, // convert from UTF-8 + MB_ERR_INVALID_CHARS, // error on invalid chars + m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename, // source UTF-8 string + MAX_SAVEFILENAME_LENGTH, // total length of source UTF-8 string, + // in CHAR's (= bytes), including end-of-string \0 + (wchar_t *)u16Message, // destination buffer + MAX_SAVEFILENAME_LENGTH // size of destination buffer, in WCHAR's + ); +#else +#ifdef __PS3 + size_t srcmax,dstmax; +#else + uint32_t srcmax,dstmax; + uint32_t srclen,dstlen; +#endif + srcmax=MAX_SAVEFILENAME_LENGTH; + dstmax=MAX_SAVEFILENAME_LENGTH; + +#if defined(__PS3__) + L10nResult lres= UTF8stoUTF16s((uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,&srcmax,u16Message,&dstmax); +#else + SceCesUcsContext context; + sceCesUcsContextInit(&context); + + sceCesUtf8StrToUtf16Str(&context, (uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,srcmax,&srclen,u16Message,dstmax,&dstlen); +#endif +#endif + if( m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData ) + { + registerSubstitutionTexture((wchar_t *)u16Message,m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData,m_saveDetails[m_iRequestingThumbnailId].dwThumbnailSize); + } + m_buttonListSaves.setTextureName(m_iRequestingThumbnailId + m_iDefaultButtonsC, (wchar_t *)u16Message); + + ++m_iRequestingThumbnailId; + if( m_iRequestingThumbnailId < (m_buttonListSaves.getItemCount() - m_iDefaultButtonsC )) + { + app.DebugPrintf("Requesting another thumbnail\n"); + // set the save to load + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + { + // something went wrong + m_bRetrievingSaveThumbnails=false; + m_bAllLoaded = true; + } + } + else + { + m_bRetrievingSaveThumbnails = false; + m_bAllLoaded = true; + } + } + else + { + // stop retrieving thumbnails, and exit + m_bRetrievingSaveThumbnails = false; + } + } + } + + switch(m_iState) + { + case e_SavesIdle: + break; + case e_SavesRepopulate: + m_bIgnoreInput = false; + m_iState=e_SavesIdle; + m_bAllLoaded=false; + m_bRetrievingSaveThumbnails=false; + m_iRequestingThumbnailId = 0; + GetSaveInfo(); + break; + case e_SavesRepopulateAfterMashupHide: + m_bIgnoreInput = false; + m_iRequestingThumbnailId = 0; + m_bAllLoaded=false; + m_bRetrievingSaveThumbnails=false; + m_bSavesDisplayed=false; + m_iSaveInfoC=0; + m_buttonListSaves.clearList(); + GetSaveInfo(); + m_iState=e_SavesIdle; + break; + case e_SavesRepopulateAfterDelete: + case e_SavesRepopulateAfterTransferDownload: + m_bIgnoreInput = false; + m_iRequestingThumbnailId = 0; + m_bAllLoaded=false; + m_bRetrievingSaveThumbnails=false; + m_bSavesDisplayed=false; + m_iSaveInfoC=0; + m_buttonListSaves.clearList(); + StorageManager.ClearSavesInfo(); + GetSaveInfo(); + m_iState=e_SavesIdle; + break; + } +#else + if(!m_bSavesDisplayed) + { + AddDefaultButtons(); + m_bSavesDisplayed=true; + m_controlSavesTimer.setVisible( false ); + } +#endif + +#ifdef _XBOX_ONE + if(g_NetworkManager.ShouldMessageForFullSession()) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_IN_PARTY_SESSION_FULL, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } +#endif + + // SAVE TRANSFERS +#ifdef __ORBIS__ + // check the status of the PSPlus common dialog + switch (sceNpCommerceDialogUpdateStatus()) + { + case SCE_COMMON_DIALOG_STATUS_FINISHED: + { + SceNpCommerceDialogResult Result; + sceNpCommerceDialogGetResult(&Result); + sceNpCommerceDialogTerminate(); + + if(Result.authorized) + { + // they just became a PSPlus member + ProfileManager.PsPlusUpdate(ProfileManager.GetPrimaryPad(), &Result); + + } + else + { + + } + + // 4J-JEV: Fix for PS4 #5148 - [ONLINE] If the user attempts to join a game when they do not have Playstation Plus, the title will lose all functionality. + m_bIgnoreInput = false; + } + break; + default: + break; + } +#endif + +} + +void UIScene_LoadOrJoinMenu::GetSaveInfo() +{ + unsigned int uiSaveC=0; + + // This will return with the number retrieved in uiSaveC + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { +#ifdef __ORBIS__ + // We need to make sure this is non-null so that we have an idea of free space + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + if(m_pSaveDetails==NULL) + { + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(m_iPad,NULL,this,"save"); + } +#endif + + uiSaveC = 0; +#ifdef _XBOX + File savesDir(L"GAME:\\Saves"); +#else + File savesDir(L"Saves"); +#endif + if( savesDir.exists() ) + { + m_saves = savesDir.listFiles(); + uiSaveC = (unsigned int)m_saves->size(); + } + // add the New Game and Tutorial after the saves list is retrieved, if there are any saves + + // Add two for New Game and Tutorial + unsigned int listItems = uiSaveC; + + AddDefaultButtons(); + + for(unsigned int i=0;iat(i)->getName(); + wchar_t *name = new wchar_t[wName.size()+1]; + for(unsigned int j = 0; j < wName.size(); ++j) + { + name[j] = wName[j]; + } + name[wName.size()] = 0; + m_buttonListSaves.addItem(name,L""); + } + m_bSavesDisplayed = true; + m_bAllLoaded = true; + m_bIgnoreInput = false; + } + else + { + // clear the saves list + m_bSavesDisplayed = false; // we're blocking the exit from this scene until complete + m_buttonListSaves.clearList(); + m_iSaveInfoC=0; + m_controlSavesTimer.setVisible(true); + + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + if(m_pSaveDetails==NULL) + { + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(m_iPad,NULL,this,"save"); + } + +#if TO_BE_IMPLEMENTED + if(eSGIStatus==C4JStorage::ESGIStatus_NoSaves) + { + uiSaveC=0; + m_controlSavesTimer.setVisible( false ); + m_SavesList.SetEnable(TRUE); + } +#endif + } + + return; +} + +void UIScene_LoadOrJoinMenu::AddDefaultButtons() +{ + m_iDefaultButtonsC = 0; + m_iMashUpButtonsC=0; + m_generators.clear(); + + m_buttonListSaves.addItem(app.GetString(IDS_CREATE_NEW_WORLD)); + m_iDefaultButtonsC++; + + int i = 0; + + for(AUTO_VAR(it, app.getLevelGenerators()->begin()); it != app.getLevelGenerators()->end(); ++it) + { + LevelGenerationOptions *levelGen = *it; + + // retrieve the save icon from the texture pack, if there is one + unsigned int uiTexturePackID=levelGen->getRequiredTexturePackId(); + + if(uiTexturePackID!=0) + { + unsigned int uiMashUpWorldsBitmask=app.GetMashupPackWorlds(m_iPad); + + if((uiMashUpWorldsBitmask & (1<<(uiTexturePackID-1024)))==0) + { + // this world is hidden, so skip + continue; + } + } + + m_generators.push_back(levelGen); + m_buttonListSaves.addItem(levelGen->getWorldName()); + + if(uiTexturePackID!=0) + { + // increment the count of the mash-up pack worlds in the save list + m_iMashUpButtonsC++; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(levelGen->getRequiredTexturePackId()); + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + wchar_t imageName[64]; + swprintf(imageName,64,L"tpack%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + m_buttonListSaves.setTextureName( m_buttonListSaves.getItemCount() - 1, imageName ); + } + } + + ++i; + } + m_iDefaultButtonsC += i; +} + +void UIScene_LoadOrJoinMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; + + // if we're retrieving save info, ignore key presses + if(!m_bSavesDisplayed) return; + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + m_bExitScene=true; +#else + navigateBack(); +#endif + handled = true; + } + break; + case ACTION_MENU_X: +#if TO_BE_IMPLEMENTED + // Change device + // Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage + // device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes. + m_bIgnoreInput=true; + StorageManager.SetSaveDevice(&CScene_MultiGameJoinLoad::DeviceSelectReturned,this,true); + ui.PlayUISFX(eSFX_Press); +#endif + // Save Transfer +#ifdef _XBOX_ONE + if(ProfileManager.IsSignedInLive( m_iPad )) + { + UIScene_LoadOrJoinMenu::s_ulFileSize=0; + LaunchSaveTransfer(); + } +#endif +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + { + bool bSignedInLive = ProfileManager.IsSignedInLive(iPad); + if(bSignedInLive) + { + LaunchSaveTransfer(); + } + } +#endif + break; + case ACTION_MENU_Y: +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + m_eAction = eAction_ViewInvites; + if(pressed && iPad == ProfileManager.GetPrimaryPad()) + { +#ifdef __ORBIS__ + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + + break; + } +#endif + + // are we offline? + if(!ProfileManager.IsSignedInLive(iPad)) + { + // get them to sign in to online + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(), &UIScene_LoadOrJoinMenu::MustSignInReturnedPSN, this, app.GetStringTable(),NULL,0,false); + } + else + { +#ifdef __ORBIS__ + SQRNetworkManager_Orbis::RecvInviteGUI(); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::RecvInviteGUI(); +#else + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#endif + } + } +#elif defined(_DURANGO) + if(getControlFocus() == eControl_GamesList && m_buttonListGames.getItemCount() > 0) + { + DWORD nIndex = m_buttonListGames.getCurrentSelection(); + FriendSessionInfo *pSelectedSession = m_currentSessions->at( nIndex ); + + PlayerUID uid = pSelectedSession->searchResult.m_playerXuids[0]; + if( uid != INVALID_XUID ) ProfileManager.ShowProfileCard(ProfileManager.GetLockedProfile(),uid); + ui.PlayUISFX(eSFX_Press); + } +#endif // __PS3__ || __ORBIS__ + break; + + case ACTION_MENU_RIGHT_SCROLL: + if(DoesSavesListHaveFocus()) + { + // 4J-PB - check we are on a valid save + if((m_iDefaultButtonsC != 0) && (m_iSaveListIndex >= m_iDefaultButtonsC)) + { + m_bIgnoreInput = true; + + // Could be delete save or Save Options + if(StorageManager.GetSaveDisabled()) + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned,this, app.GetStringTable(),NULL,0,false); + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + UINT uiIDA[4]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_TITLE_RENAMESAVE; + uiIDA[2]=IDS_TOOLTIPS_DELETESAVE; + int numOptions = 3; +#ifdef SONY_REMOTE_STORAGE_UPLOAD + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + numOptions = 4; + uiIDA[3]=IDS_TOOLTIPS_SAVETRANSFER_UPLOAD; + } +#endif +#if defined _XBOX_ONE || defined __ORBIS__ + numOptions = 4; + uiIDA[3]=IDS_COPYSAVE; +#endif + ui.RequestMessageBox(IDS_TOOLTIPS_SAVEOPTIONS, IDS_TEXT_SAVEOPTIONS, uiIDA, numOptions, iPad,&UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned,this, app.GetStringTable(),NULL,0,false); + } + else + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2,iPad,&UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned,this, app.GetStringTable(),NULL,0,false); + } + } + ui.PlayUISFX(eSFX_Press); + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // hiding a mash-up world + if((m_iSaveListIndex != JOIN_LOAD_CREATE_BUTTON_INDEX)) + { + LevelGenerationOptions *levelGen = m_generators.at(m_iSaveListIndex - 1); + + if(!levelGen->isTutorial()) + { + if(levelGen->requiresTexturePack()) + { + unsigned int uiPackID=levelGen->getRequiredTexturePackId(); + + m_bIgnoreInput = true; + app.HideMashupPackWorld(m_iPad,uiPackID); + + // update the saves list + m_iState = e_SavesRepopulateAfterMashupHide; + } + } + } + ui.PlayUISFX(eSFX_Press); + + } + break; + case ACTION_MENU_LEFT_SCROLL: +#ifdef _XBOX + if( m_bInParty ) + { + m_bShowingPartyGamesOnly = !m_bShowingPartyGamesOnly; + UpdateGamesList(); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } +#endif + break; + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + { + // if we are on the saves menu, check there are games in the games list to move to + if(DoesSavesListHaveFocus()) + { + if( m_buttonListGames.getItemCount() > 0) + { + sendInputToMovie(key, repeat, pressed, released); + } + } + else + { + sendInputToMovie(key, repeat, pressed, released); + } + } + break; + + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +int UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes) +{ + // 4J HEG - No reason to set value if keyboard was cancelled + UIScene_LoadOrJoinMenu *pClass=(UIScene_LoadOrJoinMenu *)lpParam; + pClass->m_bIgnoreInput=false; + if (bRes) + { + uint16_t ui16Text[128]; + ZeroMemory(ui16Text, 128 * sizeof(uint16_t) ); + InputManager.GetText(ui16Text); + + // check the name is valid + if(ui16Text[0]!=0) + { +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined(__PSVITA__)) + // open the save and overwrite the metadata + StorageManager.RenameSaveData(pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC, ui16Text,&UIScene_LoadOrJoinMenu::RenameSaveDataReturned,pClass); +#endif + } + else + { + pClass->m_bIgnoreInput=false; + pClass->updateTooltips(); + } + } + else + { + pClass->m_bIgnoreInput=false; + pClass->updateTooltips(); + } + + + return 0; +} +void UIScene_LoadOrJoinMenu::handleInitFocus(F64 controlId, F64 childId) +{ + app.DebugPrintf(app.USER_SR, "UIScene_LoadOrJoinMenu::handleInitFocus - %d , %d\n", (int)controlId, (int)childId); +} + +void UIScene_LoadOrJoinMenu::handleFocusChange(F64 controlId, F64 childId) +{ + app.DebugPrintf(app.USER_SR, "UIScene_LoadOrJoinMenu::handleFocusChange - %d , %d\n", (int)controlId, (int)childId); + + switch((int)controlId) + { + case eControl_GamesList: + m_iGameListIndex = childId; + m_buttonListGames.updateChildFocus( (int) childId ); + break; + case eControl_SavesList: + m_iSaveListIndex = childId; + m_bUpdateSaveSize = true; + break; + }; + updateTooltips(); +} + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD +void UIScene_LoadOrJoinMenu::remoteStorageGetSaveCallback(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code) +{ + app.DebugPrintf("remoteStorageGetCallback err : 0x%08x\n", error_code); + assert(error_code == 0); + ((UIScene_LoadOrJoinMenu*)lpParam)->LoadSaveFromCloud(); +} +#endif + +void UIScene_LoadOrJoinMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_SavesList: + { + m_bIgnoreInput=true; + + int lGenID = (int)childId - 1; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + if((int)childId == JOIN_LOAD_CREATE_BUTTON_INDEX) + { + app.SetTutorialMode( false ); + + m_controlJoinTimer.setVisible( false ); + + app.SetCorruptSaveDeleted(false); + + CreateWorldMenuInitData *params = new CreateWorldMenuInitData(); + params->iPad = m_iPad; + ui.NavigateToScene(m_iPad,eUIScene_CreateWorldMenu,(void *)params); + } + else if (lGenID < m_generators.size()) + { + LevelGenerationOptions *levelGen = m_generators.at(lGenID); + app.SetTutorialMode( levelGen->isTutorial() ); + // Reset the autosave time + app.SetAutosaveTimerTime(); + + if(levelGen->isTutorial()) + { + LoadLevelGen(levelGen); + } + else + { + LoadMenuInitData *params = new LoadMenuInitData(); + params->iPad = m_iPad; + // need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting + params->iSaveGameInfoIndex=-1; + //params->pbSaveRenamed=&m_bSaveRenamed; + params->levelGen = levelGen; + params->saveDetails = NULL; + + // navigate to the settings scene + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params); + } + } + else + { +#ifdef __ORBIS__ + // check if this is a damaged save + PSAVE_INFO pSaveInfo = &m_pSaveDetails->SaveInfoA[((int)childId)-m_iDefaultButtonsC]; + if(pSaveInfo->thumbnailData == NULL && pSaveInfo->modifiedTime == 0) // no thumbnail data and time of zero and zero blocks useset for corrupt files + { + // give the option to delete the save + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned,this, app.GetStringTable(),NULL,0,false); + + } + else +#endif + { + app.SetTutorialMode( false ); + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + LoadSaveFromDisk(m_saves->at((int)childId-m_iDefaultButtonsC)); + } + else + { + LoadMenuInitData *params = new LoadMenuInitData(); + params->iPad = m_iPad; + // need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting + params->iSaveGameInfoIndex=((int)childId)-m_iDefaultButtonsC; + //params->pbSaveRenamed=&m_bSaveRenamed; + params->levelGen = NULL; + params->saveDetails = &m_saveDetails[ ((int)childId)-m_iDefaultButtonsC ]; + +#ifdef _XBOX_ONE + // On XB1, saves might need syncing, in which case inform the user so they can decide whether they want to wait for this to happen + if( m_pSaveDetails->SaveInfoA[params->iSaveGameInfoIndex].needsSync ) + { + unsigned int uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_SYNC; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + m_loadMenuInitData = params; + ui.RequestMessageBox(IDS_LOAD_SAVED_WORLD, IDS_CONFIRM_SYNC_REQUIRED, uiIDA, 2, ProfileManager.GetPrimaryPad(),&NeedSyncMessageReturned,this,app.GetStringTable(),NULL,0,false); + } + else +#endif + { + // navigate to the settings scene + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params); + } + } + } + } + } + break; + case eControl_GamesList: + { + m_bIgnoreInput=true; + + m_eAction = eAction_JoinGame; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + { + int nIndex = (int)childId; + m_iGameListIndex = nIndex; + CheckAndJoinGame(nIndex); + } + + break; + } + } +} + +void UIScene_LoadOrJoinMenu::CheckAndJoinGame(int gameIndex) +{ + if( m_buttonListGames.getItemCount() > 0 && gameIndex < m_currentSessions->size() ) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // 4J-PB - is the player allowed to join games? + bool noUGC=false; + bool bContentRestricted=false; + + // we're online, since we are joining a game + ProfileManager.GetChatAndContentRestrictions(m_iPad,true,&noUGC,&bContentRestricted,NULL); + +#ifdef __ORBIS__ + // 4J Stu - On PS4 we don't restrict playing multiplayer based on chat restriction, so remove this check + noUGC = false; + + bool bPlayStationPlus=true; + int iPadWithNoPlaystationPlus=0; + bool isSignedInLive = true; + int iPadNotSignedInLive = -1; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) + { + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + { + // Record the first non signed in live pad + iPadNotSignedInLive = i; + } + + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + if(ProfileManager.HasPlayStationPlus(i)==false) + { + bPlayStationPlus=false; + break; + } + } + } +#endif +#ifdef __PSVITA__ + if( CGameNetworkManager::usingAdhocMode() ) + { + bContentRestricted = false; + noUGC = false; + } +#endif + + if(noUGC) + { + // not allowed to join +#ifndef __PSVITA__ + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + // Not allowed to play online + ui.RequestMessageBox(IDS_ONLINE_GAME, IDS_CHAT_RESTRICTION_UGC, uiIDA, 1, m_iPad,NULL,this,app.GetStringTable(),NULL,0,false); +#else + // Not allowed to play online + ProfileManager.ShowSystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, 0 ); +#endif + + m_bIgnoreInput=false; + return; + } + else if(bContentRestricted) + { + ui.RequestContentRestrictedMessageBox(); + + m_bIgnoreInput=false; + return; + } +#ifdef __ORBIS__ + // If this is an online game but not all players are signed in to Live, stop! + else if (!isSignedInLive) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + m_bIgnoreInput = false; + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive, NULL, NULL, app.GetStringTable()); + } + else + { + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,iPadNotSignedInLive, &UIScene_LoadOrJoinMenu::MustSignInReturnedPSN, this, app.GetStringTable()); + } + return; + } + else if(bPlayStationPlus==false) + { + // PS Plus upsell + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + // upsell psplus + int32_t iResult=sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + iResult=sceNpCommerceDialogOpen(¶m); + + // UINT uiIDA[2]; + // uiIDA[0]=IDS_CONFIRM_OK; + // uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; + // ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_LoadOrJoinMenu::PSPlusReturned,this, app.GetStringTable(),NULL,0,false); + + m_bIgnoreInput=false; + return; + } + +#endif +#endif + + //CScene_MultiGameInfo::JoinMenuInitData *initData = new CScene_MultiGameInfo::JoinMenuInitData(); + m_initData->iPad = 0;; + m_initData->selectedSession = m_currentSessions->at( gameIndex ); + + // check that we have the texture pack available + // If it's not the default texture pack + if(m_initData->selectedSession->data.texturePackParentId!=0) + { + int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount(); + bool bHasTexturePackInstalled=false; + + for(int i=0;iskins->getTexturePackByIndex(i); + if(tp->getDLCParentPackId()==m_initData->selectedSession->data.texturePackParentId) + { + bHasTexturePackInstalled=true; + break; + } + } + + if(bHasTexturePackInstalled==false) + { + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack +#ifdef _XBOX + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(m_iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + UINT uiIDA[2]; + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + //uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + + // Give the player a warning about the texture pack missing + ui.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, m_iPad,&UIScene_LoadOrJoinMenu::TexturePackDialogReturned,this,app.GetStringTable(),NULL,0,false); + + return; + } + } + m_controlJoinTimer.setVisible( false ); + +#ifdef _XBOX + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); +#endif + + m_bIgnoreInput=true; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_JoinMenu,m_initData); + } +} + +void UIScene_LoadOrJoinMenu::LoadLevelGen(LevelGenerationOptions *levelGen) +{ + // Load data from disc + //File saveFile( L"Tutorial\\Tutorial" ); + //LoadSaveFromDisk(&saveFile); + + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + StorageManager.ResetSaveData(); + // Make our next save default to the name of the level + StorageManager.SetSaveTitle(levelGen->getDefaultSaveName().c_str()); + + bool isClientSide = false; + bool isPrivate = false; + // TODO int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + int maxPlayers = 8; + + if( app.GetTutorialMode() ) + { + isClientSide = false; + maxPlayers = 4; + } + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = NULL; + param->settings = app.GetGameHostOption( eGameHostOption_Tutorial ); + param->levelGen = levelGen; + + if(levelGen->requiresTexturePack()) + { + param->texturePackId = levelGen->getRequiredTexturePackId(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(param->texturePackId); + //pMinecraft->skins->updateUI(); + } + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +void UIScene_LoadOrJoinMenu::UpdateGamesListCallback(LPVOID pParam) +{ + if(pParam != NULL) + { + UIScene_LoadOrJoinMenu *pScene = (UIScene_LoadOrJoinMenu *)pParam; + pScene->UpdateGamesList(); + } +} + +void UIScene_LoadOrJoinMenu::UpdateGamesList() +{ + // If we're ignoring input scene isn't active so do nothing + if (m_bIgnoreInput) return; + + // If a texture pack is loading, or will be loading, then ignore this ( we are going to be destroyed anyway) + if( Minecraft::GetInstance()->skins->getSelected()->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ) return; + + // if we're retrieving save info, don't show the list yet as we will be ignoring press events + if(!m_bSavesDisplayed) + { + return; + } + + + FriendSessionInfo *pSelectedSession = NULL; + if(DoesGamesListHaveFocus() && m_buttonListGames.getItemCount() > 0) + { + unsigned int nIndex = m_buttonListGames.getCurrentSelection(); + pSelectedSession = m_currentSessions->at( nIndex ); + } + + SessionID selectedSessionId; + ZeroMemory(&selectedSessionId,sizeof(SessionID)); + if( pSelectedSession != NULL )selectedSessionId = pSelectedSession->sessionId; + pSelectedSession = NULL; + + m_controlJoinTimer.setVisible( false ); + + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should show the View Gamercard tooltip + int iRB=-1; + int iY = -1; + int iX=-1; + + delete m_currentSessions; + m_currentSessions = g_NetworkManager.GetSessionList( m_iPad, 1, m_bShowingPartyGamesOnly ); + + // Update the xui list displayed + unsigned int xuiListSize = m_buttonListGames.getItemCount(); + unsigned int filteredListSize = (unsigned int)m_currentSessions->size(); + + BOOL gamesListHasFocus = DoesGamesListHaveFocus(); + + if(filteredListSize > 0) + { +#if TO_BE_IMPLEMENTED + if( !m_pGamesList->IsEnabled() ) + { + m_pGamesList->SetEnable(TRUE); + m_pGamesList->SetCurSel( 0 ); + } +#endif + m_labelNoGames.setVisible( false ); + m_controlJoinTimer.setVisible( false ); + } + else + { +#if TO_BE_IMPLEMENTED + m_pGamesList->SetEnable(FALSE); +#endif + m_controlJoinTimer.setVisible( false ); + m_labelNoGames.setVisible( true ); + +#if TO_BE_IMPLEMENTED + if( gamesListHasFocus ) m_pGamesList->InitFocus(m_iPad); +#endif + } + + // clear out the games list and re-fill + m_buttonListGames.clearList(); + + if( filteredListSize > 0 ) + { + // Reset the focus to the selected session if it still exists + unsigned int sessionIndex = 0; + m_buttonListGames.setCurrentSelection(0); + + for( AUTO_VAR(it, m_currentSessions->begin()); it < m_currentSessions->end(); ++it) + { + FriendSessionInfo *sessionInfo = *it; + + wchar_t textureName[64] = L"\0"; + + // Is this a default game or a texture pack game? + if(sessionInfo->data.texturePackParentId!=0) + { + // Do we have the texture pack + Minecraft *pMinecraft = Minecraft::GetInstance(); + TexturePack *tp = pMinecraft->skins->getTexturePackById(sessionInfo->data.texturePackParentId); + HRESULT hr; + + DWORD dwImageBytes=0; + PBYTE pbImageData=NULL; + + if(tp==NULL) + { + DWORD dwBytes=0; + PBYTE pbData=NULL; + app.GetTPD(sessionInfo->data.texturePackParentId,&pbData,&dwBytes); + + // is it in the tpd data ? + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes ); + if(dwImageBytes > 0 && pbImageData) + { + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + } + } + else + { + pbImageData = tp->getPackIcon(dwImageBytes); + if(dwImageBytes > 0 && pbImageData) + { + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + } + } + } + else + { + // default texture pack + Minecraft *pMinecraft = Minecraft::GetInstance(); + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(0); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + } + } + + m_buttonListGames.addItem( sessionInfo->displayLabel, textureName ); + + if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0) + { + m_buttonListGames.setCurrentSelection(sessionIndex); + break; + } + ++sessionIndex; + } + } + + updateTooltips(); +} + +void UIScene_LoadOrJoinMenu::HandleDLCMountingComplete() +{ + Initialise(); +} + +bool UIScene_LoadOrJoinMenu::DoesSavesListHaveFocus() +{ + if( m_buttonListSaves.hasFocus() ) + { + // check it's not the first or second element (new world or tutorial) + if(m_iSaveListIndex > (m_iDefaultButtonsC-1)) + { + return true; + } + } + return false; +} + +bool UIScene_LoadOrJoinMenu::DoesMashUpWorldHaveFocus() +{ + if(m_buttonListSaves.hasFocus()) + { + // check it's not the first or second element (new world or tutorial) + if(m_iSaveListIndex > (m_iDefaultButtonsC - 1)) + { + return false; + } + + if(m_iSaveListIndex > (m_iDefaultButtonsC - 1 - m_iMashUpButtonsC)) + { + return true; + } + else return false; + } + else return false; +} + +bool UIScene_LoadOrJoinMenu::DoesGamesListHaveFocus() +{ + return m_buttonListGames.hasFocus(); +} + +void UIScene_LoadOrJoinMenu::handleTimerComplete(int id) +{ + switch(id) + { + case JOIN_LOAD_ONLINE_TIMER_ID: + { +#ifdef _XBOX + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } +#endif + + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + // m_CheckboxOnline.SetEnable(TRUE); + // m_CheckboxPrivate.SetEnable(TRUE); + } + else + { + m_bInParty = false; + m_buttonListGames.clearList(); + m_controlJoinTimer.setVisible( true ); + m_labelNoGames.setVisible( false ); + } + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + // 4J-PB - Only Xbox will not have trial DLC patched into the game +#ifdef _XBOX + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + for(int i=0;ichImageURL); + + if(hasRegisteredSubstitutionTexture(textureName)==false) + { + PBYTE pbImageData; + int iImageDataBytes=0; + SonyHttp::getDataFromURL(pDLCInfo->chImageURL,(void **)&pbImageData,&iImageDataBytes); + + if(iImageDataBytes!=0) + { + // set the image + registerSubstitutionTexture(textureName,pbImageData,iImageDataBytes,true); + m_iConfigA[i]=-1; + } + + } + } + } + } + + bool bAllDone=true; + for(int i=0;igetName().c_str()); + + __int64 fileSize = saveFile->length(); + FileInputStream fis(*saveFile); + byteArray ba(fileSize); + fis.read(ba); + fis.close(); + + + + bool isClientSide = false; + bool isPrivate = false; + int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + + if( app.GetTutorialMode() ) + { + isClientSide = false; + maxPlayers = 4; + } + + app.SetGameHostOption(eGameHostOption_GameType,GameType::CREATIVE->getId() ); + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + LoadSaveDataThreadParam *saveData = new LoadSaveDataThreadParam(ba.data, ba.length, saveFile->getName()); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = saveData; + param->settings = app.GetGameHostOption( eGameHostOption_All ); + param->savePlatform = savePlatform; + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD +void UIScene_LoadOrJoinMenu::LoadSaveFromCloud() +{ + + wchar_t wFileName[128]; + mbstowcs(wFileName, app.getRemoteStorage()->getLocalFilename(), strlen(app.getRemoteStorage()->getLocalFilename())+1); // plus null + File cloudFile(wFileName); + + + StorageManager.ResetSaveData(); + + // Make our next save default to the name of the level + wchar_t wSaveName[128]; + mbstowcs(wSaveName, app.getRemoteStorage()->getSaveNameUTF8(), strlen(app.getRemoteStorage()->getSaveNameUTF8())+1); // plus null + StorageManager.SetSaveTitle(wSaveName); + + __int64 fileSize = cloudFile.length(); + FileInputStream fis(cloudFile); + byteArray ba(fileSize); + fis.read(ba); + fis.close(); + + + + bool isClientSide = false; + bool isPrivate = false; + int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + + if( app.GetTutorialMode() ) + { + isClientSide = false; + maxPlayers = 4; + } + + app.SetGameHostOption(eGameHostOption_All, app.getRemoteStorage()->getSaveHostOptions() ); + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + LoadSaveDataThreadParam *saveData = new LoadSaveDataThreadParam(ba.data, ba.length, cloudFile.getName()); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = app.getRemoteStorage()->getSaveSeed(); + param->saveData = saveData; + param->settings = app.GetGameHostOption( eGameHostOption_All ); + param->savePlatform = app.getRemoteStorage()->getSavePlatform(); + param->texturePackId = app.getRemoteStorage()->getSaveTexturePack(); + +#ifndef _XBOX + g_NetworkManager.FakeLocalPlayerJoined(); +#endif + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +#endif //SONY_REMOTE_STORAGE_DOWNLOAD + +int UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + // results switched for this dialog + + // Check that we have a valid save selected (can get a bad index if the save list has been refreshed) + bool validSelection= pClass->m_iDefaultButtonsC != 0 && pClass->m_iSaveListIndex >= pClass->m_iDefaultButtonsC; + + if(result==C4JStorage::EMessage_ResultDecline && validSelection) + { + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + pClass->m_bIgnoreInput=false; + } + else + { + StorageManager.DeleteSaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC], UIScene_LoadOrJoinMenu::DeleteSaveDataReturned, (LPVOID)pClass->GetCallbackUniqueId()); + pClass->m_controlSavesTimer.setVisible( true ); + } + } + else + { + pClass->m_bIgnoreInput=false; + } + + return 0; +} + +int UIScene_LoadOrJoinMenu::DeleteSaveDataReturned(LPVOID lpParam,bool bRes) +{ + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + + if(pClass) + { + if(bRes) + { + // wipe the list and repopulate it + pClass->m_iState=e_SavesRepopulateAfterDelete; + } + else pClass->m_bIgnoreInput=false; + + pClass->updateTooltips(); + } + ui.LeaveCallbackIdCriticalSection(); + return 0; +} + + +int UIScene_LoadOrJoinMenu::RenameSaveDataReturned(LPVOID lpParam,bool bRes) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)lpParam; + + if(bRes) + { + pClass->m_iState=e_SavesRepopulate; + } + else pClass->m_bIgnoreInput=false; + + pClass->updateTooltips(); + + return 0; +} + +#ifdef __ORBIS__ + + +void UIScene_LoadOrJoinMenu::LoadRemoteFileFromDisk(char* remoteFilename) +{ + wchar_t wSaveName[128]; + mbstowcs(wSaveName, remoteFilename, strlen(remoteFilename)+1); // plus null + + // processConsoleSave(wSaveName, L"ProcessedSave.bin"); + + // File remoteFile(L"ProcessedSave.bin"); + File remoteFile(wSaveName); + LoadSaveFromDisk(&remoteFile, SAVE_FILE_PLATFORM_PS3); +} +#endif + + +int UIScene_LoadOrJoinMenu::SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + + // results switched for this dialog + // EMessage_ResultAccept means cancel + switch(result) + { + case C4JStorage::EMessage_ResultDecline: // rename + { + pClass->m_bIgnoreInput=true; +#ifdef _DURANGO + // bring up a keyboard + InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE), (pClass->m_saveDetails[pClass->m_iSaveListIndex-pClass->m_iDefaultButtonsC]).UTF16SaveName,(DWORD)0,25,&UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default); +#else + // bring up a keyboard + wchar_t wSaveName[128]; + //CD - Fix - We must memset the SaveName + ZeroMemory(wSaveName, 128 * sizeof(wchar_t) ); + mbstowcs(wSaveName, pClass->m_saveDetails[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC].UTF8SaveName, strlen(pClass->m_saveDetails->UTF8SaveName)+1); // plus null + LPWSTR ptr = wSaveName; + InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE),wSaveName,(DWORD)0,25,&UIScene_LoadOrJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default); +#endif + } + break; + + case C4JStorage::EMessage_ResultThirdOption: // delete - + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&UIScene_LoadOrJoinMenu::DeleteSaveDialogReturned,pClass, app.GetStringTable(),NULL,0,false); + } + break; + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + case C4JStorage::EMessage_ResultFourthOption: // upload to cloud + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_TEXT, uiIDA, 2, iPad,&UIScene_LoadOrJoinMenu::SaveTransferDialogReturned,pClass, app.GetStringTable(),NULL,0,false); + } + break; +#endif // SONY_REMOTE_STORAGE_UPLOAD +#if defined _XBOX_ONE || defined __ORBIS__ + case C4JStorage::EMessage_ResultFourthOption: // copy save + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_COPYSAVE, IDS_TEXT_COPY_SAVE, uiIDA, 2, iPad,&UIScene_LoadOrJoinMenu::CopySaveDialogReturned,pClass, app.GetStringTable(),NULL,0,false); + } + break; +#endif + + case C4JStorage::EMessage_Cancelled: + default: + { + // reset the tooltips + pClass->updateTooltips(); + pClass->m_bIgnoreInput=false; + } + break; + } + return 0; +} + +int UIScene_LoadOrJoinMenu::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu *pClass = (UIScene_LoadOrJoinMenu *)pParam; + + // Exit with or without saving + if(result==C4JStorage::EMessage_ResultAccept) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); +#if TO_BE_IMPLEMENTED + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + app.GetDLCFullOfferIDForPackID(pClass->m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full); + + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + } + else // trial version + { + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } +#endif + + +#if defined _XBOX_ONE + if(ProfileManager.IsSignedIn(iPad)) + { + if (ProfileManager.IsSignedInLive(iPad)) + { + wstring ProductId; + app.GetDLCFullOfferIDForPackID(pClass->m_initData->selectedSession->data.texturePackParentId,ProductId); + + StorageManager.InstallOffer(1,(WCHAR *)ProductId.c_str(),NULL,NULL); + } + else + { + // 4J-JEV: Fix for XB1: #165863 - XR-074: Compliance: With no active network connection user is unable to convert from Trial to Full texture pack and is not messaged why. + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + } +#endif + + } + pClass->m_bIgnoreInput=false; + return 0; +} + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +int UIScene_LoadOrJoinMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { +#if defined(__PS3__) + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_LoadOrJoinMenu::PSN_SignInReturned, pClass); +#elif defined __PSVITA__ + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_LoadOrJoinMenu::PSN_SignInReturned, pClass); +#else + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_LoadOrJoinMenu::PSN_SignInReturned, pClass, false, iPad); +#endif + } + else + { + pClass->m_bIgnoreInput = false; + } + + return 0; +} + +int UIScene_LoadOrJoinMenu::PSN_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + if(bContinue==true) + { + switch(pClass->m_eAction) + { + case eAction_ViewInvites: + // Check if we're signed in to LIVE + if(ProfileManager.IsSignedInLive(iPad)) + { +#if defined(__PS3__) + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#elif defined __PSVITA__ + // TO BE IMPLEMENTED FOR VITA + PSVITA_STUBBED; +#else + SQRNetworkManager_Orbis::RecvInviteGUI(); +#endif + } + break; + case eAction_JoinGame: + pClass->CheckAndJoinGame(pClass->m_iGameListIndex); + break; + } + } + else + { + pClass->m_bIgnoreInput = false; + } + return 0; +} +#endif + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + +void UIScene_LoadOrJoinMenu::LaunchSaveTransfer() +{ + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_LoadOrJoinMenu::DownloadSonyCrossSaveThreadProc; + loadingParams->lpParam = (LPVOID)this; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + loadingParams->cancelFunc=&UIScene_LoadOrJoinMenu::CancelSaveTransferCallback; + loadingParams->m_cancelFuncParam=this; + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); +} + + + + +int UIScene_LoadOrJoinMenu::CreateDummySaveDataCallback(LPVOID lpParam,bool bRes) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + if(bRes) + { + pClass->m_eSaveTransferState = eSaveTransfer_GetSavesInfo; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("CreateDummySaveDataCallback failed\n"); + + } + return 0; +} + +int UIScene_LoadOrJoinMenu::CrossSaveGetSavesInfoCallback(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, bool bRes) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + if(bRes) + { + pClass->m_eSaveTransferState = eSaveTransfer_GetFileData; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("CrossSaveGetSavesInfoCallback failed\n"); + } + return 0; +} + +int UIScene_LoadOrJoinMenu::LoadCrossSaveDataCallback( void *pParam,bool bIsCorrupt, bool bIsOwner ) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) pParam; + if(bIsCorrupt == false && bIsOwner) + { + pClass->m_eSaveTransferState = eSaveTransfer_CreatingNewSave; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("LoadCrossSaveDataCallback failed \n"); + + } + return 0; +} + +int UIScene_LoadOrJoinMenu::CrossSaveFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) pParam; + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + return 0; +} + + +int UIScene_LoadOrJoinMenu::CrossSaveDeleteOnErrorReturned(LPVOID lpParam,bool bRes) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + return 0; +} + +int UIScene_LoadOrJoinMenu::RemoteSaveNotFoundCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) pParam; + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + return 0; +} + +// MGH - added this global to force the delete of the previous data, for the remote storage saves +// need to speak to Chris why this is necessary +bool g_bForceVitaSaveWipe = false; + + +int UIScene_LoadOrJoinMenu::DownloadSonyCrossSaveThreadProc( LPVOID lpParameter ) +{ + Compression::UseDefaultThreadStorage(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParameter; + pClass->m_saveTransferDownloadCancelled = false; + bool bAbortCalled = false; + Minecraft *pMinecraft=Minecraft::GetInstance(); + bool bSaveFileCreated = false; + wchar_t wSaveName[128]; + + // get the save file size + pMinecraft->progressRenderer->progressStagePercentage(0); + pMinecraft->progressRenderer->progressStart(IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD); + pMinecraft->progressRenderer->progressStage( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD ); + + ConsoleSaveFile* pSave = NULL; + + pClass->m_eSaveTransferState = eSaveTransfer_GetRemoteSaveInfo; + + + while(pClass->m_eSaveTransferState!=eSaveTransfer_Idle) + { + switch(pClass->m_eSaveTransferState) + { + case eSaveTransfer_Idle: + break; + case eSaveTransfer_GetRemoteSaveInfo: + app.DebugPrintf("UIScene_LoadOrJoinMenu getSaveInfo\n"); + app.getRemoteStorage()->getSaveInfo(); + pClass->m_eSaveTransferState = eSaveTransfer_GettingRemoteSaveInfo; + break; + case eSaveTransfer_GettingRemoteSaveInfo: + if(pClass->m_saveTransferDownloadCancelled) + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + break; + } + if(app.getRemoteStorage()->waitingForSaveInfo() == false) + { + if(app.getRemoteStorage()->saveIsAvailable()) + { + pClass->m_eSaveTransferState = eSaveTransfer_CreateDummyFile; + } + else + { + // no save available, inform the user about the functionality + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_NOT_AVAILABLE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),RemoteSaveNotFoundCallback,pClass, app.GetStringTable(),NULL,0,false); + } + } + break; + case eSaveTransfer_CreateDummyFile: + { + StorageManager.ResetSaveData(); + byte *compData = (byte *)StorageManager.AllocateSaveData( app.getRemoteStorage()->getSaveFilesize() ); + // Make our next save default to the name of the level + const char* pNameUTF8 = app.getRemoteStorage()->getSaveNameUTF8(); + mbstowcs(wSaveName, pNameUTF8, strlen(pNameUTF8)+1); // plus null + StorageManager.SetSaveTitle(wSaveName); + PBYTE pbThumbnailData=NULL; + DWORD dwThumbnailDataSize=0; + + PBYTE pbDataSaveImage=NULL; + DWORD dwDataSizeSaveImage=0; + + StorageManager.GetDefaultSaveImage(&pbDataSaveImage, &dwDataSizeSaveImage); // Get the default save thumbnail (as set by SetDefaultImages) for use on saving games t + StorageManager.GetDefaultSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); // Get the default save image (as set by SetDefaultImages) for use on saving games that + + BYTE bTextMetadata[88]; + ZeroMemory(bTextMetadata,88); + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, app.getRemoteStorage()->getSaveSeed(), true, app.getRemoteStorage()->getSaveHostOptions(), app.getRemoteStorage()->getSaveTexturePack() ); + + // set the icon and save image + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + + app.getRemoteStorage()->waitForStorageManagerIdle(); + C4JStorage::ESaveGameState saveState = StorageManager.SaveSaveData( &UIScene_LoadOrJoinMenu::CreateDummySaveDataCallback, lpParameter ); + if(saveState == C4JStorage::ESaveGame_Save) + { + pClass->m_eSaveTransferState = eSaveTransfer_CreatingDummyFile; + } + else + { + app.DebugPrintf("Failed to create dummy save file\n"); + pClass->m_eSaveTransferState = eSaveTransfer_Error; + } + } + break; + case eSaveTransfer_CreatingDummyFile: + break; + case eSaveTransfer_GetSavesInfo: + { + // we can't cancel here, we need the saves info so we can delete the file + if(pClass->m_saveTransferDownloadCancelled) + { + WCHAR wcTemp[256]; + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + } + + app.getRemoteStorage()->waitForStorageManagerIdle(); + app.DebugPrintf("CALL GetSavesInfo B\n"); + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(pClass->m_iPad,&UIScene_LoadOrJoinMenu::CrossSaveGetSavesInfoCallback,pClass,"save"); + pClass->m_eSaveTransferState = eSaveTransfer_GettingSavesInfo; + } + break; + case eSaveTransfer_GettingSavesInfo: + if(pClass->m_saveTransferDownloadCancelled) + { + WCHAR wcTemp[256]; + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + } + break; + + case eSaveTransfer_GetFileData: + { + bSaveFileCreated = true; + StorageManager.GetSaveUniqueFileDir(pClass->m_downloadedUniqueFilename); + + if(pClass->m_saveTransferDownloadCancelled) + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + break; + } + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + int idx = pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC; + app.getRemoteStorage()->waitForStorageManagerIdle(); + bool bGettingOK = app.getRemoteStorage()->getSaveData(pClass->m_downloadedUniqueFilename, SaveTransferReturned, pClass); + if(bGettingOK) + { + pClass->m_eSaveTransferState = eSaveTransfer_GettingFileData; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("app.getRemoteStorage()->getSaveData failed\n"); + + } + } + + case eSaveTransfer_GettingFileData: + { + WCHAR wcTemp[256]; + + int dataProgress = app.getRemoteStorage()->getDataProgress(); + pMinecraft->progressRenderer->progressStagePercentage(dataProgress); + + //swprintf(wcTemp, 256, L"Downloading data : %d", dataProgress);//app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,pClass->m_ulFileSize); + swprintf(wcTemp,256, app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),dataProgress); + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + if(pClass->m_saveTransferDownloadCancelled && bAbortCalled == false) + { + app.getRemoteStorage()->abort(); + bAbortCalled = true; + } + } + break; + case eSaveTransfer_FileDataRetrieved: + pClass->m_eSaveTransferState = eSaveTransfer_LoadSaveFromDisc; + break; + case eSaveTransfer_LoadSaveFromDisc: + { + if(pClass->m_saveTransferDownloadCancelled) + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + break; + } + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + int saveInfoIndex = -1; + for(int i=0;iiSaveC;i++) + { + if(strcmp(pSaveDetails->SaveInfoA[i].UTF8SaveFilename, pClass->m_downloadedUniqueFilename) == 0) + { + //found it + saveInfoIndex = i; + } + } + if(saveInfoIndex == -1) + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("CrossSaveGetSavesInfoCallback failed - couldn't find save\n"); + } + else + { +#ifdef __PS3__ + // ignore the CRC on PS3 + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],&LoadCrossSaveDataCallback,pClass, true); +#else + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],&LoadCrossSaveDataCallback,pClass); +#endif + if(eLoadStatus == C4JStorage::ESaveGame_Load) + { + pClass->m_eSaveTransferState = eSaveTransfer_LoadingSaveFromDisc; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + } + } + } + break; + case eSaveTransfer_LoadingSaveFromDisc: + + break; + case eSaveTransfer_CreatingNewSave: + { + unsigned int fileSize = StorageManager.GetSaveSize(); + byteArray ba(fileSize); + StorageManager.GetSaveData(ba.data, &fileSize); + assert(ba.length == fileSize); + + + StorageManager.ResetSaveData(); + { + PBYTE pbThumbnailData=NULL; + DWORD dwThumbnailDataSize=0; + + PBYTE pbDataSaveImage=NULL; + DWORD dwDataSizeSaveImage=0; + + StorageManager.GetDefaultSaveImage(&pbDataSaveImage, &dwDataSizeSaveImage); // Get the default save thumbnail (as set by SetDefaultImages) for use on saving games t + StorageManager.GetDefaultSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); // Get the default save image (as set by SetDefaultImages) for use on saving games that + + BYTE bTextMetadata[88]; + ZeroMemory(bTextMetadata,88); + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, app.getRemoteStorage()->getSaveSeed(), true, app.getRemoteStorage()->getSaveHostOptions(), app.getRemoteStorage()->getSaveTexturePack() ); + + // set the icon and save image + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + } + + +#ifdef SPLIT_SAVES + ConsoleSaveFileOriginal oldFormatSave( wSaveName, ba.data, ba.length, false, app.getRemoteStorage()->getSavePlatform() ); + pSave = new ConsoleSaveFileSplit( &oldFormatSave, false, pMinecraft->progressRenderer ); + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + pSave->Flush(false,false); + pClass->m_eSaveTransferState = eSaveTransfer_Saving; +#else + pSave = new ConsoleSaveFileOriginal( wSaveName, ba.data, ba.length, false, app.getRemoteStorage()->getSavePlatform() ); + pClass->m_eSaveTransferState = eSaveTransfer_Converting; + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_CONVERTING); +#endif + delete ba.data; + } + break; + case eSaveTransfer_Converting: + { + pSave->ConvertToLocalPlatform(); // check if we need to convert this file from PS3->PS4 + pClass->m_eSaveTransferState = eSaveTransfer_Saving; + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + StorageManager.SetSaveTitle(wSaveName); + StorageManager.SetSaveUniqueFilename(pClass->m_downloadedUniqueFilename); + + app.getRemoteStorage()->waitForStorageManagerIdle(); // we need to wait for the save system to be idle here, as Flush doesn't check for it. + pSave->Flush(false, false); + } + break; + case eSaveTransfer_Saving: + { + // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete + // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving. +#if defined(_DURANGO) || defined(__ORBIS__) + while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + { + Sleep(10); + StorageManager.Tick(); + } +#endif + + delete pSave; + + + pMinecraft->progressRenderer->progressStage(IDS_PROGRESS_SAVING_TO_DISC); + pClass->m_eSaveTransferState = eSaveTransfer_Succeeded; + } + break; + + case eSaveTransfer_Succeeded: + { + // if we've arrived here, the save has been created successfully + pClass->m_iState=e_SavesRepopulate; + pClass->updateTooltips(); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + app.getRemoteStorage()->waitForStorageManagerIdle(); // wait for everything to complete before we hand control back to the player + ui.RequestMessageBox( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_DOWNLOADCOMPLETE, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveFinishedCallback,pClass, app.GetStringTable()); + pClass->m_eSaveTransferState = eSaveTransfer_Finished; + } + break; + + case eSaveTransfer_Cancelled: // this is no longer used + { + assert(0); //pClass->m_eSaveTransferState = eSaveTransfer_Idle; + } + break; + case eSaveTransfer_Error: + { + if(bSaveFileCreated) + { + if(pClass->m_saveTransferDownloadCancelled) + { + WCHAR wcTemp[256]; + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + } + // if the save file has already been created we have to delete it again if there's been an error + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + int saveInfoIndex = -1; + for(int i=0;iiSaveC;i++) + { + if(strcmp(pSaveDetails->SaveInfoA[i].UTF8SaveFilename, pClass->m_downloadedUniqueFilename) == 0) + { + //found it + saveInfoIndex = i; + } + } + if(saveInfoIndex == -1) + { + app.DebugPrintf("eSaveTransfer_Error failed - couldn't find save\n"); + assert(0); + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + } + else + { + // delete the save file + app.getRemoteStorage()->waitForStorageManagerIdle(); + C4JStorage::ESaveGameState eDeleteStatus = StorageManager.DeleteSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],UIScene_LoadOrJoinMenu::CrossSaveDeleteOnErrorReturned,pClass); + if(eDeleteStatus == C4JStorage::ESaveGame_Delete) + { + pClass->m_eSaveTransferState = eSaveTransfer_ErrorDeletingSave; + } + else + { + app.DebugPrintf("StorageManager.DeleteSaveData failed!!\n"); + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + } + } + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + } + } + break; + + case eSaveTransfer_ErrorDeletingSave: + break; + case eSaveTransfer_ErrorMesssage: + { + app.getRemoteStorage()->waitForStorageManagerIdle(); // wait for everything to complete before we hand control back to the player + if(pClass->m_saveTransferDownloadCancelled) + { + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_DOWNLOADFAILED, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveFinishedCallback,pClass, app.GetStringTable()); + pClass->m_eSaveTransferState = eSaveTransfer_Finished; + } + if(bSaveFileCreated) // save file has been created, then deleted. + pClass->m_iState=e_SavesRepopulateAfterDelete; + else + pClass->m_iState=e_SavesRepopulate; + pClass->updateTooltips(); + } + break; + case eSaveTransfer_Finished: + { + + } + // waiting to dismiss the dialog + break; + } + Sleep(50); + } + + return 0; + +} + +void UIScene_LoadOrJoinMenu::SaveTransferReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + + if(s == SonyRemoteStorage::e_getDataSucceeded) + { + pClass->m_eSaveTransferState = eSaveTransfer_FileDataRetrieved; + } + else + { + pClass->m_eSaveTransferState = eSaveTransfer_Error; + app.DebugPrintf("SaveTransferReturned failed with error code : 0x%08x\n", error_code); + } + +} +ConsoleSaveFile* UIScene_LoadOrJoinMenu::SonyCrossSaveConvert() +{ + return NULL; +} + +void UIScene_LoadOrJoinMenu::CancelSaveTransferCallback(LPVOID lpParam) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + pClass->m_saveTransferDownloadCancelled = true; + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, -1, -1, -1,-1,-1,-1,-1); // MGH - added - remove the "cancel" tooltip, so the player knows it's underway (really needs a "cancelling" message) +} + +#endif + + + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + +void UIScene_LoadOrJoinMenu::LaunchSaveUpload() +{ + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_LoadOrJoinMenu::UploadSonyCrossSaveThreadProc; + loadingParams->lpParam = (LPVOID)this; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + +// 4J-PB - Waiting for Sony to fix canceling a save upload + loadingParams->cancelFunc=&UIScene_LoadOrJoinMenu::CancelSaveUploadCallback; + loadingParams->m_cancelFuncParam = this; + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); + +} + +int UIScene_LoadOrJoinMenu::CrossSaveUploadFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) pParam; + pClass->m_eSaveUploadState = eSaveUpload_Idle; + + return 0; +} + + +int UIScene_LoadOrJoinMenu::UploadSonyCrossSaveThreadProc( LPVOID lpParameter ) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParameter; + pClass->m_saveTransferUploadCancelled = false; + bool bAbortCalled = false; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // get the save file size + pMinecraft->progressRenderer->progressStagePercentage(0); + pMinecraft->progressRenderer->progressStart(IDS_TOOLTIPS_SAVETRANSFER_UPLOAD); + pMinecraft->progressRenderer->progressStage( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD ); + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + int idx = pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC; + bool bSettingOK = app.getRemoteStorage()->setSaveData(&pSaveDetails->SaveInfoA[idx], SaveUploadReturned, pClass); + + if(bSettingOK) + { + pClass->m_eSaveUploadState = eSaveUpload_UploadingFileData; + pMinecraft->progressRenderer->progressStagePercentage(0); + } + else + { + pClass->m_eSaveUploadState = eSaveUpload_Error; + } + + while(pClass->m_eSaveUploadState!=eSaveUpload_Idle) + { + switch(pClass->m_eSaveUploadState) + { + case eSaveUpload_Idle: + break; + case eSaveUpload_UploadingFileData: + { + WCHAR wcTemp[256]; + int dataProgress = app.getRemoteStorage()->getDataProgress(); + pMinecraft->progressRenderer->progressStagePercentage(dataProgress); + + //swprintf(wcTemp, 256, L"Uploading data : %d", dataProgress);//app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,pClass->m_ulFileSize); + swprintf(wcTemp,256, app.GetString(IDS_SAVETRANSFER_STAGE_PUT_DATA),dataProgress); + + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); +// 4J-PB - Waiting for Sony to fix canceling a save upload + if(pClass->m_saveTransferUploadCancelled && bAbortCalled == false) + { + // we only really want to be able to cancel during the download of data, if it's taking a long time + app.getRemoteStorage()->abort(); + bAbortCalled = true; + } + } + break; + case eSaveUpload_FileDataUploaded: + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_UPLOADCOMPLETE, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveUploadFinishedCallback,pClass, app.GetStringTable()); + pClass->m_eSaveUploadState = esaveUpload_Finished; + } + break; + case eSaveUpload_Cancelled: // this is no longer used + assert(0);// pClass->m_eSaveUploadState = eSaveUpload_Idle; + break; + case eSaveUpload_Error: + { + if(pClass->m_saveTransferUploadCancelled) + { + pClass->m_eSaveUploadState = eSaveUpload_Idle; + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_UPLOADFAILED, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveUploadFinishedCallback,pClass, app.GetStringTable()); + pClass->m_eSaveUploadState = esaveUpload_Finished; + } + } + break; + case esaveUpload_Finished: + // waiting for dialog to be dismissed + break; + } + Sleep(50); + } + + return 0; + +} + +void UIScene_LoadOrJoinMenu::SaveUploadReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + + if(pClass->m_saveTransferUploadCancelled) + { + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox( IDS_CANCEL_UPLOAD_TITLE, IDS_CANCEL_UPLOAD_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), CrossSaveUploadFinishedCallback, pClass, app.GetStringTable() ); + pClass->m_eSaveUploadState=esaveUpload_Finished; + } + else + { + if(s == SonyRemoteStorage::e_setDataSucceeded) + pClass->m_eSaveUploadState = eSaveUpload_FileDataUploaded; + else if ( !pClass->m_saveTransferUploadCancelled ) + pClass->m_eSaveUploadState = eSaveUpload_Error; + } +} + +void UIScene_LoadOrJoinMenu::CancelSaveUploadCallback(LPVOID lpParam) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu *) lpParam; + pClass->m_saveTransferUploadCancelled = true; + app.DebugPrintf("m_saveTransferUploadCancelled = true\n"); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, -1, -1, -1,-1,-1,-1,-1); // MGH - added - remove the "cancel" tooltip, so the player knows it's underway (really needs a "cancelling" message) + + pClass->m_bIgnoreInput = true; +} + +int UIScene_LoadOrJoinMenu::SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultAccept) + { + // upload the save + pClass->LaunchSaveUpload(); + + pClass->m_bIgnoreInput=false; + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} +#endif // SONY_REMOTE_STORAGE_UPLOAD + + +#if defined _XBOX_ONE +void UIScene_LoadOrJoinMenu::LaunchSaveTransfer() +{ + SaveTransferStateContainer *stateContainer = new SaveTransferStateContainer(); + stateContainer->m_iProgress = 0; + stateContainer->m_bSaveTransferInProgress = false; + stateContainer->m_bSaveTransferCancelled = false; + stateContainer->m_iPad = m_iPad; + stateContainer->m_eSaveTransferState = C4JStorage::eSaveTransfer_Idle; + stateContainer->m_pClass = this; + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &UIScene_LoadOrJoinMenu::DownloadXbox360SaveThreadProc; + loadingParams->lpParam = (LPVOID)stateContainer; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->iPad = DEFAULT_XUI_MENU_USER; + completionData->bRequiresUserAction=TRUE; + loadingParams->completionData = completionData; + + loadingParams->cancelFunc=&UIScene_LoadOrJoinMenu::CancelSaveTransferCallback; + loadingParams->m_cancelFuncParam=stateContainer; + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); +} + + + +int UIScene_LoadOrJoinMenu::DownloadXbox360SaveThreadProc( LPVOID lpParameter ) +{ + Compression::UseDefaultThreadStorage(); + + SaveTransferStateContainer *pStateContainer = (SaveTransferStateContainer *) lpParameter; + Minecraft *pMinecraft=Minecraft::GetInstance(); + ConsoleSaveFile* pSave = NULL; + + while(StorageManager.SaveTransferClearState()!=C4JStorage::eSaveTransfer_Idle) + { + Sleep(5); + } + + pStateContainer->m_bSaveTransferInProgress=true; + + UIScene_LoadOrJoinMenu::s_eSaveTransferFile = eSaveTransferFile_Marker; + RequestFileSize( pStateContainer, L"completemarker" ); + + while((pStateContainer->m_eSaveTransferState!=C4JStorage::eSaveTransfer_Idle) && pStateContainer->m_bSaveTransferInProgress && !pStateContainer->m_bSaveTransferCancelled) + { + switch(pStateContainer->m_eSaveTransferState) + { + case C4JStorage::eSaveTransfer_Idle: + break; + case C4JStorage::eSaveTransfer_FileSizeRetrieved: + switch(UIScene_LoadOrJoinMenu::s_eSaveTransferFile) + { + case eSaveTransferFile_Marker: + if(UIScene_LoadOrJoinMenu::s_ulFileSize == 0) + { + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_NONE_FOUND); + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + } + else + { + RequestFileData( pStateContainer, L"completemarker" ); + } + break; + case eSaveTransferFile_Metadata: + RequestFileData( pStateContainer, L"metadata" ); + break; + case eSaveTransferFile_SaveData: + RequestFileData( pStateContainer, L"savedata" ); + break; + }; + break; + case C4JStorage::eSaveTransfer_GettingFileData: + + break; + case C4JStorage::eSaveTransfer_FileDataRetrieved: + switch(UIScene_LoadOrJoinMenu::s_eSaveTransferFile) + { + case eSaveTransferFile_Marker: + UIScene_LoadOrJoinMenu::s_eSaveTransferFile = eSaveTransferFile_Metadata; + RequestFileSize( pStateContainer, L"metadata" ); + break; + case eSaveTransferFile_Metadata: + { + ByteArrayInputStream bais(UIScene_LoadOrJoinMenu::s_transferData); + DataInputStream dis(&bais); + + wstring saveTitle = dis.readUTF(); + StorageManager.SetSaveTitle(saveTitle.c_str()); + + wstring saveUniqueName = dis.readUTF(); + + // 4J Stu - Don't set this any more. We added it so that we could share the ban list data for this save + // However if the player downloads the same save multiple times, it will overwrite the previous version + // with that filname, and they could have made changes to it. + //StorageManager.SetSaveUniqueFilename((wchar_t *)saveUniqueName.c_str()); + + int thumbnailSize = dis.readInt(); + if(thumbnailSize > 0) + { + byteArray ba(thumbnailSize); + dis.readFully(ba); + + StorageManager.SetSaveImages(ba.data, ba.length, NULL, 0, NULL, 0); + + delete ba.data; + } + + UIScene_LoadOrJoinMenu::s_transferData = byteArray(); + UIScene_LoadOrJoinMenu::s_eSaveTransferFile = eSaveTransferFile_SaveData; + RequestFileSize( pStateContainer, L"savedata" ); + } + break; + case eSaveTransferFile_SaveData: + { +#ifdef SPLIT_SAVES + if(!pStateContainer->m_bSaveTransferCancelled) + { + ConsoleSaveFileOriginal oldFormatSave( L"Temp name", UIScene_LoadOrJoinMenu::s_transferData.data, UIScene_LoadOrJoinMenu::s_transferData.length, false, SAVE_FILE_PLATFORM_X360 ); + pSave = new ConsoleSaveFileSplit( &oldFormatSave, false, pMinecraft->progressRenderer ); + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + if(!pStateContainer->m_bSaveTransferCancelled) pSave->Flush(false,false); + } + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Saving; + +#else + pSave = new ConsoleSaveFileOriginal( wSaveName, m_transferData.data, m_transferData.length, false, SAVE_FILE_PLATFORM_X360 ); + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Converting; +#endif + delete UIScene_LoadOrJoinMenu::s_transferData.data; + UIScene_LoadOrJoinMenu::s_transferData = byteArray(); + } + break; + }; + + pStateContainer->m_iProgress=0; + break; + case C4JStorage::eSaveTransfer_Converting: +#if 0 + pSave->ConvertToLocalPlatform(); + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + if(!pStateContainer->m_bSaveTransferCancelled) pSave->Flush(false,false); + + pStateContainer->m_iProgress+=1; + if(pStateContainer->m_iProgress==101) + { + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Saving; + pStateContainer->m_iProgress=0; + break; + } + pMinecraft->progressRenderer->progressStagePercentage(pStateContainer->m_iProgress); +#endif + break; + case C4JStorage::eSaveTransfer_Saving: + // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete + // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving. +#if defined(_DURANGO) || defined(__ORBIS__) + pMinecraft->progressRenderer->progressStage(IDS_PROGRESS_SAVING_TO_DISC); + + while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + { + Sleep(10); + + // 4J Stu - DO NOT tick this here. The main thread should be the only place ticking the StorageManager. You WILL get crashes. + //StorageManager.Tick(); + } +#endif + + delete pSave; + +#ifdef _XBOX_ONE + pMinecraft->progressRenderer->progressStage(IDS_SAVE_TRANSFER_DOWNLOAD_AND_CONVERT_COMPLETE); +#endif + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + // wipe the list and repopulate it + if(!pStateContainer->m_bSaveTransferCancelled) pStateContainer->m_pClass->m_iState=e_SavesRepopulateAfterTransferDownload; + + //pClass->m_iProgress+=1; + //if(pClass->m_iProgress==101) + //{ + // pClass->m_iProgress=0; + // pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + // pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_DOWNLOAD_AND_CONVERT_COMPLETE ); + + // break; + //} + //pMinecraft->progressRenderer->progressStagePercentage(pClass->m_iProgress); + + break; + } + Sleep(50); + } + + if(pStateContainer->m_bSaveTransferCancelled) + { + WCHAR wcTemp[256]; + + pStateContainer->m_bSaveTransferCancelled=false; + swprintf(wcTemp,app.GetString(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLED)); + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + pStateContainer->m_bSaveTransferInProgress=false; + + delete pStateContainer; + + return 0; +} + +void UIScene_LoadOrJoinMenu::RequestFileSize( SaveTransferStateContainer *pClass, wchar_t *filename ) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // get the save file size + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + pMinecraft->progressRenderer->progressStage( IDS_SAVETRANSFER_STAGE_GET_DETAILS ); + +#ifdef _DEBUG_MENUS_ENABLED + if(app.GetLoadSavesFromFolderEnabled()) + { + ZeroMemory(&m_debugTransferDetails, sizeof(C4JStorage::SAVETRANSFER_FILE_DETAILS) ); + + File targetFile( wstring(L"FakeTMSPP\\").append(filename) ); + if(targetFile.exists()) m_debugTransferDetails.ulFileLen = targetFile.length(); + + SaveTransferReturned(pClass,&m_debugTransferDetails); + } + else +#endif + { + do + { + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + pMinecraft->progressRenderer->progressStage( IDS_SAVETRANSFER_STAGE_GET_DETAILS ); + Sleep(1); + pClass->m_eSaveTransferState=StorageManager.SaveTransferGetDetails(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,filename,&UIScene_LoadOrJoinMenu::SaveTransferReturned,pClass); + } + while(pClass->m_eSaveTransferState == C4JStorage::eSaveTransfer_Busy && !pClass->m_bSaveTransferCancelled ); + } +} + +void UIScene_LoadOrJoinMenu::RequestFileData( SaveTransferStateContainer *pClass, wchar_t *filename ) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + WCHAR wcTemp[256]; + + pMinecraft->progressRenderer->progressStagePercentage(0); + + swprintf(wcTemp,app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,UIScene_LoadOrJoinMenu::s_ulFileSize); + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + +#ifdef _DEBUG_MENUS_ENABLED + if(app.GetLoadSavesFromFolderEnabled()) + { + File targetFile( wstring(L"FakeTMSPP\\").append(filename) ); + if(targetFile.exists()) + { + HANDLE hSaveFile = CreateFile( targetFile.getPath().c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); + + m_debugTransferDetails.pbData = new BYTE[m_debugTransferDetails.ulFileLen]; + + DWORD numberOfBytesRead = 0; + ReadFile( hSaveFile,m_debugTransferDetails.pbData,m_debugTransferDetails.ulFileLen,&numberOfBytesRead,NULL); + assert(numberOfBytesRead == m_debugTransferDetails.ulFileLen); + + CloseHandle(hSaveFile); + + SaveTransferReturned(pClass,&m_debugTransferDetails); + } + } + else +#endif + { + do + { + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + pMinecraft->progressRenderer->progressStage( -1 ); + Sleep(1); + pClass->m_eSaveTransferState=StorageManager.SaveTransferGetData(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,filename,&UIScene_LoadOrJoinMenu::SaveTransferReturned,&UIScene_LoadOrJoinMenu::SaveTransferUpdateProgress,pClass,pClass); + } + while(pClass->m_eSaveTransferState == C4JStorage::eSaveTransfer_Busy && !pClass->m_bSaveTransferCancelled ); + } +} + +int UIScene_LoadOrJoinMenu::SaveTransferReturned(LPVOID lpParam,C4JStorage::SAVETRANSFER_FILE_DETAILS *pSaveTransferDetails) +{ + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + app.DebugPrintf("Save Transfer - size is %d\n",pSaveTransferDetails->ulFileLen); + + // if the file data is null, then assume this is the file size retrieval + if(pSaveTransferDetails->pbData==NULL) + { + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_FileSizeRetrieved; + UIScene_LoadOrJoinMenu::s_ulFileSize=pSaveTransferDetails->ulFileLen; + } + else + { + delete UIScene_LoadOrJoinMenu::s_transferData.data; + UIScene_LoadOrJoinMenu::s_transferData = byteArray(pSaveTransferDetails->pbData, UIScene_LoadOrJoinMenu::s_ulFileSize); + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_FileDataRetrieved; + } + + return 0; +} + +int UIScene_LoadOrJoinMenu::SaveTransferUpdateProgress(LPVOID lpParam,unsigned long ulBytesReceived) +{ + WCHAR wcTemp[256]; + + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(pClass->m_bSaveTransferCancelled) // was cancelled + { + pMinecraft->progressRenderer->progressStage(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLING); + swprintf(wcTemp,app.GetString(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLING)); + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + } + else + { + unsigned int uiProgress=(unsigned int)(((float)ulBytesReceived/float(UIScene_LoadOrJoinMenu::s_ulFileSize))*100.0f); + + pMinecraft->progressRenderer->progressStagePercentage(uiProgress); + swprintf(wcTemp,app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),((float)(ulBytesReceived))/1024000.0f,((float)UIScene_LoadOrJoinMenu::s_ulFileSize)/1024000.0f); + m_wstrStageText=wcTemp; + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + } + + return 0; +} + +void UIScene_LoadOrJoinMenu::CancelSaveTransferCallback(LPVOID lpParam) +{ + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + + if(!pClass->m_bSaveTransferCancelled) + { + StorageManager.CancelSaveTransfer(UIScene_LoadOrJoinMenu::CancelSaveTransferCompleteCallback,pClass); + + pClass->m_bSaveTransferCancelled=true; + } + //pClass->m_bSaveTransferInProgress=false; +} + +int UIScene_LoadOrJoinMenu::CancelSaveTransferCompleteCallback(LPVOID lpParam) +{ + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + // change the state to idle to get the download thread to terminate + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + return 0; +} + +int UIScene_LoadOrJoinMenu::NeedSyncMessageReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu *pClass = (UIScene_LoadOrJoinMenu *)pParam; + LoadMenuInitData *params = (LoadMenuInitData *)pParam; + + if( result == C4JStorage::EMessage_ResultAccept ) + { + // navigate to the settings scene + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadMenu, pClass->m_loadMenuInitData); + } + else + { + delete pClass->m_loadMenuInitData; + pClass->m_bIgnoreInput = false; + } + + return 0; +} + + +#endif + + +#ifdef _XBOX_ONE +void UIScene_LoadOrJoinMenu::HandleDLCLicenseChange() +{ + // may have installed Halloween on this menu + app.StartInstallDLCProcess(m_iPad); +} +#endif + +#if defined _XBOX_ONE || defined __ORBIS__ +int UIScene_LoadOrJoinMenu::CopySaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + + LoadingInputParams *loadingParams = new LoadingInputParams(); + void *uniqueId = (LPVOID)pClass->GetCallbackUniqueId(); + loadingParams->func = &UIScene_LoadOrJoinMenu::CopySaveThreadProc; + loadingParams->lpParam = uniqueId; + loadingParams->waitForThreadToDelete = true; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + loadingParams->cancelFunc=&UIScene_LoadOrJoinMenu::CancelCopySaveCallback; + loadingParams->m_cancelFuncParam=uniqueId; + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + ui.NavigateToScene(iPad,eUIScene_FullscreenProgress, loadingParams); + } + else + { + pClass->m_bIgnoreInput=false; + } + + return 0; +} + +int UIScene_LoadOrJoinMenu::CopySaveThreadProc( LPVOID lpParameter ) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStart(IDS_PROGRESS_COPYING_SAVE); + pMinecraft->progressRenderer->progressStage( -1 ); + + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParameter); + if( pClass ) + { + pClass->m_bCopying = true; + pClass->m_bCopyingCancelled = false; + ui.LeaveCallbackIdCriticalSection(); + // Copy save data takes two callbacks - one for completion, and one for progress. The progress callback also lets us cancel the operation, if we return false. + StorageManager.CopySaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex - pClass->m_iDefaultButtonsC],UIScene_LoadOrJoinMenu::CopySaveDataReturned,UIScene_LoadOrJoinMenu::CopySaveDataProgress,lpParameter); + + bool bContinue = true; + do + { + Sleep(100); + ui.EnterCallbackIdCriticalSection(); + pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParameter); + if( pClass ) + { + bContinue = pClass->m_bCopying; + } + else + { + bContinue = false; + } + ui.LeaveCallbackIdCriticalSection(); + } while( bContinue ); + } + else + { + ui.LeaveCallbackIdCriticalSection(); + } + + return 0; +} + +int UIScene_LoadOrJoinMenu::CopySaveDataReturned(LPVOID lpParam, bool success, C4JStorage::ESaveGameState stat) +{ + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + + if(pClass) + { + if(success) + { + pClass->m_bCopying = false; + // wipe the list and repopulate it + pClass->m_iState=e_SavesRepopulateAfterDelete; + ui.LeaveCallbackIdCriticalSection(); + } + else + { +#ifdef __ORBIS__ + UINT uiIDA[1]; + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + uiIDA[0]=IDS_OK; + + if( stat == C4JStorage::ESaveGame_CopyCompleteFailLocalStorage ) + { + ui.LeaveCallbackIdCriticalSection(); + ui.RequestMessageBox(IDS_COPYSAVE_FAILED_TITLE, IDS_COPYSAVE_FAILED_LOCAL, uiIDA, 1, ProfileManager.GetPrimaryPad(), CopySaveErrorDialogFinishedCallback, lpParam, app.GetStringTable()); + } + else if( stat == C4JStorage::ESaveGame_CopyCompleteFailQuota ) + { + ui.LeaveCallbackIdCriticalSection(); + ui.RequestMessageBox(IDS_COPYSAVE_FAILED_TITLE, IDS_COPYSAVE_FAILED_QUOTA, uiIDA, 1, ProfileManager.GetPrimaryPad(), CopySaveErrorDialogFinishedCallback, lpParam, app.GetStringTable()); + } + else + { + pClass->m_bCopying = false; + ui.LeaveCallbackIdCriticalSection(); + } +#else + pClass->m_bCopying = false; + ui.LeaveCallbackIdCriticalSection(); +#endif + } + } + else + { + ui.LeaveCallbackIdCriticalSection(); + } + return 0; +} + +bool UIScene_LoadOrJoinMenu::CopySaveDataProgress(LPVOID lpParam, int percent) +{ + bool bContinue = false; + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + if( pClass ) + { + bContinue = !pClass->m_bCopyingCancelled; + } + ui.LeaveCallbackIdCriticalSection(); + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStagePercentage(percent); + + return bContinue; +} + +void UIScene_LoadOrJoinMenu::CancelCopySaveCallback(LPVOID lpParam) +{ + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + if( pClass ) + { + pClass->m_bCopyingCancelled = true; + } + ui.LeaveCallbackIdCriticalSection(); +} + +int UIScene_LoadOrJoinMenu::CopySaveErrorDialogFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + ui.EnterCallbackIdCriticalSection(); + UIScene_LoadOrJoinMenu* pClass = (UIScene_LoadOrJoinMenu*)ui.GetSceneFromCallbackId((size_t)pParam); + if( pClass ) + { + pClass->m_bCopying = false; + } + ui.LeaveCallbackIdCriticalSection(); + + return 0; +} + +#endif // _XBOX_ONE diff --git a/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.h b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.h new file mode 100644 index 0000000..01d94b0 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadOrJoinMenu.h @@ -0,0 +1,300 @@ +#pragma once + +#include "UIScene.h" + +class LevelGenerationOptions; + + +#if defined __PS3__ || defined __ORBIS__ || defined(__PSVITA__) +#define SONY_REMOTE_STORAGE_DOWNLOAD +#endif +#if defined __PS3__ || __PSVITA__ +#define SONY_REMOTE_STORAGE_UPLOAD +#endif + + +class UIScene_LoadOrJoinMenu : public UIScene +{ +private: + enum EControls + { + eControl_SavesList, + eControl_GamesList, +#if defined(_XBOX_ONE) || defined(__ORBIS__) + eControl_SpaceIndicator, +#endif + }; + + enum EState + { + e_SavesIdle, + e_SavesRepopulate, + e_SavesRepopulateAfterMashupHide, + e_SavesRepopulateAfterDelete, + e_SavesRepopulateAfterTransferDownload, + }; + + enum eActions + { + eAction_None=0, + eAction_ViewInvites, + eAction_JoinGame, + }; + eActions m_eAction; + + static const int JOIN_LOAD_CREATE_BUTTON_INDEX = 0; + + SaveListDetails *m_saveDetails; + int m_iSaveDetailsCount; + +protected: + UIControl_SaveList m_buttonListSaves; + UIControl_SaveList m_buttonListGames; + UIControl_Label m_labelSavesListTitle, m_labelJoinListTitle, m_labelNoGames; + UIControl m_controlSavesTimer, m_controlJoinTimer; +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UIControl_SpaceIndicatorBar m_spaceIndicatorSaves; +#endif + +private: + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonListSaves, "SavesList") + UI_MAP_ELEMENT( m_buttonListGames, "JoinList") + + UI_MAP_ELEMENT( m_labelSavesListTitle, "SavesListTitle") + UI_MAP_ELEMENT( m_labelJoinListTitle, "JoinListTitle") + UI_MAP_ELEMENT( m_labelNoGames, "NoGames") + + UI_MAP_ELEMENT( m_controlSavesTimer, "SavesTimer") + UI_MAP_ELEMENT( m_controlJoinTimer, "JoinTimer") + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UI_MAP_ELEMENT( m_spaceIndicatorSaves, "SaveSizeBar") +#endif + UI_END_MAP_ELEMENTS_AND_NAMES() + + int m_iDefaultButtonsC; + int m_iMashUpButtonsC; + int m_iState; + + vector *m_currentSessions; + vector m_generators; + vector *m_saves; + + bool m_bIgnoreInput; + bool m_bAllLoaded; + bool m_bRetrievingSaveThumbnails; + bool m_bSaveThumbnailReady; + bool m_bShowingPartyGamesOnly; + bool m_bInParty; + JoinMenuInitData *m_initData; + bool m_bMultiplayerAllowed; + int m_iTexturePacksNotInstalled; + int m_iRequestingThumbnailId; + SAVE_DETAILS *m_pSaveDetails; + bool m_bSavesDisplayed; + bool m_bExitScene; + bool m_bCopying; + bool m_bCopyingCancelled; + int m_iSaveInfoC; + int m_iSaveListIndex; + int m_iGameListIndex; + //int *m_iConfigA; // track the texture packs that we don't have installed +#ifndef _XBOX_ONE + bool m_bSaveTransferInProgress; + bool m_bSaveTransferCancelled; +#endif + bool m_bUpdateSaveSize; + +public: + UIScene_LoadOrJoinMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_LoadOrJoinMenu(); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual void handleDestroy(); + virtual void handleLoseFocus(); + virtual void handleGainFocus(bool navBack); + virtual void handleTimerComplete(int id); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleInitFocus(F64 controlId, F64 childId); + + virtual EUIScene getSceneType() { return eUIScene_LoadOrJoinMenu;} + + static void UpdateGamesListCallback(LPVOID pParam); +#ifdef _XBOX_ONE + void HandleDLCLicenseChange(); +#endif + virtual void tick(); + +private: + void Initialise(); + void GetSaveInfo(); + void UpdateGamesList(); + void AddDefaultButtons(); + bool DoesSavesListHaveFocus(); + bool DoesMashUpWorldHaveFocus(); + bool DoesGamesListHaveFocus(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + + static int LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes); + static int LoadSaveCallback(LPVOID lpParam,bool bRes); + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes); + static int RenameSaveDataReturned(LPVOID lpParam,bool bRes); + static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes); +protected: + void handlePress(F64 controlId, F64 childId); + void LoadLevelGen(LevelGenerationOptions *levelGen); + void LoadSaveFromDisk(File *saveFile, ESavePlatform savePlatform = SAVE_FILE_PLATFORM_LOCAL); +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + void LoadSaveFromCloud(); +#endif +public: + virtual void HandleDLCMountingComplete(); + +#ifdef __ORBIS__ + void LoadRemoteFileFromDisk(char* remoteFilename); +#endif + +private: + void CheckAndJoinGame(int gameIndex); +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int PSN_SignInReturned(void *pParam,bool bContinue, int iPad); + static void remoteStorageGetSaveCallback(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); +#endif + +#ifdef __ORBIS__ + //static int PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif +#ifdef _XBOX_ONE + typedef struct _SaveTransferStateContainer + { + int m_iProgress; + bool m_bSaveTransferInProgress; + bool m_bSaveTransferCancelled; + int m_iPad; + C4JStorage::eSaveTransferState m_eSaveTransferState; + UIScene_LoadOrJoinMenu *m_pClass; + } SaveTransferStateContainer; + enum ESaveTransferFiles + { + eSaveTransferFile_Marker, + eSaveTransferFile_Metadata, + eSaveTransferFile_SaveData, + }; + static ESaveTransferFiles s_eSaveTransferFile; + static unsigned long s_ulFileSize; + static byteArray s_transferData; + static wstring m_wstrStageText; + LoadMenuInitData *m_loadMenuInitData; + +#ifdef _DEBUG_MENUS_ENABLED + static C4JStorage::SAVETRANSFER_FILE_DETAILS m_debugTransferDetails; +#endif + + void LaunchSaveTransfer(); + static int DownloadXbox360SaveThreadProc( LPVOID lpParameter ); + static void RequestFileSize( SaveTransferStateContainer *pClass, wchar_t *filename ); + static void RequestFileData( SaveTransferStateContainer *pClass, wchar_t *filename ); + static int SaveTransferReturned(LPVOID lpParam,C4JStorage::SAVETRANSFER_FILE_DETAILS *pSaveTransferDetails); + static int SaveTransferUpdateProgress(LPVOID lpParam,unsigned long ulBytesReceived); + static void CancelSaveTransferCallback(LPVOID lpParam); + static int NeedSyncMessageReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CancelSaveTransferCompleteCallback(LPVOID lpParam); + +#endif + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + enum eSaveTransferState + { + eSaveTransfer_Idle, + eSaveTransfer_Busy, + eSaveTransfer_GetRemoteSaveInfo, + eSaveTransfer_GettingRemoteSaveInfo, + eSaveTransfer_CreateDummyFile, + eSaveTransfer_CreatingDummyFile, + eSaveTransfer_GettingFileSize, + eSaveTransfer_FileSizeRetrieved, + eSaveTransfer_GetFileData, + eSaveTransfer_GettingFileData, + eSaveTransfer_FileDataRetrieved, + eSaveTransfer_GetSavesInfo, + eSaveTransfer_GettingSavesInfo, + eSaveTransfer_LoadSaveFromDisc, + eSaveTransfer_LoadingSaveFromDisc, + eSaveTransfer_CreatingNewSave, + eSaveTransfer_Converting, + eSaveTransfer_Saving, + eSaveTransfer_Succeeded, + eSaveTransfer_Cancelled, + eSaveTransfer_Error, + eSaveTransfer_ErrorDeletingSave, + eSaveTransfer_ErrorMesssage, + eSaveTransfer_Finished, + + }; + eSaveTransferState m_eSaveTransferState; + static unsigned long m_ulFileSize; + static wstring m_wstrStageText; + int m_iProgress; + char m_downloadedUniqueFilename[64];//SCE_SAVE_DATA_DIRNAME_DATA_MAXSIZE]; + bool m_saveTransferDownloadCancelled; + void LaunchSaveTransfer(); + static int CreateDummySaveDataCallback(LPVOID lpParam,bool bRes); + static int CrossSaveGetSavesInfoCallback(LPVOID lpParam, SAVE_DETAILS *pSaveDetails,bool bRes); + static int LoadCrossSaveDataCallback(void *pParam,bool bIsCorrupt, bool bIsOwner); + static int CrossSaveFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CrossSaveDeleteOnErrorReturned(LPVOID lpParam,bool bRes); + static int RemoteSaveNotFoundCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DownloadSonyCrossSaveThreadProc( LPVOID lpParameter ); + static void SaveTransferReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); + static ConsoleSaveFile* SonyCrossSaveConvert(); + + static void CancelSaveTransferCallback(LPVOID lpParam); +#endif + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + enum eSaveUploadState + { + eSaveUpload_Idle, + eSaveUpload_UploadingFileData, + eSaveUpload_FileDataUploaded, + eSaveUpload_Cancelled, + eSaveUpload_Error, + esaveUpload_Finished + }; + + eSaveUploadState m_eSaveUploadState; + bool m_saveTransferUploadCancelled; + + void LaunchSaveUpload(); + static int UploadSonyCrossSaveThreadProc( LPVOID lpParameter ); + static void SaveUploadReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); + static void CancelSaveUploadCallback(LPVOID lpParam); + static int SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CrossSaveUploadFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +#if defined _XBOX_ONE || defined __ORBIS__ + static int CopySaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CopySaveThreadProc( LPVOID lpParameter ); + static int CopySaveDataReturned( LPVOID lpParameter, bool success, C4JStorage::ESaveGameState state ); + static bool CopySaveDataProgress(LPVOID lpParam, int percent); + static void CancelCopySaveCallback(LPVOID lpParam); + static int CopySaveErrorDialogFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp new file mode 100644 index 0000000..7724aaa --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp @@ -0,0 +1,2043 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Mth.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\User.h" +#include "..\..\MinecraftServer.h" +#include "UI.h" +#include "UIScene_MainMenu.h" +#ifdef __ORBIS__ +#include +#endif + +Random *UIScene_MainMenu::random = new Random(); + +UIScene_MainMenu::UIScene_MainMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ +#ifdef __ORBIS + //m_ePatchCheckState=ePatchCheck_Idle; + m_bRunGameChosen=false; + m_bErrorDialogRunning=false; +#endif + + + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + parentLayer->addComponent(iPad,eUIComponent_Panorama); + parentLayer->addComponent(iPad,eUIComponent_Logo); + + m_eAction=eAction_None; + m_bIgnorePress=false; + + + m_buttons[(int)eControl_PlayGame].init(app.GetString(IDS_PLAY_GAME),eControl_PlayGame); + +#ifdef _XBOX_ONE + if(!ProfileManager.IsFullVersion()) m_buttons[(int)eControl_PlayGame].setLabel(app.GetString(IDS_PLAY_TRIAL_GAME)); + app.SetReachedMainMenu(); +#endif + + m_buttons[(int)eControl_Leaderboards].init(app.GetString(IDS_LEADERBOARDS),eControl_Leaderboards); + m_buttons[(int)eControl_Achievements].init(app.GetString(IDS_ACHIEVEMENTS),eControl_Achievements); + m_buttons[(int)eControl_HelpAndOptions].init(app.GetString(IDS_HELP_AND_OPTIONS),eControl_HelpAndOptions); + if(ProfileManager.IsFullVersion()) + { + m_bTrialVersion=false; + m_buttons[(int)eControl_UnlockOrDLC].init(app.GetString(IDS_DOWNLOADABLECONTENT),eControl_UnlockOrDLC); + } + else + { + m_bTrialVersion=true; + m_buttons[(int)eControl_UnlockOrDLC].init(app.GetString(IDS_UNLOCK_FULL_GAME),eControl_UnlockOrDLC); + } + +#ifndef _DURANGO + m_buttons[(int)eControl_Exit].init(app.GetString(IDS_EXIT_GAME),eControl_Exit); +#else + m_buttons[(int)eControl_XboxHelp].init(app.GetString(IDS_XBOX_HELP_APP), eControl_XboxHelp); +#endif + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // Not allowed to exit from a PS3 game from the game - have to use the PS button + removeControl( &m_buttons[(int)eControl_Exit], false ); + // We don't have a way to display trophies/achievements, so remove the button + removeControl( &m_buttons[(int)eControl_Achievements], false ); + m_bLaunchFullVersionPurchase=false; +#endif +#ifdef _DURANGO + // Allowed to not have achievements in the menu + removeControl( &m_buttons[(int)eControl_Achievements], false ); + // Not allowed to exit from a Xbox One game from the game - have to use the Home button + //removeControl( &m_buttons[(int)eControl_Exit], false ); + m_bWaitingForDLCInfo=false; +#endif + + doHorizontalResizeCheck(); + + m_splash = L""; + + wstring filename = L"splashes.txt"; + if( app.hasArchiveFile(filename) ) + { + byteArray splashesArray = app.getArchiveFile(filename); + ByteArrayInputStream bais(splashesArray); + InputStreamReader isr( &bais ); + BufferedReader br( &isr ); + + wstring line = L""; + while ( !(line = br.readLine()).empty() ) + { + line = trimString( line ); + if (line.length() > 0) + { + m_splashes.push_back(line); + } + } + + br.close(); + } + + m_bIgnorePress=false; + m_bLoadTrialOnNetworkManagerReady = false; + + // 4J Stu - Clear out any loaded game rules + app.setLevelGenerationOptions(NULL); + + // 4J Stu - Reset the leaving game flag so that we correctly handle signouts while in the menus + g_NetworkManager.ResetLeavingGame(); + +#if TO_BE_IMPLEMENTED + // Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); +#endif +} + +UIScene_MainMenu::~UIScene_MainMenu() +{ + m_parentLayer->removeComponent(eUIComponent_Panorama); + m_parentLayer->removeComponent(eUIComponent_Logo); +} + +void UIScene_MainMenu::updateTooltips() +{ + int iX = -1; + int iA = -1; + if(!m_bIgnorePress) + { + iA = IDS_TOOLTIPS_SELECT; + +#ifdef _XBOX_ONE + iX = IDS_TOOLTIPS_CHOOSE_USER; +#elif defined __PSVITA__ + if(ProfileManager.IsFullVersion()) + { + iX = IDS_TOOLTIP_CHANGE_NETWORK_MODE; + } +#endif + } + ui.SetTooltips( DEFAULT_XUI_MENU_USER, iA, -1, iX); +} + +void UIScene_MainMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); +} + +void UIScene_MainMenu::handleGainFocus(bool navBack) +{ + UIScene::handleGainFocus(navBack); + ui.ShowPlayerDisplayname(false); + m_bIgnorePress=false; + + // 4J-JEV: This needs to come before SetLockedProfile(-1) as it wipes the XbLive contexts. + if (!navBack) + { + for (int iPad = 0; iPad < MAX_LOCAL_PLAYERS; iPad++) + { + // For returning to menus after exiting a game. + if (ProfileManager.IsSignedIn(iPad) ) + { + ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MENUS, false); + } + } + } + ProfileManager.SetLockedProfile(-1); + + m_bIgnorePress = false; + updateTooltips(); + +#ifdef _DURANGO + ProfileManager.ClearGameUsers(); +#endif + + if(navBack && ProfileManager.IsFullVersion()) + { + // Replace the Unlock Full Game with Downloadable Content + m_buttons[(int)eControl_UnlockOrDLC].setLabel(app.GetString(IDS_DOWNLOADABLECONTENT)); + } + +#if TO_BE_IMPLEMENTED + // Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + m_Timer.SetShow(FALSE); +#endif + m_controlTimer.setVisible( false ); + + // 4J-PB - remove the "hobo humping" message legal say we can't have, and the 1080p one for Vita +#ifdef __PSVITA__ + int splashIndex = eSplashRandomStart + 2 + random->nextInt( (int)m_splashes.size() - (eSplashRandomStart + 2) ); +#else + int splashIndex = eSplashRandomStart + 1 + random->nextInt( (int)m_splashes.size() - (eSplashRandomStart + 1) ); +#endif + + // Override splash text on certain dates + SYSTEMTIME LocalSysTime; + GetLocalTime( &LocalSysTime ); + if (LocalSysTime.wMonth == 11 && LocalSysTime.wDay == 9) + { + splashIndex = eSplashHappyBirthdayEx; + } + else if (LocalSysTime.wMonth == 6 && LocalSysTime.wDay == 1) + { + splashIndex = eSplashHappyBirthdayNotch; + } + else if (LocalSysTime.wMonth == 12 && LocalSysTime.wDay == 24) // the Java game shows this on Christmas Eve, so we will too + { + splashIndex = eSplashMerryXmas; + } + else if (LocalSysTime.wMonth == 1 && LocalSysTime.wDay == 1) + { + splashIndex = eSplashHappyNewYear; + } + //splashIndex = 47; // Very short string + //splashIndex = 194; // Very long string + //splashIndex = 295; // Coloured + //splashIndex = 296; // Noise + m_splash = m_splashes.at( splashIndex ); +} + +wstring UIScene_MainMenu::getMoviePath() +{ + return L"MainMenu"; +} + +void UIScene_MainMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + if(m_bIgnorePress) return; + +#if defined (__ORBIS__) || defined (__PSVITA__) + // ignore all players except player 0 - it's their profile that is currently being used + if(iPad!=0) return; +#endif + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + ProfileManager.SetPrimaryPad(iPad); + ProfileManager.SetLockedProfile(-1); + sendInputToMovie(key, repeat, pressed, released); + } + break; +#ifdef _XBOX_ONE + case ACTION_MENU_X: + if(pressed) + { + m_bIgnorePress = true; + ProfileManager.RequestSignInUI(false, false, false, false, false, ChooseUser_SignInReturned, this, iPad); + } + break; +#endif +#ifdef __PSVITA__ + case ACTION_MENU_X: + if(pressed && ProfileManager.IsFullVersion()) + { + UINT uiIDA[2]; + uiIDA[0]=IDS__NETWORK_PSN; + uiIDA[1]=IDS_NETWORK_ADHOC; + ui.RequestMessageBox(IDS_SELECT_NETWORK_MODE_TITLE, IDS_SELECT_NETWORK_MODE_TEXT, uiIDA, 2, XUSER_INDEX_ANY, &UIScene_MainMenu::SelectNetworkModeReturned,this); + } + break; +#endif + + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_MainMenu::handlePress(F64 controlId, F64 childId) +{ + int primaryPad = ProfileManager.GetPrimaryPad(); + + int (*signInReturnedFunc) (LPVOID,const bool, const int iPad) = NULL; + + switch((int)controlId) + { + case eControl_PlayGame: +#ifdef __ORBIS__ + { + m_bIgnorePress=true; + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + ProfileManager.RefreshChatAndContentRestrictions(RefreshChatAndContentRestrictionsReturned_PlayGame, this); + } +#else + m_eAction=eAction_RunGame; + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + signInReturnedFunc = &UIScene_MainMenu::CreateLoad_SignInReturned; +#endif + break; + case eControl_Leaderboards: + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); +#ifdef __ORBIS__ + ProfileManager.RefreshChatAndContentRestrictions(RefreshChatAndContentRestrictionsReturned_Leaderboards, this); +#else + m_eAction=eAction_RunLeaderboards; + signInReturnedFunc = &UIScene_MainMenu::Leaderboards_SignInReturned; +#endif + break; + case eControl_Achievements: + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + m_eAction=eAction_RunAchievements; + signInReturnedFunc = &UIScene_MainMenu::Achievements_SignInReturned; + break; + case eControl_HelpAndOptions: + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + m_eAction=eAction_RunHelpAndOptions; + signInReturnedFunc = &UIScene_MainMenu::HelpAndOptions_SignInReturned; + break; + case eControl_UnlockOrDLC: + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + m_eAction=eAction_RunUnlockOrDLC; + signInReturnedFunc = &UIScene_MainMenu::UnlockFullGame_SignInReturned; + break; +#if defined _XBOX + case eControl_Exit: + if( ProfileManager.IsFullVersion() ) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CANCEL; + uiIDA[1]=IDS_OK; + ui.RequestMessageBox(IDS_WARNING_ARCADE_TITLE, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&UIScene_MainMenu::ExitGameReturned,this); + } + else + { +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(primaryPad,eUIScene_TrialExitUpsell); + } + break; +#endif + +#ifdef _DURANGO + case eControl_XboxHelp: + ui.PlayUISFX(eSFX_Press); + + m_eAction=eAction_RunXboxHelp; + signInReturnedFunc = &UIScene_MainMenu::XboxHelp_SignInReturned; + break; +#endif + + default: __debugbreak(); + } + + bool confirmUser = false; + + // Note: if no sign in returned func, assume this isn't required + if (signInReturnedFunc != NULL) + { + if(ProfileManager.IsSignedIn(primaryPad)) + { + if (confirmUser) + { + ProfileManager.RequestSignInUI(false, false, true, false, true, signInReturnedFunc, this, primaryPad); + } + else + { + RunAction(primaryPad); + } + } + else + { + // Ask user to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, primaryPad, &UIScene_MainMenu::MustSignInReturned, this, app.GetStringTable()); + } + } +} + +// Run current action +void UIScene_MainMenu::RunAction(int iPad) +{ + switch(m_eAction) + { + case eAction_RunGame: + RunPlayGame(iPad); + break; + case eAction_RunLeaderboards: + RunLeaderboards(iPad); + break; + case eAction_RunAchievements: + RunAchievements(iPad); + break; + case eAction_RunHelpAndOptions: + RunHelpAndOptions(iPad); + break; + case eAction_RunUnlockOrDLC: + RunUnlockOrDLC(iPad); + break; +#ifdef _DURANGO + case eAction_RunXboxHelp: + // 4J: Launch the dummy xbox help application. + WXS::User^ user = ProfileManager.GetUser(ProfileManager.GetPrimaryPad()); + Windows::Xbox::ApplicationModel::Help::Show(user); + break; +#endif + } +} + +void UIScene_MainMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + if(wcscmp((wchar_t *)region->name,L"Splash")==0) + { + PIXBeginNamedEvent(0,"Custom draw splash"); + customDrawSplash(region); + PIXEndNamedEvent(); + } +} + +void UIScene_MainMenu::customDrawSplash(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // 4J Stu - Move this to the ctor when the main menu is not the first scene we navigate to + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + delete customDrawRegion; + + + Font *font = pMinecraft->font; + + // build and render with the game call + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + glPushMatrix(); + + float width = region->x1 - region->x0; + float height = region->y1 - region->y0; + float xo = width/2; + float yo = height; + + glTranslatef(xo, yo, 0); + + glRotatef(-17, 0, 0, 1); + float sss = 1.8f - Mth::abs(Mth::sin(System::currentTimeMillis() % 1000 / 1000.0f * PI * 2) * 0.1f); + sss*=(m_fScreenWidth/m_fRawWidth); + + sss = sss * 100 / (font->width(m_splash) + 8 * 4); + glScalef(sss, sss, sss); + //drawCenteredString(font, splash, 0, -8, 0xffff00); + font->drawShadow(m_splash, 0 - (font->width(m_splash)) / 2, -8, 0xffff00); + glPopMatrix(); + + glDisable(GL_RESCALE_NORMAL); + + glEnable(GL_DEPTH_TEST); + + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); +} + +int UIScene_MainMenu::MustSignInReturned(void *pParam, int iPad, C4JStorage::EMessageResult result) +{ + UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + // we need to specify local game here to display local and LIVE profiles in the list + switch(pClass->m_eAction) + { + case eAction_RunGame: ProfileManager.RequestSignInUI(false, true, false, false, true, &UIScene_MainMenu::CreateLoad_SignInReturned, pClass, iPad ); break; + case eAction_RunHelpAndOptions: ProfileManager.RequestSignInUI(false, false, true, false, true, &UIScene_MainMenu::HelpAndOptions_SignInReturned, pClass, iPad ); break; + case eAction_RunLeaderboards: ProfileManager.RequestSignInUI(false, false, true, false, true, &UIScene_MainMenu::Leaderboards_SignInReturned, pClass, iPad ); break; + case eAction_RunAchievements: ProfileManager.RequestSignInUI(false, false, true, false, true, &UIScene_MainMenu::Achievements_SignInReturned, pClass, iPad ); break; + case eAction_RunUnlockOrDLC: ProfileManager.RequestSignInUI(false, false, true, false, true, &UIScene_MainMenu::UnlockFullGame_SignInReturned, pClass, iPad ); break; +#ifdef _DURANGO + case eAction_RunXboxHelp: ProfileManager.RequestSignInUI(false, false, true, false, true, &UIScene_MainMenu::XboxHelp_SignInReturned, pClass, iPad ); break; +#endif + } + } + else + { + pClass->m_bIgnorePress=false; + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_eAction) + { + case eAction_RunLeaderboardsPSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_MainMenu::Leaderboards_SignInReturned, pClass); + break; + case eAction_RunGamePSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_MainMenu::CreateLoad_SignInReturned, pClass); + break; + case eAction_RunUnlockOrDLCPSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_MainMenu::UnlockFullGame_SignInReturned, pClass); + break; + } +#elif defined __PSVITA__ + switch(pClass->m_eAction) + { + case eAction_RunLeaderboardsPSN: + //CD - Must force Ad-Hoc off if they want leaderboard PSN sign-in + //Save settings change + app.SetGameSettings(0, eGameSetting_PSVita_NetworkModeAdhoc, 0); + //Force off + CGameNetworkManager::setAdhocMode(false); + //Now Sign-in + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_MainMenu::Leaderboards_SignInReturned, pClass); + break; + case eAction_RunGamePSN: + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_MainMenu::CreateLoad_SignInReturned, pClass); + break; + case eAction_RunUnlockOrDLCPSN: + //CD - Must force Ad-Hoc off if they want commerce PSN sign-in + //Save settings change + app.SetGameSettings(0, eGameSetting_PSVita_NetworkModeAdhoc, 0); + //Force off + CGameNetworkManager::setAdhocMode(false); + //Now Sign-in + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_MainMenu::UnlockFullGame_SignInReturned, pClass); + break; + } +#else + switch(pClass->m_eAction) + { + case eAction_RunLeaderboardsPSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_MainMenu::Leaderboards_SignInReturned, pClass, true, iPad); + break; + case eAction_RunGamePSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_MainMenu::CreateLoad_SignInReturned, pClass, true, iPad); + break; + case eAction_RunUnlockOrDLCPSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_MainMenu::UnlockFullGame_SignInReturned, pClass, true, iPad); + break; + } + +#endif + } + else + { + if( pClass->m_eAction == eAction_RunGamePSN ) + { + if( result == C4JStorage::EMessage_Cancelled) + CreateLoad_SignInReturned(pClass, false, 0); + else + CreateLoad_SignInReturned(pClass, true, 0); + } + else + { + pClass->m_bIgnorePress=false; + } + } + + return 0; +} +#endif + +int UIScene_MainMenu::HelpAndOptions_SignInReturned(void *pParam,bool bContinue,int iPad) +{ + UIScene_MainMenu *pClass = (UIScene_MainMenu *)pParam; + + if(bContinue) + { + // 4J-JEV: Don't we only need to update rich-presence if the sign-in status changes. + ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MENUS, false); + +#if TO_BE_IMPLEMENTED + if(app.GetTMSDLCInfoRead()) +#endif + { + ProfileManager.SetLockedProfile(ProfileManager.GetPrimaryPad()); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(iPad,eUIScene_HelpAndOptionsMenu); + } +#if TO_BE_IMPLEMENTED + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions); + + // block all input + pClass->m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;im_Buttons[i].SetShow(FALSE); + } + + pClass->updateTooltips(); + + pClass->m_Timer.SetShow(TRUE); + } +#endif + } + else + { + pClass->m_bIgnorePress=false; + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_bIgnorePress = false; + + return 0; +} +#endif + +int UIScene_MainMenu::CreateLoad_SignInReturned(void *pParam, bool bContinue, int iPad) +{ + UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; + + if(bContinue) + { + // 4J-JEV: We only need to update rich-presence if the sign-in status changes. + ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MENUS, false); + + UINT uiIDA[1] = { IDS_OK }; + + if(ProfileManager.IsGuest(ProfileManager.GetPrimaryPad())) + { + pClass->m_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + ProfileManager.SetLockedProfile(ProfileManager.GetPrimaryPad()); + + + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + if(ProfileManager.IsFullVersion()) + { + bool bSignedInLive = ProfileManager.IsSignedInLive(iPad); +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode()) + { + if(SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + { + bSignedInLive = true; + } + else + { + // adhoc mode, but we didn't make the connection, turn off adhoc mode, and just go with whatever the regular online status is + CGameNetworkManager::setAdhocMode(false); + bSignedInLive = ProfileManager.IsSignedInLive(iPad); + } + } +#endif + + // Check if we're signed in to LIVE + if(bSignedInLive) + { + // 4J-PB - Need to check for installed DLC + if(!app.DLCInstallProcessCompleted()) app.StartInstallDLCProcess(iPad); + + if(ProfileManager.IsGuest(iPad)) + { + pClass->m_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + // 4J Stu - Not relevant to PS3 +#ifdef _XBOX_ONE +// if(app.GetTMSDLCInfoRead() && app.GetBanListRead(iPad)) + if(app.GetBanListRead(iPad)) + { + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + } + else + { + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + + // block all input + pClass->m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + // for(int i=0;iupdateTooltips(); + + pClass->m_controlTimer.setVisible( true ); + } +#endif +#if TO_BE_IMPLEMENTED + // check if all the TMS files are loaded + if(app.GetTMSDLCInfoRead() && app.GetTMSXUIDsFileRead() && app.GetBanListRead(iPad)) + { + if(StorageManager.SetSaveDevice(&UIScene_MainMenu::DeviceSelectReturned,pClass)==true) + { + // save device already selected + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); + // check for DLC + // start timer to track DLC check finished + pClass->m_Timer.SetShow(TRUE); + XuiSetTimer(pClass->m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); + } + } + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + + // block all input + pClass->m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;im_Buttons[i].SetShow(FALSE); + } + + updateTooltips(); + + pClass->m_Timer.SetShow(TRUE); + } +#else + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); +#endif + } + } + else + { +#if TO_BE_IMPLEMENTED + // offline + ProfileManager.DisplayOfflineProfile(&CScene_Main::CreateLoad_OfflineProfileReturned,pClass, ProfileManager.GetPrimaryPad() ); +#else + app.DebugPrintf("Offline Profile returned not implemented\n"); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); +#endif + } + } + else + { + // 4J-PB - if this is the trial game, we can't have any networking + // Can't apply the player's settings here - they haven't come back from the QuerySignInStatud call above yet. + // Need to let them action in the main loop when they come in + // ensure we've applied this player's settings + //app.ApplyGameSettingsChanged(iPad); + +#if defined(__PS3__) || defined(__ORBIS__) || defined( __PSVITA__) + // ensure we've applied this player's settings - we do have them on PS3 + app.ApplyGameSettingsChanged(iPad); +#endif + +#ifdef __ORBIS__ + if(!g_NetworkManager.IsReadyToPlayOrIdle()) + { + pClass->m_bLoadTrialOnNetworkManagerReady = true; + ui.NavigateToScene(iPad, eUIScene_Timer); + } + else +#endif + { + // go straight in to the trial level + LoadTrial(); + } + } + } + } + else + { + pClass->m_bIgnorePress=false; + + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + pClass->m_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + } + else + { + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { + pClass->m_bIgnorePress=false; +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see leaderboards + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); +#endif + } + else + { + ProfileManager.SetLockedProfile(ProfileManager.GetPrimaryPad()); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LeaderboardsMenu); + } + } + } + else + { + pClass->m_bIgnorePress=false; + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_bIgnorePress=false; + // 4J-JEV: We only need to update rich-presence if the sign-in status changes. + ProfileManager.SetCurrentGameActivity(iPad, CONTEXT_PRESENCE_MENUS, false); + + XShowAchievementsUI( ProfileManager.GetPrimaryPad() ); + } + else + { + pClass->m_bIgnorePress=false; + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;iRunUnlockOrDLC(iPad); + } + else + { + pClass->m_bIgnorePress=false; + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_errorCode = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + bool bPatchAvailable; + switch(pClass->m_errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + bPatchAvailable=true; + break; + default: + bPatchAvailable=false; + break; + } + + if(!bPatchAvailable) + { + pClass->m_eAction=eAction_RunGame; + signInReturnedFunc = &UIScene_MainMenu::CreateLoad_SignInReturned; + } + else + { + pClass->m_bRunGameChosen=true; + pClass->m_bErrorDialogRunning=true; + int32_t ret=sceErrorDialogInitialize(); + if ( ret==SCE_OK ) + { + SceErrorDialogParam param; + sceErrorDialogParamInitialize( ¶m ); + // 4J-PB - We want to display the option to get the patch now + param.errorCode = SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED;//pClass->m_errorCode; + ret = sceUserServiceGetInitialUser( ¶m.userId ); + if ( ret == SCE_OK ) + { + ret=sceErrorDialogOpen( ¶m ); + } + return; + } + +// UINT uiIDA[1]; +// uiIDA[0]=IDS_OK; +// ui.RequestMessageBox(IDS_PATCH_AVAILABLE_TITLE, IDS_PATCH_AVAILABLE_TEXT, uiIDA, 1, XUSER_INDEX_ANY,NULL,pClass); + } + + // Check if PSN is unavailable because of age restriction + if (pClass->m_errorCode == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(), &UIScene_MainMenu::PlayOfflineReturned, pClass, app.GetStringTable()); + + return; + } + + bool confirmUser = false; + + // Note: if no sign in returned func, assume this isn't required + if (signInReturnedFunc != NULL) + { + if(ProfileManager.IsSignedIn(primaryPad)) + { + if (confirmUser) + { + ProfileManager.RequestSignInUI(false, false, true, false, true, signInReturnedFunc, pClass, primaryPad); + } + else + { + pClass->RunAction(primaryPad); + } + } + else + { + // Ask user to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, primaryPad, &UIScene_MainMenu::MustSignInReturned, pClass, app.GetStringTable()); + } + } +} + +void UIScene_MainMenu::RefreshChatAndContentRestrictionsReturned_Leaderboards(void *pParam) +{ + int primaryPad = ProfileManager.GetPrimaryPad(); + + UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; + + int (*signInReturnedFunc) (LPVOID,const bool, const int iPad) = NULL; + + // 4J-PB - Check if there is a patch for the game + pClass->m_errorCode = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + bool bPatchAvailable; + switch(pClass->m_errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + bPatchAvailable=true; + break; + default: + bPatchAvailable=false; + break; + } + + if(!bPatchAvailable) + { + pClass->m_eAction=eAction_RunLeaderboards; + signInReturnedFunc = &UIScene_MainMenu::Leaderboards_SignInReturned; + } + else + { + int32_t ret=sceErrorDialogInitialize(); + pClass->m_bErrorDialogRunning=true; + if ( ret==SCE_OK ) + { + SceErrorDialogParam param; + sceErrorDialogParamInitialize( ¶m ); + // 4J-PB - We want to display the option to get the patch now + param.errorCode = SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED;//pClass->m_errorCode; + ret = sceUserServiceGetInitialUser( ¶m.userId ); + if ( ret == SCE_OK ) + { + ret=sceErrorDialogOpen( ¶m ); + } + } + +// UINT uiIDA[1]; +// uiIDA[0]=IDS_OK; +// ui.RequestMessageBox(IDS_PATCH_AVAILABLE_TITLE, IDS_PATCH_AVAILABLE_TEXT, uiIDA, 1, XUSER_INDEX_ANY,NULL,pClass); + } + + bool confirmUser = false; + + // Update error code + pClass->m_errorCode = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + // Check if PSN is unavailable because of age restriction + if (pClass->m_errorCode == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(), nullptr, pClass, app.GetStringTable()); + + return; + } + + // Note: if no sign in returned func, assume this isn't required + if (signInReturnedFunc != NULL) + { + if(ProfileManager.IsSignedIn(primaryPad)) + { + if (confirmUser) + { + ProfileManager.RequestSignInUI(false, false, true, false, true, signInReturnedFunc, pClass, primaryPad); + } + else + { + pClass->RunAction(primaryPad); + } + } + else + { + // Ask user to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, primaryPad, &UIScene_MainMenu::MustSignInReturned, pClass, app.GetStringTable()); + } + } +} + +int UIScene_MainMenu::PlayOfflineReturned(void *pParam, int iPad, C4JStorage::EMessageResult result) +{ + UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if (pClass->m_eAction == eAction_RunGame) + { + CreateLoad_SignInReturned(pClass, true, 0); + } + else + { + pClass->m_bIgnorePress=false; + } + } + else + { + pClass->m_bIgnorePress=false; + } + + return 0; +} +#endif + +void UIScene_MainMenu::RunPlayGame(int iPad) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // clear the remembered signed in users so their profiles get read again + app.ClearSignInChangeUsersMask(); + + app.ReleaseSaveThumbnail(); + + if(ProfileManager.IsGuest(iPad)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + m_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + ProfileManager.SetLockedProfile(iPad); + + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + + // 4J-PB - Need to check for installed DLC + if(!app.DLCInstallProcessCompleted()) app.StartInstallDLCProcess(iPad); + + if(ProfileManager.IsFullVersion()) + { + // are we offline? + bool bSignedInLive = ProfileManager.IsSignedInLive(iPad); +#ifdef __PSVITA__ + if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_PSVita_NetworkModeAdhoc) == true) + { + CGameNetworkManager::setAdhocMode(true); + bSignedInLive = SQRNetworkManager_AdHoc_Vita::GetAdhocStatus(); + app.DebugPrintf("Adhoc mode signed in : %s\n", bSignedInLive ? "true" : "false"); + } + else + { + CGameNetworkManager::setAdhocMode(false); + app.DebugPrintf("PSN mode signed in : %s\n", bSignedInLive ? "true" : "false"); + } + +#endif //__PSVITA__ + + if(!bSignedInLive) + { +#if defined(__PS3__) || defined __PSVITA__ + // enable input again + m_bIgnorePress=false; + + // Not sure why 360 doesn't need this, but leaving as __PS3__ only for now until we see that it does. Without this, on a PS3 offline game, the primary player just gets the default Player1234 type name + pMinecraft->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + m_eAction=eAction_RunGamePSN; + // get them to sign in to online + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode()) + { + uiIDA[0]=IDS_NETWORK_ADHOC; + // this should be "Connect to adhoc network" + ui.RequestMessageBox(IDS_PRO_NOTADHOCONLINE_TITLE, IDS_PRO_NOTADHOCONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_MainMenu::MustSignInReturnedPSN,this, app.GetStringTable()); + } + else + { + /* 4J-PB - Add this after release + // Determine why they're not "signed in live" + if (ProfileManager.IsSignedInPSN(iPad)) + { + m_eAction=eAction_RunGame; + // Signed in to PSN but not connected (no internet access) + + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, UIScene_MainMenu::PlayOfflineReturned, this, app.GetStringTable()); + } + else + { + m_eAction=eAction_RunGamePSN; + // Not signed in to PSN + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPad, &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable()); + return; + } */ + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_MainMenu::MustSignInReturnedPSN,this, app.GetStringTable()); + + } +#else + + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPad, &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable()); +#endif + +#elif defined __ORBIS__ + + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + m_eAction=eAction_RunGame; + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, UIScene_MainMenu::PlayOfflineReturned, this, app.GetStringTable()); + } + else + { + m_eAction=eAction_RunGamePSN; + // Not signed in to PSN + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, iPad, &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + return; + } +#else + ProfileManager.SetLockedProfile(iPad); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); +#endif + } + else + { +#ifdef _XBOX_ONE + if(!app.GetBanListRead(iPad)) + { + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files +// for(int i=0;iuser->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + // save device already selected + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + // check for DLC + // start timer to track DLC check finished + m_Timer.SetShow(TRUE); + XuiSetTimer(m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + //app.NavigateToScene(iPad,eUIScene_MultiGameJoinLoad); + } + } + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;iuser->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); +#endif + } + } + else + { + // 4J-PB - if this is the trial game, we can't have any networking + // go straight in to the trial level + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + // Can't apply the player's settings here - they haven't come back from the QuerySignInStatud call above yet. + // Need to let them action in the main loop when they come in + // ensure we've applied this player's settings + //app.ApplyGameSettingsChanged(iPad); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // ensure we've applied this player's settings - we do have them on PS3 + app.ApplyGameSettingsChanged(iPad); +#endif + +#ifdef __ORBIS__ + if(!g_NetworkManager.IsReadyToPlayOrIdle()) + { + m_bLoadTrialOnNetworkManagerReady = true; + ui.NavigateToScene(iPad, eUIScene_Timer); + } + else +#endif + { + LoadTrial(); + } + } + } +} + +void UIScene_MainMenu::RunLeaderboards(int iPad) +{ + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // guests can't look at leaderboards + if(ProfileManager.IsGuest(iPad)) + { + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else if(!ProfileManager.IsSignedInLive(iPad)) + { +#if defined __PS3__ || defined __PSVITA__ + m_eAction=eAction_RunLeaderboardsPSN; + // get them to sign in to online + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),&UIScene_MainMenu::MustSignInReturnedPSN,this, app.GetStringTable()); + +/* 4J-PB - Add this after release +#elif defined __PSVITA__ + m_eAction=eAction_RunLeaderboardsPSN; + // Determine why they're not "signed in live" + if (ProfileManager.IsSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_CURRENTLY_NOT_ONLINE_TITLE, IDS_PRO_PSNOFFLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable()); + return; + }*/ +#elif defined __ORBIS__ + m_eAction=eAction_RunLeaderboardsPSN; + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), &UIScene_MainMenu::MustSignInReturnedPSN, this, app.GetStringTable()); + return; + } +#else + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); +#endif + } + else + { + // we're supposed to check for parental control restrictions before showing leaderboards + // The title enforces the user's NP parental control setting for age-based content + //restriction in network communications. + // If age restrictions are in place and the user's age does not meet + // the age restriction of the title's online service content rating (CERO, ESRB, PEGI, etc.), then the title must + //display a message such as the following and disallow online service for this user. + + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see leaderboards + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,this, app.GetStringTable()); +#endif + } + else + { + ProfileManager.SetLockedProfile(iPad); + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(iPad, eUIScene_LeaderboardsMenu); + } + } +} +void UIScene_MainMenu::RunUnlockOrDLC(int iPad) +{ + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // Check if this means downloadable content + if(ProfileManager.IsFullVersion()) + { +#ifdef __ORBIS__ + // 4J-PB - Check if there is a patch for the game + m_errorCode = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + bool bPatchAvailable; + switch(m_errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + bPatchAvailable=true; + break; + default: + bPatchAvailable=false; + break; + } + + if(bPatchAvailable) + { + m_bIgnorePress=false; + + int32_t ret=sceErrorDialogInitialize(); + m_bErrorDialogRunning=true; + if ( ret==SCE_OK ) + { + SceErrorDialogParam param; + sceErrorDialogParamInitialize( ¶m ); + // 4J-PB - We want to display the option to get the patch now + param.errorCode = SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED;//pClass->m_errorCode; + ret = sceUserServiceGetInitialUser( ¶m.userId ); + if ( ret == SCE_OK ) + { + ret=sceErrorDialogOpen( ¶m ); + } + } + +// UINT uiIDA[1]; +// uiIDA[0]=IDS_OK; +// ui.RequestMessageBox(IDS_PATCH_AVAILABLE_TITLE, IDS_PATCH_AVAILABLE_TEXT, uiIDA, 1, XUSER_INDEX_ANY,NULL,this); + return; + } + + // Check if PSN is unavailable because of age restriction + if (m_errorCode == SCE_NP_ERROR_AGE_RESTRICTION) + { + m_bIgnorePress=false; + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(), nullptr, this, app.GetStringTable()); + + return; + } +#endif + // downloadable content + if(ProfileManager.IsSignedInLive(iPad)) + { + if(ProfileManager.IsGuest(iPad)) + { + m_bIgnorePress=false; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + +#if defined _XBOX_ONE + if(app.GetTMSDLCInfoRead()) +#endif + { + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { + m_bIgnorePress=false; +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see the store + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,this, app.GetStringTable()); +#endif + } + else + { + ProfileManager.SetLockedProfile(iPad); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DLCMainMenu); + } + } +#if defined _XBOX_ONE + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_DLCMain); + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files +// for(int i=0;iRecordUpsellPresented(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + ProfileManager.DisplayFullVersionPurchase(false,iPad,eSen_UpsellID_Full_Version_Of_Game); +#endif + } + } +} + +void UIScene_MainMenu::tick() +{ + UIScene::tick(); + +#if defined(__PS3__) || defined (__ORBIS__) || defined(__PSVITA__) + if(m_bLaunchFullVersionPurchase) + { + int iCommerceState=app.GetCommerceState(); + // 4J-PB - if there's a commerce error - store down, player can't access store - let the DisplayFullVersionPurchase show the error + if((iCommerceState==CConsoleMinecraftApp::eCommerce_State_Online) || (iCommerceState==CConsoleMinecraftApp::eCommerce_State_Error)) + { + m_bLaunchFullVersionPurchase=false; + m_bIgnorePress=false; + updateTooltips(); + + // 4J-PB - need to check this user can access the store + bool bContentRestricted=false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,this, app.GetStringTable()); + } + else + { + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + ProfileManager.DisplayFullVersionPurchase(false,ProfileManager.GetPrimaryPad(),eSen_UpsellID_Full_Version_Of_Game); + } + } + } + + // 4J-PB - check for a trial version changing to a full version + if(m_bTrialVersion) + { + if(ProfileManager.IsFullVersion()) + { + m_bTrialVersion=false; + m_buttons[(int)eControl_UnlockOrDLC].init(app.GetString(IDS_DOWNLOADABLECONTENT),eControl_UnlockOrDLC); + } + } +#endif + +#if defined _XBOX_ONE + if(m_bWaitingForDLCInfo) + { + if(app.GetTMSDLCInfoRead()) + { + m_bWaitingForDLCInfo=false; + ProfileManager.SetLockedProfile(m_iPad); + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DLCMainMenu); + } + } + + if(g_NetworkManager.ShouldMessageForFullSession()) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_IN_PARTY_SESSION_FULL, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } +#endif + +#ifdef __ORBIS__ + + // process the error dialog (for a patch being available) + // SQRNetworkManager_Orbis::tickErrorDialog also runs the error dialog, so wrap this so this doesn't terminate a signin dialog + if(m_bErrorDialogRunning) + { + SceErrorDialogStatus stat = sceErrorDialogUpdateStatus(); + if( stat == SCE_ERROR_DIALOG_STATUS_FINISHED ) + { + sceErrorDialogTerminate(); + // if m_bRunGameChosen is true, we're here after selecting play game, and we should let the user continue with an offline game + if(m_bRunGameChosen) + { + m_bRunGameChosen=false; + m_eAction = eAction_RunGame; + + // give the option of continuing offline + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION_PATCH_AVAILABLE, uiIDA, 1, ProfileManager.GetPrimaryPad(), &UIScene_MainMenu::PlayOfflineReturned, this, app.GetStringTable()); + + } + m_bErrorDialogRunning=false; + } + } + + if(m_bLoadTrialOnNetworkManagerReady && g_NetworkManager.IsReadyToPlayOrIdle()) + { + m_bLoadTrialOnNetworkManagerReady = false; + LoadTrial(); + } + +#endif +} + +void UIScene_MainMenu::RunAchievements(int iPad) +{ +#if TO_BE_IMPLEMENTED + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // guests can't look at achievements + if(ProfileManager.IsGuest(iPad)) + { + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + XShowAchievementsUI( iPad ); + } +#endif +} + +void UIScene_MainMenu::RunHelpAndOptions(int iPad) +{ + if(ProfileManager.IsGuest(iPad)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + +#if TO_BE_IMPLEMENTED + // 4J-PB - You can be offline and still can go into help and options + if(app.GetTMSDLCInfoRead() || !ProfileManager.IsSignedInLive(iPad)) +#endif + { + ProfileManager.SetLockedProfile(iPad); +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(iPad,eUIScene_HelpAndOptionsMenu); + } +#if TO_BE_IMPLEMENTED + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions); + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;iseed = 0; + param->saveData = NULL; + param->settings = app.GetGameHostOption( eGameHostOption_Tutorial ) | app.GetGameHostOption(eGameHostOption_DisableSaving); + + vector *generators = app.getLevelGenerators(); + param->levelGen = generators->at(0); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = ProfileManager.GetPrimaryPad(); + loadingParams->completionData = completionData; + + ui.ShowTrialTimer(true); + +#ifdef _XBOX_ONE + ui.ShowPlayerDisplayname(true); +#endif + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +void UIScene_MainMenu::handleUnlockFullVersion() +{ + m_buttons[(int)eControl_UnlockOrDLC].setLabel(app.GetString(IDS_DOWNLOADABLECONTENT),true); +} + + +#ifdef __PSVITA__ +int UIScene_MainMenu::SelectNetworkModeReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_MainMenu* pClass = (UIScene_MainMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + app.DebugPrintf("Setting network mode to PSN\n"); + app.SetGameSettings(0, eGameSetting_PSVita_NetworkModeAdhoc, 0); + } + else if(result==C4JStorage::EMessage_ResultDecline) + { + app.DebugPrintf("Setting network mode to Adhoc\n"); + app.SetGameSettings(0, eGameSetting_PSVita_NetworkModeAdhoc, 1); + } + pClass->updateTooltips(); + return 0; +} +#endif //__PSVITA__ diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.h b/Minecraft.Client/Common/UI/UIScene_MainMenu.h new file mode 100644 index 0000000..f1b358d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.h @@ -0,0 +1,175 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_MainMenu : public UIScene +{ +private: + enum EControls + { + eControl_PlayGame, + eControl_Leaderboards, + eControl_Achievements, + eControl_HelpAndOptions, + eControl_UnlockOrDLC, +#ifndef _DURANGO + eControl_Exit, +#else + eControl_XboxHelp, +#endif + eControl_Count, + }; + +// #ifdef __ORBIS__ +// enum EPatchCheck +// { +// ePatchCheck_Idle, +// ePatchCheck_Init, +// ePatchCheck_Running, +// }; +// #endif + + UIControl_Button m_buttons[eControl_Count]; + UIControl m_controlTimer; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttons[(int)eControl_PlayGame], "Button1") + UI_MAP_ELEMENT( m_buttons[(int)eControl_Leaderboards], "Button2") + UI_MAP_ELEMENT( m_buttons[(int)eControl_Achievements], "Button3") + UI_MAP_ELEMENT( m_buttons[(int)eControl_HelpAndOptions], "Button4") + UI_MAP_ELEMENT( m_buttons[(int)eControl_UnlockOrDLC], "Button5") +#ifndef _DURANGO + UI_MAP_ELEMENT( m_buttons[(int)eControl_Exit], "Button6") +#else + UI_MAP_ELEMENT( m_buttons[(int)eControl_XboxHelp], "Button6") +#endif + UI_MAP_ELEMENT( m_controlTimer, "Timer") + UI_END_MAP_ELEMENTS_AND_NAMES() + + static Random *random; + bool m_bIgnorePress; + bool m_bTrialVersion; + bool m_bLoadTrialOnNetworkManagerReady; +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + bool m_bLaunchFullVersionPurchase; +#endif + +#ifdef _XBOX_ONE + bool m_bWaitingForDLCInfo; +#endif + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + vector m_splashes; + wstring m_splash; + enum eSplashIndexes + { + eSplashHappyBirthdayEx = 0, + eSplashHappyBirthdayNotch, + eSplashMerryXmas, + eSplashHappyNewYear, + + // The start index in the splashes vector from which we can select a random splash + eSplashRandomStart, + }; + + enum eActions + { + eAction_None=0, + eAction_RunGame, + eAction_RunLeaderboards, + eAction_RunAchievements, + eAction_RunHelpAndOptions, + eAction_RunUnlockOrDLC, +#if defined(__PS3__)|| defined(__PSVITA__) || defined(__ORBIS__) + eAction_RunLeaderboardsPSN, + eAction_RunGamePSN, + eAction_RunUnlockOrDLCPSN, +#elif defined _DURANGO + eAction_RunXboxHelp, +#endif + + }; + eActions m_eAction; +public: + UIScene_MainMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_MainMenu(); + + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_MainMenu;} + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); +protected: + void customDrawSplash(IggyCustomDrawCallbackRegion *region); + + + virtual wstring getMoviePath(); + +public: + virtual void tick(); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleUnlockFullVersion(); + +protected: + void handlePress(F64 controlId, F64 childId); + + void handleGainFocus(bool navBack); + + virtual long long getDefaultGtcButtons() { return 0; } + +private: + void RunPlayGame(int iPad); + void RunLeaderboards(int iPad); + void RunUnlockOrDLC(int iPad); + void RunAchievements(int iPad); + void RunHelpAndOptions(int iPad); + + void RunAction(int iPad); + + static void LoadTrial(); + +#ifdef _XBOX_ONE + static int ChooseUser_SignInReturned(void *pParam,bool bContinue, int iPad); +#endif + static int CreateLoad_SignInReturned(void *pParam,bool bContinue, int iPad); + static int HelpAndOptions_SignInReturned(void *pParam,bool bContinue,int iPad); + static int Achievements_SignInReturned(void *pParam,bool bContinue,int iPad); + static int MustSignInReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + static int Leaderboards_SignInReturned(void* pParam, bool bContinue, int iPad); + static int UnlockFullGame_SignInReturned(void *pParam,bool bContinue,int iPad); + static int ExitGameReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + +#ifdef __ORBIS__ + static void RefreshChatAndContentRestrictionsReturned_PlayGame(void *pParam); + static void RefreshChatAndContentRestrictionsReturned_Leaderboards(void *pParam); + + static int PlayOfflineReturned(void *pParam, int iPad, C4JStorage::EMessageResult result); +#endif + +#ifdef _DURANGO + static int XboxHelp_SignInReturned(void *pParam, bool bContinue, int iPad); +#endif + +#ifdef __PSVITA__ + static int SelectNetworkModeReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +#ifdef __ORBIS__ + //EPatchCheck m_ePatchCheckState; + bool m_bRunGameChosen; + int32_t m_errorCode; + bool m_bErrorDialogRunning; +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp b/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp new file mode 100644 index 0000000..6b8dc55 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp @@ -0,0 +1,161 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_MessageBox.h" + +UIScene_MessageBox::UIScene_MessageBox(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + MessageBoxInfo *param = (MessageBoxInfo *)initData; + + m_buttonCount = param->uiOptionC; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = param->uiOptionC; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = param->dwFocusButton; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcInit , 2 , value ); + + int buttonIndex = 0; + if(param->uiOptionC > 3) + { + m_buttonButtons[eControl_Button0].init(app.GetString(param->uiOptionA[buttonIndex]),buttonIndex); + ++buttonIndex; + } + if(param->uiOptionC > 2) + { + m_buttonButtons[eControl_Button1].init(app.GetString(param->uiOptionA[buttonIndex]),buttonIndex); + ++buttonIndex; + } + if(param->uiOptionC > 1) + { + m_buttonButtons[eControl_Button2].init(app.GetString(param->uiOptionA[buttonIndex]),buttonIndex); + ++buttonIndex; + } + m_buttonButtons[eControl_Button3].init(app.GetString(param->uiOptionA[buttonIndex]),buttonIndex); + + m_labelTitle.init(app.GetString(param->uiTitle)); + m_labelContent.init(app.GetString(param->uiText)); + + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAutoResize , 0 , NULL ); + + m_Func = param->Func; + m_lpParam = param->lpParam; + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); + + // 4J-TomK - rebuild touch after auto resize +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} + +UIScene_MessageBox::~UIScene_MessageBox() +{ + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +wstring UIScene_MessageBox::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1 && !m_parentLayer->IsFullscreenGroup()) + { + return L"MessageBoxSplit"; + } + else + { + return L"MessageBox"; + } +} + +void UIScene_MessageBox::updateTooltips() +{ + ui.SetTooltips( m_parentLayer->IsFullscreenGroup()?XUSER_INDEX_ANY:m_iPad, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_CANCEL); +} + +void UIScene_MessageBox::handleReload() +{ + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = m_buttonCount; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (F64)getControlFocus(); + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcInit , 2 , value ); + + out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAutoResize , 0 , NULL ); +} + +void UIScene_MessageBox::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + if(m_Func) m_Func(m_lpParam, iPad, C4JStorage::EMessage_Cancelled); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } + handled = true; +} + +void UIScene_MessageBox::handlePress(F64 controlId, F64 childId) +{ + C4JStorage::EMessageResult result = C4JStorage::EMessage_Cancelled; + switch((int)controlId) + { + case 0: + result = C4JStorage::EMessage_ResultAccept; + break; + case 1: + result = C4JStorage::EMessage_ResultDecline; + break; + case 2: + result = C4JStorage::EMessage_ResultThirdOption; + break; + case 3: + result = C4JStorage::EMessage_ResultFourthOption; + break; + } + + navigateBack(); + if(m_Func) m_Func(m_lpParam, m_iPad, result); +} + +bool UIScene_MessageBox::hasFocus(int iPad) +{ + // 4J-JEV: Fix for PS4 #5204 - [TRC][R4033] The application can be locked up by second user logging out of the system. + if (m_iPad == 255) + { + // Message box is for everyone + return bHasFocus; + } + else if (ProfileManager.IsSignedIn(m_iPad)) + { + // Owner is still present + return bHasFocus && (iPad == m_iPad); + } + else + { + // Original owner has left so let everyone interact + return bHasFocus; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_MessageBox.h b/Minecraft.Client/Common/UI/UIScene_MessageBox.h new file mode 100644 index 0000000..3c349de --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_MessageBox.h @@ -0,0 +1,59 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_MessageBox : public UIScene +{ +private: + enum EControls + { + eControl_Button0, + eControl_Button1, + eControl_Button2, + eControl_Button3, + + eControl_COUNT + }; + + int( *m_Func)(LPVOID,int,const C4JStorage::EMessageResult); + LPVOID m_lpParam; + int m_buttonCount; + + UIControl_Button m_buttonButtons[eControl_COUNT]; + UIControl_Label m_labelTitle, m_labelContent; + IggyName m_funcInit, m_funcAutoResize; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonButtons[eControl_Button0], "Button0") + UI_MAP_ELEMENT( m_buttonButtons[eControl_Button1], "Button1") + UI_MAP_ELEMENT( m_buttonButtons[eControl_Button2], "Button2") + UI_MAP_ELEMENT( m_buttonButtons[eControl_Button3], "Button3") + + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_MAP_ELEMENT( m_labelContent, "Content") + + UI_MAP_NAME( m_funcInit, L"Init") + UI_MAP_NAME( m_funcAutoResize, L"AutoResize") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_MessageBox(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_MessageBox(); + + virtual EUIScene getSceneType() { return eUIScene_MessageBox;} + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return false; } + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + + virtual void updateTooltips(); + +public: + virtual void handleReload(); + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual bool hasFocus(int iPad); + +protected: + void handlePress(F64 controlId, F64 childId); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_PauseMenu.cpp b/Minecraft.Client/Common/UI/UIScene_PauseMenu.cpp new file mode 100644 index 0000000..d9569cc --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_PauseMenu.cpp @@ -0,0 +1,1483 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_PauseMenu.h" +#include "..\..\MinecraftServer.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\DLCTexturePack.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#ifdef __ORBIS__ +#include +#endif + +#ifdef _DURANGO +#include "..\..\Durango\Leaderboards\DurangoStatsDebugger.h" +#endif + +#ifdef __PSVITA__ +#include "PSVita\Network\SonyCommerce_Vita.h" +#endif + +#if defined __PS3__ || defined __ORBIS__ +#define USE_SONY_REMOTE_STORAGE +#endif + +UIScene_PauseMenu::UIScene_PauseMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + m_bIgnoreInput=false; + m_eAction=eAction_None; + + m_buttons[BUTTON_PAUSE_RESUMEGAME].init(app.GetString(IDS_RESUME_GAME),BUTTON_PAUSE_RESUMEGAME); + m_buttons[BUTTON_PAUSE_HELPANDOPTIONS].init(app.GetString(IDS_HELP_AND_OPTIONS),BUTTON_PAUSE_HELPANDOPTIONS); + m_buttons[BUTTON_PAUSE_LEADERBOARDS].init(app.GetString(IDS_LEADERBOARDS),BUTTON_PAUSE_LEADERBOARDS); +#ifdef _DURANGO + m_buttons[BUTTON_PAUSE_XBOXHELP].init(app.GetString(IDS_XBOX_HELP_APP), BUTTON_PAUSE_XBOXHELP); +#else + m_buttons[BUTTON_PAUSE_ACHIEVEMENTS].init(app.GetString(IDS_ACHIEVEMENTS),BUTTON_PAUSE_ACHIEVEMENTS); +#endif +#if defined(_XBOX_ONE) || defined(__ORBIS__) + m_bTrialTexturePack = false; + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + m_pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + + if(!m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + m_bTrialTexturePack = true; + } + } + + // 4J-TomK - check for all possible labels being fed into BUTTON_PAUSE_SAVEGAME (Bug 163775) + // this has to be done before button initialisation! + wchar_t saveButtonLabels[2][256]; + swprintf( saveButtonLabels[0], 256, L"%ls", app.GetString( IDS_SAVE_GAME )); + swprintf( saveButtonLabels[1], 256, L"%ls", app.GetString( IDS_DISABLE_AUTOSAVE )); + m_buttons[BUTTON_PAUSE_SAVEGAME].setAllPossibleLabels(2,saveButtonLabels); + + if(app.GetGameHostOption(eGameHostOption_DisableSaving) || m_bTrialTexturePack) + { + m_savesDisabled = true; + m_buttons[BUTTON_PAUSE_SAVEGAME].init(app.GetString(IDS_SAVE_GAME),BUTTON_PAUSE_SAVEGAME); + } + else + { + m_savesDisabled = false; + m_buttons[BUTTON_PAUSE_SAVEGAME].init(app.GetString(IDS_DISABLE_AUTOSAVE),BUTTON_PAUSE_SAVEGAME); + } +#else + m_buttons[BUTTON_PAUSE_SAVEGAME].init(app.GetString(IDS_SAVE_GAME),BUTTON_PAUSE_SAVEGAME); +#endif + m_buttons[BUTTON_PAUSE_EXITGAME].init(app.GetString(IDS_EXIT_GAME),BUTTON_PAUSE_EXITGAME); + + if(!ProfileManager.IsFullVersion()) + { + // hide the trial timer + ui.ShowTrialTimer(false); + } + + updateControlsVisibility(); + + doHorizontalResizeCheck(); + + // get rid of the quadrant display if it's on + ui.HidePressStart(); + +#if TO_BE_IMPLEMENTED + XuiSetTimer(m_hObj,IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); +#endif + + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); + } + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_PauseMenu, 0); + TelemetryManager->RecordPauseOrInactive(m_iPad); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft != NULL && pMinecraft->localgameModes[iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[iPad]; + + // This just allows it to be shown + gameMode->getTutorial()->showTutorialPopup(false); + } + m_bErrorDialogRunning = false; +} + +UIScene_PauseMenu::~UIScene_PauseMenu() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft != NULL && pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + + // This just allows it to be shown + gameMode->getTutorial()->showTutorialPopup(true); + } + + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + m_parentLayer->showComponent(m_iPad,eUIComponent_MenuBackground,false); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +} + +wstring UIScene_PauseMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"PauseMenuSplit"; + } + else + { + return L"PauseMenu"; + } +} + +void UIScene_PauseMenu::tick() +{ + UIScene::tick(); + +#ifdef __PSVITA__ + // 4J-MGH - Need to check for installed DLC here, as we delay the installation of the key file on Vita + if(!app.DLCInstallProcessCompleted()) app.StartInstallDLCProcess(0); +#endif + + +#if defined _XBOX_ONE || defined __ORBIS__ + if(!m_bTrialTexturePack && m_savesDisabled != (app.GetGameHostOption(eGameHostOption_DisableSaving) != 0) && ProfileManager.GetPrimaryPad() == m_iPad ) + { + // We show the save button if saves are disabled as this lets us show a prompt to enable them (via purchasing a texture pack) + if( app.GetGameHostOption(eGameHostOption_DisableSaving) ) + { + m_savesDisabled = true; + m_buttons[BUTTON_PAUSE_SAVEGAME].setLabel( app.GetString(IDS_SAVE_GAME) ); + } + else + { + m_savesDisabled = false; + m_buttons[BUTTON_PAUSE_SAVEGAME].setLabel( app.GetString(IDS_DISABLE_AUTOSAVE) ); + } + } +#endif + +#ifdef __ORBIS__ + // Process the error dialog (for a patch being available) + if(m_bErrorDialogRunning) + { + SceErrorDialogStatus stat = sceErrorDialogUpdateStatus(); + if( stat == SCE_ERROR_DIALOG_STATUS_FINISHED ) + { + sceErrorDialogTerminate(); + m_bErrorDialogRunning=false; + } + } +#endif +} + +void UIScene_PauseMenu::updateTooltips() +{ + bool bUserisClientSide = ProfileManager.IsSignedInLive(m_iPad); + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + +#ifdef _XBOX_ONE + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(m_iPad); +#endif + + int iY = -1; +#if defined __PS3__ || defined __ORBIS__ + if(m_iPad == ProfileManager.GetPrimaryPad() ) iY = IDS_TOOLTIPS_GAME_INVITES; +#endif + int iRB = -1; + int iX = -1; + + if(ProfileManager.IsFullVersion()) + { + if(StorageManager.GetSaveDisabled()) + { + iX = bIsisPrimaryHost?IDS_TOOLTIPS_SELECTDEVICE:-1; +#ifdef _XBOX_ONE + iRB = bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1; +#endif + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide ) + { +#ifndef __PS3__ + iY = IDS_TOOLTIPS_SHARE; +#endif + } + } + else + { + iX = bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1; +#ifdef _XBOX_ONE + iRB = bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1; +#endif + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { +#ifndef __PS3__ + iY = IDS_TOOLTIPS_SHARE; +#endif + } + } + } + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,iX,iY, -1,-1,-1,iRB); +} + +void UIScene_PauseMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + m_parentLayer->showComponent(m_iPad,eUIComponent_MenuBackground,true); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +} + +void UIScene_PauseMenu::handlePreReload() +{ +#if defined _XBOX_ONE || defined __ORBIS__ + if(ProfileManager.GetPrimaryPad() == m_iPad) + { + // 4J-TomK - check for all possible labels being fed into BUTTON_PAUSE_SAVEGAME (Bug 163775) + // this has to be done before button initialisation! + wchar_t saveButtonLabels[2][256]; + swprintf( saveButtonLabels[0], 256, L"%ls", app.GetString( IDS_SAVE_GAME )); + swprintf( saveButtonLabels[1], 256, L"%ls", app.GetString( IDS_DISABLE_AUTOSAVE )); + m_buttons[BUTTON_PAUSE_SAVEGAME].setAllPossibleLabels(2,saveButtonLabels); + } +#endif +} + +void UIScene_PauseMenu::handleReload() +{ + updateTooltips(); + updateControlsVisibility(); + +#if defined _XBOX_ONE || defined __ORBIS__ + if(ProfileManager.GetPrimaryPad() == m_iPad) + { + // We show the save button if saves are disabled as this lets us show a prompt to enable them (via purchasing a texture pack) + if( app.GetGameHostOption(eGameHostOption_DisableSaving) || m_bTrialTexturePack ) + { + m_savesDisabled = true; + m_buttons[BUTTON_PAUSE_SAVEGAME].setLabel( app.GetString(IDS_SAVE_GAME) ); + } + else + { + m_savesDisabled = false; + m_buttons[BUTTON_PAUSE_SAVEGAME].setLabel( app.GetString(IDS_DISABLE_AUTOSAVE) ); + } + } +#endif + + doHorizontalResizeCheck(); +} + +void UIScene_PauseMenu::updateControlsVisibility() +{ + // are we the primary player? + // 4J-PB - fix for 7844 & 7845 - + // TCR # 128: XLA Pause Menu: When in a multiplayer game as a client the Pause Menu does not have a Leaderboards option. + // TCR # 128: XLA Pause Menu: When in a multiplayer game as a client the Pause Menu does not have an Achievements option. + if(ProfileManager.GetPrimaryPad()==m_iPad) // && g_NetworkManager.IsHost()) + { + // are we in splitscreen? + // how many local players do we have? + if( app.GetLocalPlayerCount()>1 ) + { + // Hide the BUTTON_PAUSE_LEADERBOARDS and BUTTON_PAUSE_ACHIEVEMENTS + removeControl( &m_buttons[BUTTON_PAUSE_LEADERBOARDS], false ); +#ifndef _XBOX_ONE + removeControl( &m_buttons[BUTTON_PAUSE_ACHIEVEMENTS], false ); +#endif + } +#ifdef __PSVITA__ + // MGH added - remove leaderboards in adhoc + if(CGameNetworkManager::usingAdhocMode()) + { + removeControl( &m_buttons[BUTTON_PAUSE_LEADERBOARDS], false ); + } +#endif + + if( !g_NetworkManager.IsHost() ) + { + // Hide the BUTTON_PAUSE_SAVEGAME + removeControl( &m_buttons[BUTTON_PAUSE_SAVEGAME], false ); + } + } + else + { + // Hide the BUTTON_PAUSE_LEADERBOARDS, BUTTON_PAUSE_ACHIEVEMENTS and BUTTON_PAUSE_SAVEGAME + removeControl( &m_buttons[BUTTON_PAUSE_LEADERBOARDS], false ); +#ifndef _XBOX_ONE + removeControl( &m_buttons[BUTTON_PAUSE_ACHIEVEMENTS], false ); +#endif + removeControl( &m_buttons[BUTTON_PAUSE_SAVEGAME], false ); + } + + // is saving disabled? + if(StorageManager.GetSaveDisabled()) + { +#ifdef _XBOX + // disable save button + m_buttons[BUTTON_PAUSE_SAVEGAME].setEnable(false); +#endif + } + +#if defined(__PS3__) || defined (__PSVITA__) || defined(__ORBIS__) + // We don't have a way to display trophies/achievements, so remove the button, and we're allowed to not have it on Xbox One + removeControl( &m_buttons[BUTTON_PAUSE_ACHIEVEMENTS], false ); +#endif + +} + +void UIScene_PauseMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) + { + return; + } + + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + +#ifdef _XBOX_ONE + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==iPad); + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(iPad); +#endif + + switch(key) + { +#ifdef _DURANGO + case ACTION_MENU_GTC_RESUME: +#endif +#if defined(__PS3__) // not for Orbis - we want to use the pause menu (touchpad press) to select a menu item + case ACTION_MENU_PAUSEMENU: +#endif + case ACTION_MENU_CANCEL: + if(pressed) + { +#ifdef _DURANGO + //DurangoStatsDebugger::PrintStats(iPad); +#endif + + if( iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + + ui.PlayUISFX(eSFX_Back); + navigateBack(); + if(!ProfileManager.IsFullVersion()) + { + ui.ShowTrialTimer(true); + } + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + +#if TO_BE_IMPLEMENTED + case VK_PAD_X: + // Change device + if(bIsisPrimaryHost) + { + // we need a function to deal with the return from this - if it changes, we need to update the pause menu and tooltips + // Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage + // device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes. + m_bIgnoreInput=true; + + StorageManager.SetSaveDevice(&UIScene_PauseMenu::DeviceSelectReturned,this,true); + } + rfHandled = TRUE; + break; +#endif + + case ACTION_MENU_Y: + { + +#if defined(__PS3__) || defined(__ORBIS__) + if(pressed && iPad == ProfileManager.GetPrimaryPad()) + { +#ifdef __ORBIS__ + // If a patch is available, can't view invites + if (CheckForPatch()) break; +#endif + + // Are we offline? + if(!ProfileManager.IsSignedInLive(iPad)) + { + m_eAction=eAction_ViewInvitesPSN; +#ifdef __ORBIS__ + int npAvailability = ProfileManager.getNPAvailability(iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive is the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPad, &UIScene_PauseMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + } +#else // __PS3__ + // get them to sign in to online + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPad, &UIScene_PauseMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); +#endif + } + else + { +#ifdef __ORBIS__ + SQRNetworkManager_Orbis::RecvInviteGUI(); +#else // __PS3__ + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#endif + } + } +#else +#if TO_BE_IMPLEMENTED + if(bUserisClientSide) + { + // 4J Stu - Added check in 1.8.2 bug fix (TU6) to stop repeat key presses + bool bCanScreenshot = true; + for(int j=0; j < XUSER_MAX_COUNT;++j) + { + if(app.GetXuiAction(j) == eAppAction_SocialPostScreenshot) + { + bCanScreenshot = false; + break; + } + } + if(bCanScreenshot) app.SetAction(pInputData->UserIndex,eAppAction_SocialPost); + } + rfHandled = TRUE; +#endif +#endif // __PS3__ + } + break; +#ifdef _XBOX_ONE + case ACTION_MENU_RIGHT_SCROLL: + if( bDisplayBanTip ) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ACTION_BAN_LEVEL_TITLE, IDS_ACTION_BAN_LEVEL_DESCRIPTION, uiIDA, 2, iPad,&UIScene_PauseMenu::BanGameDialogReturned,this, app.GetStringTable(), NULL, 0, false); + + //rfHandled = TRUE; + } + break; +#endif + } +} + +void UIScene_PauseMenu::handlePress(F64 controlId, F64 childId) +{ + if(m_bIgnoreInput) return; + + switch((int)controlId) + { + case BUTTON_PAUSE_RESUMEGAME: + if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + navigateBack(); + break; + case BUTTON_PAUSE_LEADERBOARDS: + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + //4J Gordon: Being used for the leaderboards proper now + // guests can't look at leaderboards + if(ProfileManager.IsGuest(m_iPad)) + { + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(), NULL, 0, false); + } + else if(!ProfileManager.IsSignedInLive(m_iPad)) + { +#ifdef __ORBIS__ + // If a patch is available, can't show leaderboard + if (CheckForPatch()) break; + + // Check for content restricted user + // Update error code + int errorCode = ProfileManager.getNPAvailability(m_iPad); + + // Check if PSN is unavailable because of age restriction + if (errorCode == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, m_iPad, NULL, NULL, app.GetStringTable()); + + break;; + } + +#endif + +#if defined __PS3__ || __PSVITA__ + // get them to sign in to online + m_eAction=eAction_ViewLeaderboardsPSN; + UINT uiIDA[1]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::MustSignInReturnedPSN,this, app.GetStringTable(), NULL, 0, false); +#elif defined(__ORBIS__) + m_eAction=eAction_ViewLeaderboardsPSN; + int npAvailability = ProfileManager.getNPAvailability(m_iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive is the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, m_iPad, NULL, NULL, app.GetStringTable()); + } + else + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(m_iPad)) + { + // Signed in to PSN but not connected (no internet access) + + // Id + assert(!ProfileManager.isConnectedToPSN(m_iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, m_iPad, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, m_iPad, &UIScene_PauseMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + } +#else + UINT uiIDA[1] = { IDS_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, m_iPad); +#endif + } + else + { + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(m_iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see leaderboards + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, m_iPad,NULL,this, app.GetStringTable(), NULL, 0, false); +#endif + } + else + { + ui.NavigateToScene(m_iPad, eUIScene_LeaderboardsMenu); + } + } + } + break; +#ifdef _DURANGO + case BUTTON_PAUSE_XBOXHELP: + { + // 4J: Launch the crummy xbox help application. + WXS::User^ user = ProfileManager.GetUser(m_iPad); + Windows::Xbox::ApplicationModel::Help::Show(user); + } + break; +#elif TO_BE_IMPLEMENTED + case BUTTON_PAUSE_ACHIEVEMENTS: + + // guests can't look at achievements + if(ProfileManager.IsGuest(pNotifyPressData->UserIndex)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(), NULL, 0, false); + } + else + { + XShowAchievementsUI( pNotifyPressData->UserIndex ); + } + break; +#endif + + case BUTTON_PAUSE_HELPANDOPTIONS: + ui.NavigateToScene(m_iPad,eUIScene_HelpAndOptionsMenu); + break; + case BUTTON_PAUSE_SAVEGAME: + PerformActionSaveGame(); + break; + case BUTTON_PAUSE_EXITGAME: + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + // Check if it's the trial version + if(ProfileManager.IsFullVersion()) + { + UINT uiIDA[3]; + + // is it the primary player exiting? + if(m_iPad==ProfileManager.GetPrimaryPad()) + { + int playTime = -1; + if( pMinecraft->localplayers[m_iPad] != NULL ) + { + playTime = (int)pMinecraft->localplayers[m_iPad]->getSessionTimer(); + } + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + if(g_NetworkManager.IsHost() && StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, m_iPad,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, m_iPad,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + } + else if(g_NetworkManager.IsHost() && g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned, dynamic_cast(this), app.GetStringTable(), NULL, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned, dynamic_cast(this), app.GetStringTable(), NULL, 0, false); + } +#else + if(StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else + { + if( g_NetworkManager.IsHost() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, m_iPad,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, m_iPad,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + } +#endif + } + else + { + int playTime = -1; + if( pMinecraft->localplayers[m_iPad] != NULL ) + { + playTime = (int)pMinecraft->localplayers[m_iPad]->getSessionTimer(); + } + + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Exited); + + + // just exit the player + app.SetAction(m_iPad,eAppAction_ExitPlayer); + } + } + else + { + // is it the primary player exiting? + if(m_iPad==ProfileManager.GetPrimaryPad()) + { + int playTime = -1; + if( pMinecraft->localplayers[m_iPad] != NULL ) + { + playTime = (int)pMinecraft->localplayers[m_iPad]->getSessionTimer(); + } + + // adjust the trial time played + ui.ReduceTrialTimerValue(); + + // exit the level + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::ExitGameDialogReturned, dynamic_cast(this), app.GetStringTable(), NULL, 0, false); + + } + else + { + int playTime = -1; + if( pMinecraft->localplayers[m_iPad] != NULL ) + { + playTime = (int)pMinecraft->localplayers[m_iPad]->getSessionTimer(); + } + + TelemetryManager->RecordLevelExit(m_iPad, eSen_LevelExitStatus_Exited); + + // just exit the player + app.SetAction(m_iPad,eAppAction_ExitPlayer); + } + } + } + break; + } +} + +void UIScene_PauseMenu::PerformActionSaveGame() +{ + // is the player trying to save in the trial version? + if(!ProfileManager.IsFullVersion()) + { +#ifdef __ORBIS__ + // If a patch is available, can't buy full game + if (CheckForPatch()) return; +#endif + + // Unlock the full version? + if(!ProfileManager.IsSignedInLive(m_iPad)) + { +#if defined(__PS3__) + m_eAction=eAction_SaveGamePSN; + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::MustSignInReturnedPSN,this, app.GetStringTable(), NULL, 0, false); +#elif defined(__ORBIS__) + m_eAction=eAction_SaveGamePSN; + int npAvailability = ProfileManager.getNPAvailability(m_iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive is the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, m_iPad, NULL, NULL, app.GetStringTable()); + } + else + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(m_iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(m_iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, m_iPad, NULL, NULL, app.GetStringTable()); + } + else + { + // Not signed in to PSN + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, m_iPad, &UIScene_PauseMenu::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + } +#endif + } + else + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + ui.RequestMessageBox(IDS_UNLOCK_TITLE, IDS_UNLOCK_TOSAVE_TEXT, uiIDA, 2,m_iPad,&UIScene_PauseMenu::UnlockFullSaveReturned,this,app.GetStringTable(), NULL, 0, false); + } + + return; + } + + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + m_pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + + if(!m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // upsell +#ifdef _XBOX + ULONGLONG ullOfferID_Full; + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the texture pack + TelemetryManager->RecordUpsellPresented(m_iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack +#ifdef __PSVITA__ + if(app.DLCInstallProcessCompleted() && !SonyCommerce_Vita::getDLCUpgradePending()) // MGH - devtrack #5861 On vita it can take a bit after the install has finished to register the purchase, so make sure we don't end up asking to purchase again +#endif + { + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, m_iPad,&UIScene_PauseMenu::WarningTrialTexturePackReturned,this,app.GetStringTable(), NULL, 0, false); + } + + return; + } + else + { + m_bTrialTexturePack = false; + } + } + + // does the save exist? + bool bSaveExists; + C4JStorage::ESaveGameState result=StorageManager.DoesSaveExist(&bSaveExists); + +#ifdef _XBOX + if(result == C4JStorage::ELoadGame_DeviceRemoved) + { + // this will be a tester trying to be clever + UINT uiIDA[2]; + uiIDA[0]=IDS_SELECTANEWDEVICE; + uiIDA[1]=IDS_NODEVICE_DECLINE; + + ui.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::DeviceRemovedDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + if(!m_savesDisabled) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_DISABLE_AUTOSAVE, IDS_CONFIRM_DISABLE_AUTOSAVE, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::DisableAutosaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else +#endif + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::SaveGameDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + else + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_ENABLE_AUTOSAVE, IDS_CONFIRM_ENABLE_AUTOSAVE, uiIDA, 2, m_iPad,&IUIScene_PauseMenu::EnableAutosaveDialogReturned,this, app.GetStringTable(), NULL, 0, false); +#else + // flag a app action of save game + app.SetAction(m_iPad,eAppAction_SaveGame); +#endif + } + } +} + +void UIScene_PauseMenu::ShowScene(bool show) +{ + app.DebugPrintf("UIScene_PauseMenu::ShowScene is not implemented\n"); +} + +void UIScene_PauseMenu::HandleDLCInstalled() +{ + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + //m_bIgnoreInput=false; + app.DebugPrintf("UIScene_PauseMenu::HandleDLCInstalled - m_bIgnoreInput false\n"); + } + else + { + // 4J-PB - Somehow, on th edisc build, we get in here, but don't call HandleDLCMountingComplete, so input locks up + //m_bIgnoreInput=true; + app.DebugPrintf("UIScene_PauseMenu::HandleDLCInstalled - m_bIgnoreInput true\n"); + } + // this will send a CustomMessage_DLCMountingComplete when done +} + + +void UIScene_PauseMenu::HandleDLCMountingComplete() +{ + // check if we should display the save option + + //m_bIgnoreInput=false; + app.DebugPrintf("UIScene_PauseMenu::HandleDLCMountingComplete - m_bIgnoreInput false \n"); + + // if(ProfileManager.IsFullVersion()) + // { + // bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + // + // if(bIsisPrimaryHost) + // { + // m_buttons[BUTTON_PAUSE_SAVEGAME].setEnable(true); + // } + // } +} + +int UIScene_PauseMenu::UnlockFullSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedInLive(pMinecraft->player->GetXboxPad())) + { + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,pClass, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { + ProfileManager.DisplayFullVersionPurchase(false,pMinecraft->player->GetXboxPad(),eSen_UpsellID_Full_Version_Of_Game); + } + } + } + else + { + //SentientManager.RecordUpsellResponded(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID, eSen_UpsellOutcome_Declined); + } + + return 0; +} + +int UIScene_PauseMenu::SaveGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + + if(bContinue==true) + { + pClass->PerformActionSaveGame(); + } + + return 0; +} + +#ifdef _XBOX_ONE +int UIScene_PauseMenu::BanGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + app.SetAction(iPad,eAppAction_BanLevel); + } + return 0; +} +#endif + +#if defined(__PS3__) || defined (__PSVITA__) || defined(__ORBIS__) +int UIScene_PauseMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { +#ifdef __PS3__ + switch(pClass->m_eAction) + { + case eAction_ViewLeaderboardsPSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_PauseMenu::ViewLeaderboards_SignInReturned, pClass); + break; + case eAction_ViewInvitesPSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_PauseMenu::ViewInvites_SignInReturned, pClass); + break; + case eAction_SaveGamePSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_PauseMenu::SaveGame_SignInReturned, pClass); + break; + case eAction_BuyTexturePackPSN: + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_PauseMenu::BuyTexturePack_SignInReturned, pClass); + break; + } +#elif defined __PSVITA__ + switch(pClass->m_eAction) + { + case eAction_ViewLeaderboardsPSN: + //CD - Must force Ad-Hoc off if they want leaderboard PSN sign-in + //Save settings change + app.SetGameSettings(0, eGameSetting_PSVita_NetworkModeAdhoc, 0); + //Force off + CGameNetworkManager::setAdhocMode(false); + //Now Sign-in + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_PauseMenu::ViewLeaderboards_SignInReturned, pClass); + break; + case eAction_ViewInvitesPSN: + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_PauseMenu::ViewInvites_SignInReturned, pClass); + break; + case eAction_SaveGamePSN: + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_PauseMenu::SaveGame_SignInReturned, pClass); + break; + case eAction_BuyTexturePackPSN: + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_PauseMenu::BuyTexturePack_SignInReturned, pClass); + break; + } +#else + switch(pClass->m_eAction) + { + case eAction_ViewLeaderboardsPSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_PauseMenu::ViewLeaderboards_SignInReturned, pClass, false, iPad); + break; + case eAction_ViewInvitesPSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_PauseMenu::ViewInvites_SignInReturned, pClass, false, iPad); + break; + case eAction_SaveGamePSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_PauseMenu::SaveGame_SignInReturned, pClass, false, iPad); + break; + case eAction_BuyTexturePackPSN: + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_PauseMenu::BuyTexturePack_SignInReturned, pClass, false, iPad); + break; + } +#endif + } + + return 0; +} + +int UIScene_PauseMenu::ViewLeaderboards_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + + if(bContinue==true) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + // guests can't look at leaderboards + if(ProfileManager.IsGuest(pClass->m_iPad)) + { + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable(), NULL, 0, false); + } + else if(ProfileManager.IsSignedInLive(iPad)) + { +#ifndef __ORBIS__ + bool bContentRestricted=false; + ProfileManager.GetChatAndContentRestrictions(pClass->m_iPad,true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + // you can't see leaderboards + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,pClass, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { + ui.NavigateToScene(pClass->m_iPad, eUIScene_LeaderboardsMenu); + } + } + } + + return 0; +} + +int UIScene_PauseMenu::WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + +#ifdef __ORBIS__ + // If a patch is available, can't proceed + if (pClass->CheckForPatch()) return 0; +#endif + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(result==C4JStorage::EMessage_ResultAccept) + { + if(!ProfileManager.IsSignedInLive(iPad)) + { + pClass->m_eAction=eAction_SaveGamePSN; +#ifdef __ORBIS__// Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive is the npAvailability isn't SCE_OK + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + // Determine why they're not "signed in live" + if (ProfileManager.isSignedInPSN(iPad)) + { + // Signed in to PSN but not connected (no internet access) + assert(!ProfileManager.isConnectedToPSN(iPad)); + + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else + { + UINT uiIDA[1]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + ui.RequestMessageBox( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 1, iPad, &UIScene_PauseMenu::MustSignInReturnedPSN, pClass, app.GetStringTable(), NULL, 0, false); + } +#else // __PS3__ + // You're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 2, iPad,&UIScene_PauseMenu::MustSignInReturnedPSN,pClass, app.GetStringTable(), NULL, 0, false); +#endif + } + else + { +#ifndef __ORBIS__ + // 4J-PB - need to check this user can access the store + bool bContentRestricted=false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad,NULL,pClass, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { + // need to get info on the pack to see if the user has already downloaded it + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + // retrieve the store name for the skin pack + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + const char *pchPackName=wstringtofilename(pDLCPack->getName()); + app.DebugPrintf("Texture Pack - %s\n",pchPackName); + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo((char *)pchPackName); + + if(pSONYDLCInfo!=NULL) + { + char chName[42]; + char chKeyName[20]; + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + // find the info on the skin pack + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + // So we assume the first sku for the product is the one we want + + // MGH - keyname in the DLC file is 16 chars long, but there's no space for a NULL terminating char + memset(chKeyName, 0, sizeof(chKeyName)); + strncpy(chKeyName, pSONYDLCInfo->chDLCKeyname, 16); + +#ifdef __ORBIS__ + strcpy(chName, chKeyName); +#else + sprintf(chName,"%s-%s",app.GetCommerceCategory(),chKeyName); +#endif + app.GetDLCSkuIDFromProductList(chName,chSkuID); + + // 4J-PB - need to check for an empty store +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if(app.CheckForEmptyStore(iPad)==false) +#endif + { + if(app.DLCAlreadyPurchased(chSkuID)) + { + app.DownloadAlreadyPurchased(chSkuID); + } + else + { + app.Checkout(chSkuID); + } + } + } + } + } + } +#endif // + + return 0; +} + +int UIScene_PauseMenu::BuyTexturePack_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + + if(bContinue==true) + { + // Check if we're signed in to LIVE + if(ProfileManager.IsSignedInLive(iPad)) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + +#ifndef __ORBIS__ + // 4J-PB - need to check this user can access the store + bool bContentRestricted=false; + ProfileManager.GetChatAndContentRestrictions(iPad,true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad,NULL,pClass, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { + // need to get info on the pack to see if the user has already downloaded it + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + // retrieve the store name for the skin pack + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + const char *pchPackName=wstringtofilename(pDLCPack->getName()); + app.DebugPrintf("Texture Pack - %s\n",pchPackName); + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo((char *)pchPackName); + + if(pSONYDLCInfo!=NULL) + { + char chName[42]; + char chKeyName[20]; + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + // find the info on the skin pack + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + // So we assume the first sku for the product is the one we want + + // MGH - keyname in the DLC file is 16 chars long, but there's no space for a NULL terminating char + memset(chKeyName, 0, sizeof(chKeyName)); + strncpy(chKeyName, pSONYDLCInfo->chDLCKeyname, 16); + +#ifdef __ORBIS__ + strcpy(chName, chKeyName); +#else + sprintf(chName,"%s-%s",app.GetCommerceCategory(),chKeyName); +#endif + app.GetDLCSkuIDFromProductList(chName,chSkuID); + + // 4J-PB - need to check for an empty store +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if(app.CheckForEmptyStore(iPad)==false) +#endif + { + if(app.DLCAlreadyPurchased(chSkuID)) + { + app.DownloadAlreadyPurchased(chSkuID); + } + else + { + app.Checkout(chSkuID); + } + } + } + } +#else + // TO BE IMPEMENTED FOR ORBIS +#endif + } + } + return 0; +} + +int UIScene_PauseMenu::ViewInvites_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + if(bContinue==true) + { + // Check if we're signed in to LIVE + if(ProfileManager.IsSignedInLive(iPad)) + { +#ifdef __ORBIS__ + SQRNetworkManager_Orbis::RecvInviteGUI(); +#elif defined __PS3__ + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); +#else // __PSVITA__ + PSVITA_STUBBED; +#endif + } + } + return 0; +} + + +int UIScene_PauseMenu::ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu *pClass = (UIScene_PauseMenu *)pParam; + // Exit with or without saving + // Decline means save in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) + { + if( result==C4JStorage::EMessage_ResultDecline ) // Save + { + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { +#ifdef _XBOX + // upsell + ULONGLONG ullOfferID_Full; + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad() ,&UIScene_PauseMenu::WarningTrialTexturePackReturned, dynamic_cast(pClass),app.GetStringTable(), NULL, 0, false); + + return S_OK; + } + } + + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&IUIScene_PauseMenu::ExitGameAndSaveReturned, dynamic_cast(pClass), app.GetStringTable(), NULL, 0, false); + return 0; + } + else + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + StorageManager.SetSaveDisabled(false); +#endif + MinecraftServer::getInstance()->setSaveOnExit( true ); + } + } + else + { + // been a few requests for a confirm on exit without saving + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_DECLINE_SAVE_GAME, IDS_CONFIRM_DECLINE_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&IUIScene_PauseMenu::ExitGameDeclineSaveReturned, dynamic_cast(pClass), app.GetStringTable(), NULL, 0, false); + return 0; + } + + app.SetAction(iPad,eAppAction_ExitWorld); + } + return 0; +} + +#endif + +void UIScene_PauseMenu::SetIgnoreInput(bool ignoreInput) +{ + m_bIgnoreInput = ignoreInput; +} + +#ifdef _XBOX_ONE +void UIScene_PauseMenu::HandleDLCLicenseChange() +{ +} +#endif + +#ifdef __ORBIS__ +bool UIScene_PauseMenu::CheckForPatch() +{ + int npAvailability = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + bool bPatchAvailable; + switch(npAvailability) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + bPatchAvailable=true; + break; + default: + bPatchAvailable=false; + break; + } + + if(bPatchAvailable) + { + int32_t ret = sceErrorDialogInitialize(); + if ( ret==SCE_OK ) + { + m_bErrorDialogRunning = true; + + SceErrorDialogParam param; + sceErrorDialogParamInitialize( ¶m ); + // 4J-PB - We want to display the option to get the patch now + param.errorCode = SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED;//pClass->m_errorCode; + ret = sceUserServiceGetInitialUser( ¶m.userId ); + if ( ret == SCE_OK ) + { + ret = sceErrorDialogOpen( ¶m ); + } + else + { + sceErrorDialogTerminate(); + } + } + } + + return bPatchAvailable; +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_PauseMenu.h b/Minecraft.Client/Common/UI/UIScene_PauseMenu.h new file mode 100644 index 0000000..f1bd53a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_PauseMenu.h @@ -0,0 +1,112 @@ +#pragma once + +#include "UIScene.h" +#include "IUIScene_PauseMenu.h" + +#define BUTTON_PAUSE_RESUMEGAME 0 +#define BUTTON_PAUSE_HELPANDOPTIONS 1 +#define BUTTON_PAUSE_LEADERBOARDS 2 + +#ifdef _XBOX_ONE +#define BUTTON_PAUSE_XBOXHELP 3 +#else +#define BUTTON_PAUSE_ACHIEVEMENTS 3 +#endif + +#define BUTTON_PAUSE_SAVEGAME 4 +#define BUTTON_PAUSE_EXITGAME 5 +#define BUTTONS_PAUSE_MAX BUTTON_PAUSE_EXITGAME + 1 + +class UIScene_PauseMenu : public UIScene, public IUIScene_PauseMenu +{ +private: + bool m_savesDisabled; + bool m_bTrialTexturePack; + bool m_bErrorDialogRunning; + + enum eActions + { + eAction_None=0, +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + eAction_ViewLeaderboardsPSN, + eAction_ViewInvitesPSN, + eAction_SaveGamePSN, + eAction_BuyTexturePackPSN +#endif + + }; + eActions m_eAction; + + UIControl_Button m_buttons[BUTTONS_PAUSE_MAX]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_RESUMEGAME], "Button1") + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_HELPANDOPTIONS], "Button2") + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_LEADERBOARDS], "Button3") +#ifdef _DURANGO + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_XBOXHELP], "Button4") +#else + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_ACHIEVEMENTS], "Button4") +#endif + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_SAVEGAME], "Button5") + UI_MAP_ELEMENT( m_buttons[BUTTON_PAUSE_EXITGAME], "Button6") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual void HandleDLCMountingComplete(); + virtual void HandleDLCInstalled(); +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + static int UnlockFullSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveGame_SignInReturned(void *pParam,bool bContinue, int iPad); + +public: + UIScene_PauseMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_PauseMenu(); + + virtual EUIScene getSceneType() { return eUIScene_PauseMenu;} + + virtual void tick(); + + virtual void updateTooltips(); + virtual void updateComponents(); + virtual void handlePreReload(); + virtual void handleReload(); + +protected: + void updateControlsVisibility(); + + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + virtual void ShowScene(bool show); + virtual void SetIgnoreInput(bool ignoreInput); + bool m_bIgnoreInput; + +private: + void PerformActionSaveGame(); + +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ViewLeaderboards_SignInReturned(void *pParam,bool bContinue, int iPad); + static int ViewInvites_SignInReturned(void *pParam,bool bContinue, int iPad); + static int BuyTexturePack_SignInReturned(void *pParam,bool bContinue, int iPad); + static int WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +protected: +#ifdef _XBOX_ONE + static int BanGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + virtual long long getDefaultGtcButtons() { return _360_GTC_BACK | _360_GTC_PLAY; } +#endif + +#ifdef __ORBIS__ + bool CheckForPatch(); +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.cpp b/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.cpp new file mode 100644 index 0000000..4f1b974 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_QuadrantSignin.h" +#include "..\..\Minecraft.h" +#if defined(__ORBIS__) +#include "Common\Network\Sony\SonyHttp.h" +#endif + +UIScene_QuadrantSignin::UIScene_QuadrantSignin(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_signInInfo = *((SignInInfo *)_initData); + + m_bIgnoreInput = false; + + m_lastRequestedAvatar = -1; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + m_iconRequested[i] = false; + + m_labelPressToJoin[i].init(app.GetString(IDS_MUST_SIGN_IN_TITLE)); + m_labelConnectController[i].init(L""); + m_labelAccountType[i].init(L""); + + //wchar_t num[2]; + //swprintf(num,2,L"%d",i+1); + //m_labelPlayerNumber[i].init(num); + + m_controllerStatus[i] = eControllerStatus_ConnectController; + + if(ProfileManager.IsSignedIn(i)) + { + app.DebugPrintf("Index %d is signed in\n", i); + + setControllerState(i, eControllerStatus_PlayerDetails); + m_labelDisplayName[i].init(ProfileManager.GetDisplayName(i)); + } + else if(InputManager.IsPadConnected(i)) + { + app.DebugPrintf("Index %d is not signed in\n", i); + + setControllerState(i, eControllerStatus_PressToJoin); + m_labelDisplayName[i].init(L""); + } + else + { + app.DebugPrintf("Index %d is not connected\n", i); + + setControllerState(i, eControllerStatus_ConnectController); + } + } + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(InputManager.IsCircleCrossSwapped()) + { + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = true; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetABSwap , 1 , value ); + } +#endif + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); +} + +UIScene_QuadrantSignin::~UIScene_QuadrantSignin() +{ + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +wstring UIScene_QuadrantSignin::getMoviePath() +{ + return L"QuadrantSignin"; +} + +void UIScene_QuadrantSignin::updateTooltips() +{ + ui.SetTooltips(m_iPad, IDS_TOOLTIPS_CONTINUE, IDS_TOOLTIPS_CANCEL); +} + +// Returns true if this scene has focus for the pad passed in +bool UIScene_QuadrantSignin::hasFocus(int iPad) +{ + // Allow input from any controller + return bHasFocus; +} + +bool UIScene_QuadrantSignin::hidesLowerScenes() +{ + // This is a Modal dialog, so don't need to hide the scene behind + return false; +} + +void UIScene_QuadrantSignin::tick() +{ + UIScene::tick(); + + updateState(); +} + +void UIScene_QuadrantSignin::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + app.DebugPrintf("UIScene_QuadrantSignin handling input for pad %d, key %d, repeat- %s, pressed- %s, released- %s\n", iPad, key, repeat?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + if(!m_bIgnoreInput) + { + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + { + if(pressed) + { +#ifdef _DURANGO + if(InputManager.IsPadLocked(iPad)) + { + if(iPad != ProfileManager.GetPrimaryPad()) + { + ProfileManager.RemoveGamepadFromGame(iPad); + } + else +#endif + { + m_bIgnoreInput = true; + m_signInInfo.Func(m_signInInfo.lpParam,false,iPad); + ProfileManager.CancelProfileAvatarRequest(); + + navigateBack(); + } + } +#ifdef _DURANGO + } +#endif + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + m_bIgnoreInput = true; + if(ProfileManager.IsSignedIn(iPad)) + { + app.DebugPrintf("Signed in pad pressed\n"); + ProfileManager.CancelProfileAvatarRequest(); + +#ifdef _DURANGO + // On Durango, if we don't navigate forward here, then when we are on the main menu, it (re)gains focus & that causes our users to get cleared + ui.NavigateToScene(m_iPad, eUIScene_Timer); +#endif + navigateBack(); + m_signInInfo.Func(m_signInInfo.lpParam,true,m_iPad); + } + else + { + app.DebugPrintf("Non-signed in pad pressed\n"); + ProfileManager.RequestSignInUI(false, false, false, true, true,&UIScene_QuadrantSignin::SignInReturned, this, iPad); + } + } + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + if(pressed) + { + sendInputToMovie(key, repeat, pressed, released); + } + break; + } + } + + handled = true; +} + +int UIScene_QuadrantSignin::SignInReturned(void *pParam,bool bContinue, int iPad) +{ + app.DebugPrintf("SignInReturned for pad %d\n", iPad); + + UIScene_QuadrantSignin *pClass = (UIScene_QuadrantSignin *)pParam; + +#ifdef _DURANGO + if(bContinue && pClass->m_signInInfo.requireOnline && ProfileManager.IsSignedIn(iPad)) + { + ProfileManager.CheckMultiplayerPrivileges(iPad, true, &checkAllPrivilegesCallback, pClass); + } + else +#endif + { + pClass->m_bIgnoreInput = false; + pClass->updateState(); + } + + return 0; +} + +#ifdef _DURANGO +void UIScene_QuadrantSignin::checkAllPrivilegesCallback(LPVOID lpParam, bool hasPrivileges, int iPad) +{ + UIScene_QuadrantSignin* pClass = (UIScene_QuadrantSignin*)lpParam; + + if(!hasPrivileges) + { + ProfileManager.RemoveGamepadFromGame(iPad); + } + pClass->m_bIgnoreInput = false; + pClass->updateState(); +} +#endif + +void UIScene_QuadrantSignin::updateState() +{ + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(ProfileManager.IsSignedIn(i) && InputManager.IsPadConnected(i)) + { + //app.DebugPrintf("Index %d is signed in, display name - '%s'\n", i, ProfileManager.GetDisplayName(i).data()); + + setControllerState(i, eControllerStatus_PlayerDetails); + m_labelDisplayName[i].setLabel(ProfileManager.GetDisplayName(i)); + //m_buttonControllers[i].setLabel(app.GetString(IDS_TOOLTIPS_CONTINUE),i); + + if(!m_iconRequested[i]) + { + app.DebugPrintf(app.USER_SR, "Requesting avatar for %d\n", i); + if(ProfileManager.GetProfileAvatar(i, &UIScene_QuadrantSignin::AvatarReturned, this)) + { + m_iconRequested[i] = true; + m_lastRequestedAvatar = i; + } + } + } + else if(InputManager.IsPadConnected(i)) + { + //app.DebugPrintf("Index %d is not signed in\n", i); + + setControllerState(i, eControllerStatus_PressToJoin); + m_labelDisplayName[i].setLabel(L""); + m_iconRequested[i] = false; + } + else + { + //app.DebugPrintf("Index %d is not connected\n", i); + + setControllerState(i, eControllerStatus_ConnectController); + m_iconRequested[i] = false; + } + } +} + +void UIScene_QuadrantSignin::setControllerState(int iPad, EControllerStatus state) +{ + if(m_controllerStatus[iPad] != state) + { + m_controllerStatus[iPad] = state; + + IggyDataValue result; + IggyDataValue value[2]; + value[0].type = IGGY_DATATYPE_number; + value[0].number = iPad; + + value[1].type = IGGY_DATATYPE_number; + value[1].number = (int)state; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetControllerStatus , 2 , value ); + } +} + +int UIScene_QuadrantSignin::AvatarReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes) +{ + UIScene_QuadrantSignin *pClass = (UIScene_QuadrantSignin *)lpParam; + app.DebugPrintf(app.USER_SR,"AvatarReturned callback\n"); + if(pbThumbnail != NULL) + { + // 4J-JEV - Added to ensure each new texture gets a unique name. + static unsigned int quadrantImageCount = 0; + + wchar_t iconName[32]; + swprintf(iconName,32,L"quadrantImage%05d",quadrantImageCount++); + + pClass->registerSubstitutionTexture(iconName,pbThumbnail,dwThumbnailBytes,true); + pClass->m_bitmapIcon[pClass->m_lastRequestedAvatar].setTextureName(iconName); + } + + pClass->m_lastRequestedAvatar = -1; + + return 0; +} diff --git a/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.h b/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.h new file mode 100644 index 0000000..b500fcc --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_QuadrantSignin.h @@ -0,0 +1,110 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_QuadrantSignin : public UIScene +{ +private: + enum EControllerStatus + { + eControllerStatus_ConnectController, + eControllerStatus_PressToJoin, + eControllerStatus_PlayerDetails + }; + + bool m_bIgnoreInput; + SignInInfo m_signInInfo; + + EControllerStatus m_controllerStatus[4]; + bool m_iconRequested[4]; + + int m_lastRequestedAvatar; + + UIControl m_controlPanels[4]; + UIControl_Label m_labelPressToJoin[4], m_labelDisplayName[4], m_labelAccountType[4], m_labelPlayerNumber[4], m_labelConnectController[4]; + UIControl_BitmapIcon m_bitmapIcon[4]; + IggyName m_funcJoinButtonPressed, m_funcSetControllerStatus, m_funcSetABSwap; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_controlPanels[0],"Controller1") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_controlPanels[0]) + UI_MAP_ELEMENT(m_labelPressToJoin[0], "PressLabel") + + UI_MAP_ELEMENT(m_labelDisplayName[0], "GamerTag") + UI_MAP_ELEMENT(m_labelAccountType[0], "AccountType") + UI_MAP_ELEMENT(m_labelPlayerNumber[0], "PlayerNumber") + UI_MAP_ELEMENT(m_bitmapIcon[0], "PlayerPic") + + UI_MAP_ELEMENT(m_labelConnectController[0], "ConnectControllerLabel") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT(m_controlPanels[1],"Controller2") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_controlPanels[1]) + UI_MAP_ELEMENT(m_labelPressToJoin[1], "PressLabel") + + UI_MAP_ELEMENT(m_labelDisplayName[1], "GamerTag") + UI_MAP_ELEMENT(m_labelAccountType[1], "AccountType") + UI_MAP_ELEMENT(m_labelPlayerNumber[1], "PlayerNumber") + UI_MAP_ELEMENT(m_bitmapIcon[1], "PlayerPic") + + UI_MAP_ELEMENT(m_labelConnectController[1], "ConnectControllerLabel") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT(m_controlPanels[2],"Controller3") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_controlPanels[2]) + UI_MAP_ELEMENT(m_labelPressToJoin[2], "PressLabel") + + UI_MAP_ELEMENT(m_labelDisplayName[2], "GamerTag") + UI_MAP_ELEMENT(m_labelAccountType[2], "AccountType") + UI_MAP_ELEMENT(m_labelPlayerNumber[2], "PlayerNumber") + UI_MAP_ELEMENT(m_bitmapIcon[2], "PlayerPic") + + UI_MAP_ELEMENT(m_labelConnectController[2], "ConnectControllerLabel") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT(m_controlPanels[3],"Controller4") + UI_BEGIN_MAP_CHILD_ELEMENTS(m_controlPanels[3]) + UI_MAP_ELEMENT(m_labelPressToJoin[3], "PressLabel") + + UI_MAP_ELEMENT(m_labelDisplayName[3], "GamerTag") + UI_MAP_ELEMENT(m_labelAccountType[3], "AccountType") + UI_MAP_ELEMENT(m_labelPlayerNumber[3], "PlayerNumber") + UI_MAP_ELEMENT(m_bitmapIcon[3], "PlayerPic") + + UI_MAP_ELEMENT(m_labelConnectController[3], "ConnectControllerLabel") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME(m_funcJoinButtonPressed, L"JoinButtonPressed") + UI_MAP_NAME(m_funcSetControllerStatus, L"SetControllerStatus") + UI_MAP_NAME(m_funcSetABSwap, L"SetABSwap") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_QuadrantSignin(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_QuadrantSignin(); + + virtual EUIScene getSceneType() { return eUIScene_QuadrantSignin;} + virtual void updateTooltips(); + + virtual bool hasFocus(int iPad); + virtual bool hidesLowerScenes(); + + void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +private: + static int SignInReturned(void *pParam,bool bContinue, int iPad); + static int AvatarReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes); + + void updateState(); + void setControllerState(int iPad, EControllerStatus state); + +#ifdef _DURANGO + static void checkAllPrivilegesCallback(LPVOID lpParam, bool hasPrivileges, int iPad); +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.cpp b/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.cpp new file mode 100644 index 0000000..3b67f79 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_ReinstallMenu.h" + +UIScene_ReinstallMenu::UIScene_ReinstallMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + +#if TO_BE_IMPLEMENTED + XuiControlSetText(m_Buttons[eControl_Theme],app.GetString(IDS_REINSTALL_THEME)); + XuiControlSetText(m_Buttons[eControl_Gamerpic1],app.GetString(IDS_REINSTALL_GAMERPIC_1)); + XuiControlSetText(m_Buttons[eControl_Gamerpic2],app.GetString(IDS_REINSTALL_GAMERPIC_2)); + XuiControlSetText(m_Buttons[eControl_Avatar1],app.GetString(IDS_REINSTALL_AVATAR_ITEM_1)); + XuiControlSetText(m_Buttons[eControl_Avatar2],app.GetString(IDS_REINSTALL_AVATAR_ITEM_2)); + XuiControlSetText(m_Buttons[eControl_Avatar3],app.GetString(IDS_REINSTALL_AVATAR_ITEM_3)); +#endif +} + +wstring UIScene_ReinstallMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"ReinstallSplit"; + } + else + { + return L"ReinstallMenu"; + } +} + +void UIScene_ReinstallMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE); +} + +void UIScene_ReinstallMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + // 4J Stu - Do we want to show the logo in-game? + //if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + //else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +void UIScene_ReinstallMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed && !repeat) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_ReinstallMenu::handlePress(F64 controlId, F64 childId) +{ +#if TO_BE_IMPLEMENTED + switch((int)controlId) + { + case BUTTON_HAO_CHANGESKIN: + ui.NavigateToScene(m_iPad, eUIScene_SkinSelectMenu); + break; + case BUTTON_HAO_HOWTOPLAY: + ui.NavigateToScene(m_iPad, eUIScene_HowToPlayMenu); + break; + case BUTTON_HAO_CONTROLS: + ui.NavigateToScene(m_iPad, eUIScene_ControlsMenu); + break; + case BUTTON_HAO_SETTINGS: + ui.NavigateToScene(m_iPad, eUIScene_SettingsMenu); + break; + case BUTTON_HAO_CREDITS: + ui.NavigateToScene(m_iPad, eUIScene_Credits); + break; + case BUTTON_HAO_REINSTALL: + ui.NavigateToScene(m_iPad, eUIScene_ReinstallMenu); + break; + case BUTTON_HAO_DEBUG: + ui.NavigateToScene(m_iPad, eUIScene_DebugOptions); + break; + } +#endif +} diff --git a/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.h b/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.h new file mode 100644 index 0000000..54c20a6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_ReinstallMenu.h @@ -0,0 +1,47 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_ReinstallMenu : public UIScene +{ +private: + enum EControls + { + eControl_Theme, + eControl_Gamerpic1, + eControl_Gamerpic2, + eControl_Avatar1, + eControl_Avatar2, + eControl_Avatar3, + eControl_COUNT, + }; + UIControl_Button m_buttons[eControl_COUNT]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttons[eControl_Theme], "Button1") + UI_MAP_ELEMENT( m_buttons[eControl_Gamerpic1], "Button2") + UI_MAP_ELEMENT( m_buttons[eControl_Gamerpic2], "Button3") + UI_MAP_ELEMENT( m_buttons[eControl_Avatar1], "Button4") + UI_MAP_ELEMENT( m_buttons[eControl_Avatar2], "Button5") + UI_MAP_ELEMENT( m_buttons[eControl_Avatar3], "Button6") + UI_END_MAP_ELEMENTS_AND_NAMES() + + //bool m_bNotInGame; +public: + UIScene_ReinstallMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_ReinstallMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_SaveMessage.cpp b/Minecraft.Client/Common/UI/UIScene_SaveMessage.cpp new file mode 100644 index 0000000..a91d5aa --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SaveMessage.cpp @@ -0,0 +1,182 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SaveMessage.h" + +#define PROFILE_LOADED_TIMER_ID 0 +#define PROFILE_LOADED_TIMER_TIME 50 + +UIScene_SaveMessage::UIScene_SaveMessage(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + parentLayer->addComponent(iPad,eUIComponent_Panorama); + parentLayer->addComponent(iPad,eUIComponent_Logo); + + m_buttonConfirm.init(app.GetString(IDS_CONFIRM_OK),eControl_Confirm); + m_labelDescription.init(app.GetString(IDS_SAVE_ICON_MESSAGE)); + + IggyDataValue result; + + // Russian needs to resize the box + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcAutoResize , 0 , NULL ); + + // 4J-PB - If we have a signed in user connected, let's get the DLC now + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( (InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i)) ) + { + if(!app.DLCInstallProcessCompleted() && !app.DLCInstallPending()) + { + app.StartInstallDLCProcess(i); + break; + } + } + } + + m_bIgnoreInput=false; + + // 4J-TomK - rebuild touch after auto resize +#ifdef __PSVITA__ + ui.TouchBoxRebuild(this); +#endif +} + +UIScene_SaveMessage::~UIScene_SaveMessage() +{ + m_parentLayer->removeComponent(eUIComponent_Panorama); + m_parentLayer->removeComponent(eUIComponent_Logo); +} + +wstring UIScene_SaveMessage::getMoviePath() +{ + return L"SaveMessage"; +} + +void UIScene_SaveMessage::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT ); +} + +void UIScene_SaveMessage::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bIgnoreInput) return; +#if defined (__ORBIS__) || defined (__PSVITA__) + // ignore all players except player 0 - it's their profile that is currently being used + if(iPad!=0) return; +#endif + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + // #ifdef __PS3__ + // case ACTION_MENU_Y: + // if(pressed) + // { + // // language select - switch to Greek for now + // if(app.GetMinecraftLanguage(iPad)==MINECRAFT_LANGUAGE_DEFAULT) + // { + // app.SetMinecraftLanguage(iPad,MINECRAFT_LANGUAGE_GREEK); + // } + // else + // { + // app.SetMinecraftLanguage(iPad,MINECRAFT_LANGUAGE_DEFAULT); + // } + // // reload the string table + // ui.SetupFont(); + // app.loadStringTable(); + // handleReload(); + // } + // break; + // #endif + } +} + +void UIScene_SaveMessage::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Confirm: + + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + m_bIgnoreInput=true; + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + // wait for the profile to be read - this has been kicked off earlier, so should be read by now + addTimer(PROFILE_LOADED_TIMER_ID,PROFILE_LOADED_TIMER_TIME); +#else + ui.NavigateToHomeMenu(); +#endif + break; + }; +} + +void UIScene_SaveMessage::handleTimerComplete(int id) +{ + switch(id) + { + case PROFILE_LOADED_TIMER_ID: + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + C4JStorage::eOptionsCallback eStatus=app.GetOptionsCallbackStatus(0); + + switch(eStatus) + { + case C4JStorage::eOptions_Callback_Read: + case C4JStorage::eOptions_Callback_Read_FileNotFound: + case C4JStorage::eOptions_Callback_Read_Fail: +#ifdef __PSVITA__ + case C4JStorage::eOptions_Callback_Write_Fail: + case C4JStorage::eOptions_Callback_Write: +#endif + // set defaults - which has already been done + killTimer(PROFILE_LOADED_TIMER_ID); + ui.NavigateToHomeMenu(); + SQRNetworkManager::SafeToRespondToGameBootInvite(); + app.SetOptionsCallbackStatus(0,C4JStorage::eOptions_Callback_Idle); + break; + case C4JStorage::eOptions_Callback_Read_CorruptDeleted: + killTimer(PROFILE_LOADED_TIMER_ID); + ui.NavigateToHomeMenu(); + SQRNetworkManager::SafeToRespondToGameBootInvite(); + app.SetOptionsCallbackStatus(0,C4JStorage::eOptions_Callback_Idle); + break; + case C4JStorage::eOptions_Callback_Read_Corrupt: + // get the user to delete the options file + app.DebugPrintf("Corrupt options file\n"); + app.SetOptionsCallbackStatus(0,C4JStorage::eOptions_Callback_Read_CorruptDeletePending); + m_bIgnoreInput=false; + // give the option to delete the save + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_CORRUPT_FILE, IDS_CORRUPT_OPTIONS, uiIDA, 1, + 0,&UIScene_SaveMessage::DeleteOptionsDialogReturned,this, app.GetStringTable()); + break; + } +#endif + } + + break; + } +} + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) +int UIScene_SaveMessage::DeleteOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //UIScene_SaveMessage* pClass = (UIScene_SaveMessage*)pParam; + + // kick off the delete + StorageManager.DeleteOptionsData(iPad); + + return 0; +} +#endif diff --git a/Minecraft.Client/Common/UI/UIScene_SaveMessage.h b/Minecraft.Client/Common/UI/UIScene_SaveMessage.h new file mode 100644 index 0000000..cedc8c8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SaveMessage.h @@ -0,0 +1,51 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SaveMessage : public UIScene +{ +private: + enum EControls + { + eControl_Confirm, + }; + + bool m_bIgnoreInput; + + UIControl_Button m_buttonConfirm; + UIControl_Label m_labelDescription; + IggyName m_funcAutoResize; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_buttonConfirm, "Confirm") + UI_MAP_ELEMENT(m_labelDescription, "Description") + UI_MAP_NAME( m_funcAutoResize, L"AutoResize") + UI_END_MAP_ELEMENTS_AND_NAMES() + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + static int DeleteOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +public: + UIScene_SaveMessage(int iPad, void *initData, UILayer *parentLayer); + ~UIScene_SaveMessage(); + + virtual EUIScene getSceneType() { return eUIScene_SaveMessage;} + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + virtual void updateTooltips(); + +protected: + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleTimerComplete(int id); + +protected: + void handlePress(F64 controlId, F64 childId); + + virtual long long getDefaultGtcButtons() { return 0; } +}; diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp new file mode 100644 index 0000000..6d892d7 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsAudioMenu.h" + +UIScene_SettingsAudioMenu::UIScene_SettingsAudioMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_MUSIC ),app.GetGameSettings(m_iPad,eGameSetting_MusicVolume)); + m_sliderMusic.init(TempString,eControl_Music,0,100,app.GetGameSettings(m_iPad,eGameSetting_MusicVolume)); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SOUND ),app.GetGameSettings(m_iPad,eGameSetting_SoundFXVolume)); + m_sliderSound.init(TempString,eControl_Sound,0,100,app.GetGameSettings(m_iPad,eGameSetting_SoundFXVolume)); + + doHorizontalResizeCheck(); + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); +#endif + } +} + +UIScene_SettingsAudioMenu::~UIScene_SettingsAudioMenu() +{ +} + +wstring UIScene_SettingsAudioMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsAudioMenuSplit"; + } + else + { + return L"SettingsAudioMenu"; + } +} + +void UIScene_SettingsAudioMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsAudioMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + } +} + +void UIScene_SettingsAudioMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsAudioMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_Music: + m_sliderMusic.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_MusicVolume,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_MUSIC ),value); + m_sliderMusic.setLabel(TempString); + + break; + case eControl_Sound: + m_sliderSound.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_SoundFXVolume,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SOUND ),value); + m_sliderSound.setLabel(TempString); + + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h new file mode 100644 index 0000000..6c48b22 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsAudioMenu.h @@ -0,0 +1,38 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SettingsAudioMenu : public UIScene +{ +private: + enum EControls + { + eControl_Music, + eControl_Sound + }; + + UIControl_Slider m_sliderMusic, m_sliderSound; // Sliders + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_sliderMusic, "Music") + UI_MAP_ELEMENT( m_sliderSound, "Sound") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_SettingsAudioMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsAudioMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsAudioMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.cpp new file mode 100644 index 0000000..d5447f7 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsControlMenu.h" + +UIScene_SettingsControlMenu::UIScene_SettingsControlMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + WCHAR TempString[256]; + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INGAME ),app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame)); + m_sliderSensitivityInGame.init(TempString,eControl_SensitivityInGame,0,200,app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame)); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INMENU ),app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu)); + m_sliderSensitivityInMenu.init(TempString,eControl_SensitivityInMenu,0,200,app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu)); + + doHorizontalResizeCheck(); + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); +#endif + } +} + +UIScene_SettingsControlMenu::~UIScene_SettingsControlMenu() +{ +} + +wstring UIScene_SettingsControlMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsControlMenuSplit"; + } + else + { + return L"SettingsControlMenu"; + } +} + +void UIScene_SettingsControlMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsControlMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + } +} + +void UIScene_SettingsControlMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsControlMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_SensitivityInGame: + m_sliderSensitivityInGame.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INGAME ),value); + m_sliderSensitivityInGame.setLabel(TempString); + + break; + case eControl_SensitivityInMenu: + m_sliderSensitivityInMenu.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INMENU ),value); + m_sliderSensitivityInMenu.setLabel(TempString); + + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.h new file mode 100644 index 0000000..6d3b864 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsControlMenu.h @@ -0,0 +1,37 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SettingsControlMenu : public UIScene +{ +private: + enum EControls + { + eControl_SensitivityInGame, + eControl_SensitivityInMenu + }; + + UIControl_Slider m_sliderSensitivityInGame, m_sliderSensitivityInMenu; // Sliders + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_sliderSensitivityInGame, "SensitivityInGame") + UI_MAP_ELEMENT( m_sliderSensitivityInMenu, "SensitivityInMenu") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_SettingsControlMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsControlMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsControlMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp new file mode 100644 index 0000000..1234121 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsGraphicsMenu.h" + +UIScene_SettingsGraphicsMenu::UIScene_SettingsGraphicsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bNotInGame=(Minecraft::GetInstance()->level==NULL); + + m_checkboxClouds.init(app.GetString(IDS_CHECKBOX_RENDER_CLOUDS),eControl_Clouds,(app.GetGameSettings(m_iPad,eGameSetting_Clouds)!=0)); + m_checkboxBedrockFog.init(app.GetString(IDS_CHECKBOX_RENDER_BEDROCKFOG),eControl_BedrockFog,(app.GetGameSettings(m_iPad,eGameSetting_BedrockFog)!=0)); + m_checkboxCustomSkinAnim.init(app.GetString(IDS_CHECKBOX_CUSTOM_SKIN_ANIM),eControl_CustomSkinAnim,(app.GetGameSettings(m_iPad,eGameSetting_CustomSkinAnim)!=0)); + + + WCHAR TempString[256]; + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma)); + m_sliderGamma.init(TempString,eControl_Gamma,0,100,app.GetGameSettings(m_iPad,eGameSetting_Gamma)); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); + m_sliderInterfaceOpacity.init(TempString,eControl_InterfaceOpacity,0,100,app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); + + doHorizontalResizeCheck(); + + bool bInGame=(Minecraft::GetInstance()->level!=NULL); + bool bIsPrimaryPad=(ProfileManager.GetPrimaryPad()==m_iPad); + // if we're not in the game, we need to use basescene 0 + if(bInGame) + { + // If the game has started, then you need to be the host to change the in-game gamertags + if(bIsPrimaryPad) + { + // we are the primary player on this machine, but not the game host + // are we the game host? If not, we need to remove the bedrockfog setting + if(!g_NetworkManager.IsHost()) + { + // hide the in-game bedrock fog setting + removeControl(&m_checkboxBedrockFog, true); + } + } + else + { + // We shouldn't have the bedrock fog option, or the m_CustomSkinAnim option + removeControl(&m_checkboxBedrockFog, true); + removeControl(&m_checkboxCustomSkinAnim, true); + } + } + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); +#endif + } +} + +UIScene_SettingsGraphicsMenu::~UIScene_SettingsGraphicsMenu() +{ +} + +wstring UIScene_SettingsGraphicsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsGraphicsMenuSplit"; + } + else + { + return L"SettingsGraphicsMenu"; + } +} + +void UIScene_SettingsGraphicsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsGraphicsMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +void UIScene_SettingsGraphicsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_Clouds,m_checkboxClouds.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_BedrockFog,m_checkboxBedrockFog.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_CustomSkinAnim,m_checkboxCustomSkinAnim.IsChecked()?1:0); + + navigateBack(); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsGraphicsMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_Gamma: + m_sliderGamma.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Gamma,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),value); + m_sliderGamma.setLabel(TempString); + + break; + case eControl_InterfaceOpacity: + m_sliderInterfaceOpacity.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_InterfaceOpacity,value); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),value); + m_sliderInterfaceOpacity.setLabel(TempString); + + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h new file mode 100644 index 0000000..e9c4905 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsGraphicsMenu.h @@ -0,0 +1,46 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SettingsGraphicsMenu : public UIScene +{ +private: + enum EControls + { + eControl_Clouds, + eControl_BedrockFog, + eControl_CustomSkinAnim, + eControl_Gamma, + eControl_InterfaceOpacity + }; + + UIControl_CheckBox m_checkboxClouds, m_checkboxBedrockFog, m_checkboxCustomSkinAnim; // Checkboxes + UIControl_Slider m_sliderGamma, m_sliderInterfaceOpacity; // Sliders + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_checkboxClouds, "Clouds") + UI_MAP_ELEMENT( m_checkboxBedrockFog, "BedrockFog") + UI_MAP_ELEMENT( m_checkboxCustomSkinAnim, "CustomSkinAnim") + UI_MAP_ELEMENT( m_sliderGamma, "Gamma") + UI_MAP_ELEMENT( m_sliderInterfaceOpacity, "InterfaceOpacity") + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bNotInGame; +public: + UIScene_SettingsGraphicsMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsGraphicsMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsGraphicsMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsMenu.cpp new file mode 100644 index 0000000..4ef7eb5 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsMenu.cpp @@ -0,0 +1,168 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsMenu.h" +#include "..\..\Minecraft.h" + +UIScene_SettingsMenu::UIScene_SettingsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + m_buttons[BUTTON_ALL_OPTIONS].init(app.GetString(IDS_OPTIONS),BUTTON_ALL_OPTIONS); + m_buttons[BUTTON_ALL_AUDIO].init(app.GetString(IDS_AUDIO),BUTTON_ALL_AUDIO); + m_buttons[BUTTON_ALL_CONTROL].init(app.GetString(IDS_CONTROL),BUTTON_ALL_CONTROL); + m_buttons[BUTTON_ALL_GRAPHICS].init(app.GetString(IDS_GRAPHICS),BUTTON_ALL_GRAPHICS); + m_buttons[BUTTON_ALL_UI].init(app.GetString(IDS_USER_INTERFACE),BUTTON_ALL_UI); + m_buttons[BUTTON_ALL_RESETTODEFAULTS].init(app.GetString(IDS_RESET_TO_DEFAULTS),BUTTON_ALL_RESETTODEFAULTS); + + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + removeControl( &m_buttons[BUTTON_ALL_AUDIO], true); + removeControl( &m_buttons[BUTTON_ALL_GRAPHICS], true); + } + + doHorizontalResizeCheck(); + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); +#endif + } +} + +UIScene_SettingsMenu::~UIScene_SettingsMenu() +{ +} + +wstring UIScene_SettingsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsMenuSplit"; + } + else + { + return L"SettingsMenu"; + } +} + +void UIScene_SettingsMenu::handleReload() +{ + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + removeControl( &m_buttons[BUTTON_ALL_AUDIO], true); + removeControl( &m_buttons[BUTTON_ALL_GRAPHICS], true); + } + + doHorizontalResizeCheck(); +} + +void UIScene_SettingsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +void UIScene_SettingsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + app.CheckGameSettingsChanged(true,iPad); + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsMenu::handlePress(F64 controlId, F64 childId) +{ + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + switch((int)controlId) + { + case BUTTON_ALL_OPTIONS: + ui.NavigateToScene(m_iPad, eUIScene_SettingsOptionsMenu); + break; + case BUTTON_ALL_AUDIO: + ui.NavigateToScene(m_iPad, eUIScene_SettingsAudioMenu); + break; + case BUTTON_ALL_CONTROL: + ui.NavigateToScene(m_iPad, eUIScene_SettingsControlMenu); + break; + case BUTTON_ALL_GRAPHICS: + ui.NavigateToScene(m_iPad, eUIScene_SettingsGraphicsMenu); + break; + case BUTTON_ALL_UI: + ui.NavigateToScene(m_iPad, eUIScene_SettingsUIMenu); + break; + case BUTTON_ALL_RESETTODEFAULTS: + { + // check they really want to do this + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + ui.RequestMessageBox(IDS_DEFAULTS_TITLE, IDS_DEFAULTS_TEXT, uiIDA, 2, m_iPad,&UIScene_SettingsMenu::ResetDefaultsDialogReturned,this, app.GetStringTable(), NULL, 0, false); + } + break; + } +} + +int UIScene_SettingsMenu::ResetDefaultsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_SettingsMenu* pClass = (UIScene_SettingsMenu*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined __PSVITA__) + app.SetDefaultOptions(StorageManager.GetDashboardProfileSettings(pClass->m_iPad),pClass->m_iPad); +#else + app.SetDefaultOptions(ProfileManager.GetDashboardProfileSettings(pClass->m_iPad),pClass->m_iPad); +#endif + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + app.CheckGameSettingsChanged(true,iPad); + } + return 0; +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsMenu.h new file mode 100644 index 0000000..7f5fe16 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsMenu.h @@ -0,0 +1,47 @@ +#pragma once + +#include "UIScene.h" + +#define BUTTON_ALL_OPTIONS 0 +#define BUTTON_ALL_AUDIO 1 +#define BUTTON_ALL_CONTROL 2 +#define BUTTON_ALL_GRAPHICS 4 +#define BUTTON_ALL_UI 5 +#define BUTTON_ALL_RESETTODEFAULTS 6 +#define BUTTONS_ALL_MAX BUTTON_ALL_RESETTODEFAULTS + 1 + +class UIScene_SettingsMenu : public UIScene +{ +private: + UIControl_Button m_buttons[BUTTONS_ALL_MAX]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_OPTIONS], "Button1") + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_AUDIO], "Button2") + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_CONTROL], "Button3") + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_GRAPHICS], "Button4") + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_UI], "Button5") + UI_MAP_ELEMENT( m_buttons[BUTTON_ALL_RESETTODEFAULTS], "Button6") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_SettingsMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + virtual void handleReload(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + + static int ResetDefaultsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.cpp new file mode 100644 index 0000000..72576de --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.cpp @@ -0,0 +1,253 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsOptionsMenu.h" + +int UIScene_SettingsOptionsMenu::m_iDifficultySettingA[4]= +{ + IDS_DIFFICULTY_PEACEFUL, + IDS_DIFFICULTY_EASY, + IDS_DIFFICULTY_NORMAL, + IDS_DIFFICULTY_HARD +}; + +int UIScene_SettingsOptionsMenu::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + +UIScene_SettingsOptionsMenu::UIScene_SettingsOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bNotInGame=(Minecraft::GetInstance()->level==NULL); + + m_checkboxViewBob.init(app.GetString(IDS_VIEW_BOBBING),eControl_ViewBob,(app.GetGameSettings(m_iPad,eGameSetting_ViewBob)!=0)); + m_checkboxShowHints.init(app.GetString(IDS_HINTS),eControl_ShowHints,(app.GetGameSettings(m_iPad,eGameSetting_Hints)!=0)); + m_checkboxShowTooltips.init(app.GetString(IDS_IN_GAME_TOOLTIPS),eControl_ShowTooltips,(app.GetGameSettings(m_iPad,eGameSetting_Tooltips)!=0)); + m_checkboxInGameGamertags.init(app.GetString(IDS_IN_GAME_GAMERTAGS),eControl_InGameGamertags,(app.GetGameSettings(m_iPad,eGameSetting_GamertagsVisible)!=0)); + + // check if we should display the mash-up option + if(m_bNotInGame && app.GetMashupPackWorlds(m_iPad)!=0xFFFFFFFF) + { + // the mash-up option is needed + m_bMashUpWorldsUnhideOption=true; + m_checkboxMashupWorlds.init(app.GetString(IDS_UNHIDE_MASHUP_WORLDS),eControl_ShowMashUpWorlds,false); + } + else + { + //m_checkboxMashupWorlds.init(L"",eControl_ShowMashUpWorlds,false); + removeControl(&m_checkboxMashupWorlds, true); + m_bMashUpWorldsUnhideOption=false; + } + + unsigned char ucValue=app.GetGameSettings(m_iPad,eGameSetting_Autosave); + + wchar_t autosaveLabels[9][256]; + for(unsigned int i = 0; i < 9; ++i) + { + if(i==0) + { + swprintf( autosaveLabels[i], 256, L"%ls", app.GetString( IDS_SLIDER_AUTOSAVE_OFF )); + } + else + { + swprintf( autosaveLabels[i], 256, L"%ls: %d %ls", app.GetString( IDS_SLIDER_AUTOSAVE ),i*15, app.GetString( IDS_MINUTES )); + } + + } + m_sliderAutosave.setAllPossibleLabels(9,autosaveLabels); + m_sliderAutosave.init(autosaveLabels[ucValue],eControl_Autosave,0,8,ucValue); + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + removeControl(&m_sliderAutosave,true); +#endif + + ucValue = app.GetGameSettings(m_iPad,eGameSetting_Difficulty); + wchar_t difficultyLabels[4][256]; + for(unsigned int i = 0; i < 4; ++i) + { + swprintf( difficultyLabels[i], 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[i])); + } + m_sliderDifficulty.setAllPossibleLabels(4,difficultyLabels); + m_sliderDifficulty.init(difficultyLabels[ucValue],eControl_Difficulty,0,3,ucValue); + + wstring wsText=app.GetString(m_iDifficultySettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)]); + EHTMLFontSize size = eHTMLSize_Normal; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = eHTMLSize_Splitscreen; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText; + + m_labelDifficultyText.init(wsText); + + // If you are in-game, only the game host can change in-game gamertags, and you can't change difficulty + // only the primary player gets to change the autosave and difficulty settings + bool bRemoveDifficulty=false; + bool bRemoveAutosave=false; + bool bRemoveInGameGamertags=false; + float fRemoveHeight=0.0f,fWidth,fHeight; + + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bPrimaryPlayer = ProfileManager.GetPrimaryPad()==m_iPad; + if(!bPrimaryPlayer) + { + bRemoveDifficulty=true; + bRemoveAutosave=true; + bRemoveInGameGamertags=true; + } + + if(!bNotInGame) // in the game + { + bRemoveDifficulty=true; + if(!g_NetworkManager.IsHost()) + { + bRemoveAutosave=true; + bRemoveInGameGamertags=true; + } + } + if(bRemoveDifficulty) + { + m_labelDifficultyText.setVisible( false ); + removeControl(&m_sliderDifficulty, true); + } + + if(bRemoveAutosave) + { + removeControl(&m_sliderAutosave, true); + } + + if(bRemoveInGameGamertags) + { + removeControl(&m_checkboxInGameGamertags, true); + } + + doHorizontalResizeCheck(); + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); +#endif + } +} + +UIScene_SettingsOptionsMenu::~UIScene_SettingsOptionsMenu() +{ +} + +wstring UIScene_SettingsOptionsMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsOptionsMenuSplit"; + } + else + { + return L"SettingsOptionsMenu"; + } +} + +void UIScene_SettingsOptionsMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsOptionsMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,RenderManager.IsHiDef()); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +void UIScene_SettingsOptionsMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_ViewBob,m_checkboxViewBob.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_GamertagsVisible,m_checkboxInGameGamertags.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_Hints,m_checkboxShowHints.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_Tooltips,m_checkboxShowTooltips.IsChecked()?1:0); + + // the mashup option will only be shown if some worlds have been previously hidden + if(m_bMashUpWorldsUnhideOption && m_checkboxMashupWorlds.IsChecked()) + { + // unhide all worlds + app.EnableMashupPackWorlds(m_iPad); + } + + // 4J-PB - don't action changes here or we might write to the profile on backing out here and then get a change in the settings all, and write again on backing out there + //app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsOptionsMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_Autosave: + m_sliderAutosave.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Autosave,value); + // Update the autosave timer + app.SetAutosaveTimerTime(); + + break; + case eControl_Difficulty: + m_sliderDifficulty.handleSliderMove(value); + + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,value); + + wstring wsText=app.GetString(m_iDifficultySettingA[value]); + EHTMLFontSize size = eHTMLSize_Normal; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = eHTMLSize_Splitscreen; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText; + m_labelDifficultyText.setLabel(wsText.c_str()); + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.h new file mode 100644 index 0000000..265a079 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsOptionsMenu.h @@ -0,0 +1,57 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SettingsOptionsMenu : public UIScene +{ +private: + enum EControls + { + eControl_ViewBob, + eControl_ShowHints, + eControl_ShowTooltips, + eControl_InGameGamertags, + eControl_ShowMashUpWorlds, + eControl_Autosave, + eControl_Difficulty + }; +protected: + static int m_iDifficultySettingA[4]; + static int m_iDifficultyTitleSettingA[4]; + +private: + UIControl_CheckBox m_checkboxViewBob, m_checkboxShowHints, m_checkboxShowTooltips, m_checkboxInGameGamertags, m_checkboxMashupWorlds; // Checkboxes + UIControl_Slider m_sliderAutosave, m_sliderDifficulty; // Sliders + UIControl_Label m_labelDifficultyText; //Text + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_checkboxViewBob, "ViewBob") + UI_MAP_ELEMENT( m_checkboxShowHints, "ShowHints") + UI_MAP_ELEMENT( m_checkboxShowTooltips, "ShowTooltips") + UI_MAP_ELEMENT( m_checkboxInGameGamertags, "InGameGamertags") + UI_MAP_ELEMENT( m_checkboxMashupWorlds, "ShowMashUpWorlds") + UI_MAP_ELEMENT( m_sliderAutosave, "Autosave") + UI_MAP_ELEMENT( m_sliderDifficulty, "Difficulty") + UI_MAP_ELEMENT( m_labelDifficultyText, "DifficultyText") + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bNotInGame; + bool m_bMashUpWorldsUnhideOption; +public: + UIScene_SettingsOptionsMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsOptionsMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsOptionsMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.cpp new file mode 100644 index 0000000..917012d --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SettingsUIMenu.h" + +UIScene_SettingsUIMenu::UIScene_SettingsUIMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_bNotInGame=(Minecraft::GetInstance()->level==NULL); + + m_checkboxDisplayHUD.init(app.GetString(IDS_CHECKBOX_DISPLAY_HUD),eControl_DisplayHUD,(app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)!=0)); + m_checkboxDisplayHand.init(app.GetString(IDS_CHECKBOX_DISPLAY_HAND),eControl_DisplayHand,(app.GetGameSettings(m_iPad,eGameSetting_DisplayHand)!=0)); + m_checkboxDisplayDeathMessages.init(app.GetString(IDS_CHECKBOX_DEATH_MESSAGES),eControl_DisplayDeathMessages,(app.GetGameSettings(m_iPad,eGameSetting_DeathMessages)!=0)); + m_checkboxDisplayAnimatedCharacter.init(app.GetString(IDS_CHECKBOX_ANIMATED_CHARACTER),eControl_DisplayAnimatedCharacter,(app.GetGameSettings(m_iPad,eGameSetting_AnimatedCharacter)!=0)); + m_checkboxSplitscreen.init(app.GetString(IDS_CHECKBOX_VERTICAL_SPLIT_SCREEN),eControl_Splitscreen,(app.GetGameSettings(m_iPad,eGameSetting_SplitScreenVertical)!=0)); + m_checkboxShowSplitscreenGamertags.init(app.GetString(IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS),eControl_ShowSplitscreenGamertags,(app.GetGameSettings(m_iPad,eGameSetting_DisplaySplitscreenGamertags)!=0)); + + WCHAR TempString[256]; + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZE ),app.GetGameSettings(m_iPad,eGameSetting_UISize)+1); + m_sliderUISize.init(TempString,eControl_UISize,1,3,app.GetGameSettings(m_iPad,eGameSetting_UISize)+1); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZESPLITSCREEN ),app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1); + m_sliderUISizeSplitscreen.init(TempString,eControl_UISizeSplitscreen,1,3,app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1); + + doHorizontalResizeCheck(); + + bool bInGame=(Minecraft::GetInstance()->level!=NULL); + bool bPrimaryPlayer = ProfileManager.GetPrimaryPad()==m_iPad; + + // if we're not in the game, we need to use basescene 0 + if(bInGame) + { + // If the game has started, then you need to be the host to change the in-game gamertags + if(!bPrimaryPlayer) + { + // hide things we don't want the splitscreen player changing + removeControl(&m_checkboxSplitscreen, true); + removeControl(&m_checkboxShowSplitscreenGamertags, true); + } + } + + + if(app.GetLocalPlayerCount()>1) + { +#if TO_BE_IMPLEMENTED + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); +#endif + } +} + +void UIScene_SettingsUIMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SettingsUIMenu::updateComponents() +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + if(bNotInGame) + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + } + else + { + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,false); + + if( app.GetLocalPlayerCount() == 1 ) m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,true); + else m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + + } +} + +UIScene_SettingsUIMenu::~UIScene_SettingsUIMenu() +{ +} + +wstring UIScene_SettingsUIMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SettingsUIMenuSplit"; + } + else + { + return L"SettingsUIMenu"; + } +} + +void UIScene_SettingsUIMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_DisplayHUD,m_checkboxDisplayHUD.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DisplayHand,m_checkboxDisplayHand.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DisplaySplitscreenGamertags,m_checkboxShowSplitscreenGamertags.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DeathMessages,m_checkboxDisplayDeathMessages.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_AnimatedCharacter,m_checkboxDisplayAnimatedCharacter.IsChecked()?1:0); + + // if the splitscreen vertical/horizontal has changed, need to update the scenes + if(app.GetGameSettings(m_iPad,eGameSetting_SplitScreenVertical)!=(m_checkboxSplitscreen.IsChecked()?1:0)) + { + // changed + app.SetGameSettings(m_iPad,eGameSetting_SplitScreenVertical,m_checkboxSplitscreen.IsChecked()?1:0); + + // close the xui scenes, so we don't have the navigate backed to menu at the wrong place + if(app.GetLocalPlayerCount()==2) + { + ui.CloseAllPlayersScenes(); + } + else + { + navigateBack(); + } + } + else + { + navigateBack(); + } + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + sendInputToMovie(key, repeat, pressed, released); + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_LEFT: + case ACTION_MENU_RIGHT: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_SettingsUIMenu::handleSliderMove(F64 sliderId, F64 currentValue) +{ + WCHAR TempString[256]; + int value = (int)currentValue; + switch((int)sliderId) + { + case eControl_UISize: + m_sliderUISize.handleSliderMove(value); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZE ),value); + m_sliderUISize.setLabel(TempString); + + // is this different from the current value? + if(value != app.GetGameSettings(m_iPad,eGameSetting_UISize)+1) + { + app.SetGameSettings(m_iPad,eGameSetting_UISize,value-1); + // Apply the changes to the selected text position + ui.UpdateSelectedItemPos(m_iPad); + } + + break; + case eControl_UISizeSplitscreen: + m_sliderUISizeSplitscreen.handleSliderMove(value); + + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZESPLITSCREEN ),value); + m_sliderUISizeSplitscreen.setLabel(TempString); + + if(value != app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1) + { + // slider is 1 to 3 + app.SetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen,value-1); + // Apply the changes to the selected text position + ui.UpdateSelectedItemPos(m_iPad); + } + + break; + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.h b/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.h new file mode 100644 index 0000000..8968bbe --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SettingsUIMenu.h @@ -0,0 +1,53 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_SettingsUIMenu : public UIScene +{ +private: + enum EControls + { + eControl_DisplayHUD, + eControl_DisplayHand, + eControl_DisplayDeathMessages, + eControl_DisplayAnimatedCharacter, + eControl_Splitscreen, + eControl_ShowSplitscreenGamertags, + eControl_UISize, + eControl_UISizeSplitscreen + }; + + UIControl_CheckBox m_checkboxDisplayHUD, m_checkboxDisplayHand, m_checkboxDisplayDeathMessages, m_checkboxDisplayAnimatedCharacter, m_checkboxSplitscreen, m_checkboxShowSplitscreenGamertags; // Checkboxes + UIControl_Slider m_sliderUISize, m_sliderUISizeSplitscreen; // Sliders + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_checkboxDisplayHUD, "DisplayHUD") + UI_MAP_ELEMENT( m_checkboxDisplayHand, "DisplayHand") + UI_MAP_ELEMENT( m_checkboxDisplayDeathMessages, "DisplayDeathMessages") + UI_MAP_ELEMENT( m_checkboxDisplayAnimatedCharacter, "DisplayAnimatedCharacter") + UI_MAP_ELEMENT( m_checkboxSplitscreen, "Splitscreen") + UI_MAP_ELEMENT( m_checkboxShowSplitscreenGamertags, "ShowSplitscreenGamertags") + + UI_MAP_ELEMENT( m_sliderUISize, "UISize") + UI_MAP_ELEMENT( m_sliderUISizeSplitscreen, "UISizeSplitscreen") + UI_END_MAP_ELEMENTS_AND_NAMES() + + bool m_bNotInGame; +public: + UIScene_SettingsUIMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SettingsUIMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SettingsUIMenu;} + + virtual void updateTooltips(); + virtual void updateComponents(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleSliderMove(F64 sliderId, F64 currentValue); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp new file mode 100644 index 0000000..c29bac2 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.cpp @@ -0,0 +1,206 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SignEntryMenu.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" + +UIScene_SignEntryMenu::UIScene_SignEntryMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + SignEntryScreenInput* initData = (SignEntryScreenInput*)_initData; + m_sign = initData->sign; + + m_bConfirmed = false; + m_bIgnoreInput = false; + + m_buttonConfirm.init(app.GetString(IDS_DONE), eControl_Confirm); + m_labelMessage.init(app.GetString(IDS_EDIT_SIGN_MESSAGE)); + + for(unsigned int i = 0; i<4; ++i) + { +#if TO_BE_IMPLEMENTED + // Have to have the Latin alphabet here, since that's what we have on the sign in-game + // but because the JAP/KOR/CHN fonts don't have extended European characters, let's restrict those languages to not having the extended character set, since they can't see what they are typing + switch(XGetLanguage()) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_RUSSIAN: + m_signRows[i].SetKeyboardType(C_4JInput::EKeyboardMode_Alphabet); + break; + default: + m_signRows[i].SetKeyboardType(C_4JInput::EKeyboardMode_Full); + break; + } + + m_signRows[i].SetText( m_sign->GetMessage(i).c_str() ); + m_signRows[i].SetTextLimit(15); + // Set the title and desc for the edit keyboard popup + m_signRows[i].SetTitleAndText(IDS_SIGN_TITLE,IDS_SIGN_TITLE_TEXT); +#endif + m_textInputLines[i].init(m_sign->GetMessage(i).c_str(), i); + } + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); +} + +UIScene_SignEntryMenu::~UIScene_SignEntryMenu() +{ + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +wstring UIScene_SignEntryMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SignEntryMenuSplit"; + } + else + { + return L"SignEntryMenu"; + } +} + +void UIScene_SignEntryMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_SignEntryMenu::tick() +{ + UIScene::tick(); + + if(m_bConfirmed) + { + m_bConfirmed = false; + + // Set the sign text here so we on;y call the verify once it has been set, not while we're typing in to it + for(int i=0;i<4;i++) + { + wstring temp=m_textInputLines[i].getLabel(); + m_sign->SetMessage(i,temp); + } + + m_sign->setChanged(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + // need to send the new data + if (pMinecraft->level->isClientSide) + { + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player != NULL && player->connection && player->connection->isStarted()) + { + player->connection->send( shared_ptr( new SignUpdatePacket(m_sign->x, m_sign->y, m_sign->z, m_sign->IsVerified(), m_sign->IsCensored(), m_sign->GetMessages()) ) ); + } + } + ui.CloseUIScenes(m_iPad); + } +} + +void UIScene_SignEntryMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if(m_bConfirmed || m_bIgnoreInput) return; + + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + // user backed out, so wipe the sign + wstring temp=L""; + + for(int i=0;i<4;i++) + { + m_sign->SetMessage(i,temp); + } + + navigateBack(); + ui.PlayUISFX(eSFX_Back); + handled = true; + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + sendInputToMovie(key, repeat, pressed, released); + handled = true; + break; + } +} + +int UIScene_SignEntryMenu::KeyboardCompleteCallback(LPVOID lpParam,bool bRes) +{ + // 4J HEG - No reason to set value if keyboard was cancelled + UIScene_SignEntryMenu *pClass=(UIScene_SignEntryMenu *)lpParam; + pClass->m_bIgnoreInput = false; + if (bRes) + { + uint16_t pchText[128]; + ZeroMemory(pchText, 128 * sizeof(uint16_t) ); + InputManager.GetText(pchText); + pClass->m_textInputLines[pClass->m_iEditingLine].setLabel((wchar_t *)pchText); + } + return 0; +} + +void UIScene_SignEntryMenu::handlePress(F64 controlId, F64 childId) +{ + switch((int)controlId) + { + case eControl_Confirm: + { + m_bConfirmed = true; + } + break; + case eControl_Line1: + case eControl_Line2: + case eControl_Line3: + case eControl_Line4: + { + m_iEditingLine = (int)controlId; + m_bIgnoreInput = true; +#ifdef _XBOX_ONE + // 4J-PB - Xbox One uses the Windows virtual keyboard, and doesn't have the Xbox 360 Latin keyboard type, so we can't restrict the input set to alphanumeric. The closest we get is the emailSmtpAddress type. + int language = XGetLanguage(); + switch(language) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_TCHINESE: + InputManager.RequestKeyboard(app.GetString(IDS_SIGN_TITLE),m_textInputLines[m_iEditingLine].getLabel(),(DWORD)m_iPad,15,&UIScene_SignEntryMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Email); + break; + default: + InputManager.RequestKeyboard(app.GetString(IDS_SIGN_TITLE),m_textInputLines[m_iEditingLine].getLabel(),(DWORD)m_iPad,15,&UIScene_SignEntryMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Alphabet); + break; + } +#else + InputManager.RequestKeyboard(app.GetString(IDS_SIGN_TITLE),m_textInputLines[m_iEditingLine].getLabel(),(DWORD)m_iPad,15,&UIScene_SignEntryMenu::KeyboardCompleteCallback,this,C_4JInput::EKeyboardMode_Alphabet); +#endif + } + break; + } +} + +void UIScene_SignEntryMenu::handleDestroy() +{ +#ifdef __PSVITA__ + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); +#endif + + // another player destroyed the anvil, so shut down the keyboard if it is displayed +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + InputManager.DestroyKeyboard(); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h new file mode 100644 index 0000000..28b37d5 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SignEntryMenu.h @@ -0,0 +1,58 @@ +#pragma once + +#include "UIScene.h" + +class SignTileEntity; + +class UIScene_SignEntryMenu : public UIScene +{ +private: + enum EControls + { + // Lines should be 0-3 + eControl_Line1, + eControl_Line2, + eControl_Line3, + eControl_Line4, + eControl_Confirm + }; + + shared_ptr m_sign; + int m_iEditingLine; + bool m_bConfirmed; + bool m_bIgnoreInput; + + UIControl_Button m_buttonConfirm; + UIControl_Label m_labelMessage; + UIControl_TextInput m_textInputLines[4]; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_buttonConfirm, "Confirm") + UI_MAP_ELEMENT( m_labelMessage, "Message") + + UI_MAP_ELEMENT( m_textInputLines[0], "Line1") + UI_MAP_ELEMENT( m_textInputLines[1], "Line2") + UI_MAP_ELEMENT( m_textInputLines[2], "Line3") + UI_MAP_ELEMENT( m_textInputLines[3], "Line4") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_SignEntryMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_SignEntryMenu(); + + virtual EUIScene getSceneType() { return eUIScene_SignEntryMenu;} + virtual void updateTooltips(); + + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + void handlePress(F64 controlId, F64 childId); + static int KeyboardCompleteCallback(LPVOID lpParam,const bool bRes); + virtual void handleDestroy(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp new file mode 100644 index 0000000..6910dd6 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.cpp @@ -0,0 +1,1798 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_SkinSelectMenu.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#ifdef __ORBIS__ +#include +#elif defined __PSVITA__ +#include +#endif + +#define SKIN_SELECT_PACK_DEFAULT 0 +#define SKIN_SELECT_PACK_FAVORITES 1 +//#define SKIN_SELECT_PACK_PLAYER_CUSTOM 1 +#define SKIN_SELECT_MAX_DEFAULTS 2 + +WCHAR *UIScene_SkinSelectMenu::wchDefaultNamesA[]= +{ + L"USE LOCALISED VERSION", // Server selected + L"Steve", + L"Tennis Steve", + L"Tuxedo Steve", + L"Athlete Steve", + L"Scottish Steve", + L"Prisoner Steve", + L"Cyclist Steve", + L"Boxer Steve", +}; + +UIScene_SkinSelectMenu::UIScene_SkinSelectMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_labelSelected.init( app.GetString( IDS_SELECTED ) ); + +#ifdef __ORBIS__ + m_bErrorDialogRunning=false; +#endif + + m_bIgnoreInput=false; + m_bNoSkinsToShow = false; + + m_currentPack = NULL; + m_packIndex = SKIN_SELECT_PACK_DEFAULT; + m_skinIndex = 0; + + m_originalSkinId = app.GetPlayerSkinId(iPad); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_selectedSkinPath = L""; + m_selectedCapePath = L""; + m_vAdditionalSkinBoxes = NULL; + + m_bSlidingSkins = false; + m_bAnimatingMove = false; + m_bSkinIndexChanged = false; + + m_currentNavigation = eSkinNavigation_Skin; + + m_currentPackCount = 0; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward); + + m_characters[eCharacter_Next1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left); + m_characters[eCharacter_Next2].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left); + m_characters[eCharacter_Next3].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left); + m_characters[eCharacter_Next4].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left); + + m_characters[eCharacter_Previous1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right); + m_characters[eCharacter_Previous2].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right); + m_characters[eCharacter_Previous3].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right); + m_characters[eCharacter_Previous4].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right); + + m_labelSkinName.init(L""); + m_labelSkinOrigin.init(L""); + + m_leftLabel = L""; + m_centreLabel = L""; + m_rightLabel = L""; + +#ifdef __PSVITA__ + // initialise vita tab controls with ids + m_TouchTabLeft.init(ETouchInput_TabLeft); + m_TouchTabRight.init(ETouchInput_TabRight); + m_TouchTabCenter.init(ETouchInput_TabCenter); + m_TouchIggyCharacters.init(ETouchInput_IggyCharacters); +#endif + + // block input if we're waiting for DLC to install. The end of dlc mounting custom message will fill the save list + if(app.StartInstallDLCProcess(m_iPad)) + { + // DLC mounting in progress, so disable input + m_bIgnoreInput=true; + + m_controlTimer.setVisible( true ); + m_controlIggyCharacters.setVisible( false ); + m_controlSkinNamePlate.setVisible( false ); + + setCharacterLocked(false); + setCharacterSelected(false); + } + else + { + m_controlTimer.setVisible( false ); + + if(app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin)>0) + { + // Change to display the favorites if there are any. The current skin will be in there (probably) - need to check for it + m_currentPack = app.m_dlcManager.getPackContainingSkin(m_currentSkinPath); + bool bFound; + if(m_currentPack != NULL) + { + m_packIndex = app.m_dlcManager.getPackIndex(m_currentPack,bFound,DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + } + } + + // If we have any favourites, set this to the favourites + // first validate the favorite skins - we might have uninstalled the DLC needed for them + app.ValidateFavoriteSkins(m_iPad); + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + m_packIndex = SKIN_SELECT_PACK_FAVORITES; + } + + handlePackIndexChanged(); + } + + // Display the tooltips + +#ifdef __PSVITA__ + InitializeCriticalSection(&m_DLCInstallCS); // to prevent a race condition between the install and the mounted callback +#endif + +} + +void UIScene_SkinSelectMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, m_bNoSkinsToShow?-1:IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); +} + +void UIScene_SkinSelectMenu::updateComponents() +{ + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); +} + +wstring UIScene_SkinSelectMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"SkinSelectMenuSplit"; + } + else + { + return L"SkinSelectMenu"; + } +} + +void UIScene_SkinSelectMenu::tick() +{ + UIScene::tick(); + + if(m_bSkinIndexChanged) + { + m_bSkinIndexChanged = false; + handleSkinIndexChanged(); + } + + // check for new DLC installed + + // check for the patch error dialog +#ifdef __ORBIS__ + + // process the error dialog (for a patch being available) + if(m_bErrorDialogRunning) + { + SceErrorDialogStatus stat = sceErrorDialogUpdateStatus(); + if( stat == SCE_ERROR_DIALOG_STATUS_FINISHED ) + { + sceErrorDialogTerminate(); + m_bErrorDialogRunning=false; + } + } + +#endif +} + +void UIScene_SkinSelectMenu::handleAnimationEnd() +{ + if(m_bSlidingSkins) + { + m_bSlidingSkins = false; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward, false); + m_characters[eCharacter_Next1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left, false); + m_characters[eCharacter_Previous1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right, false); + + m_bSkinIndexChanged = true; + //handleSkinIndexChanged(); + + m_bAnimatingMove = false; + } +} + +void UIScene_SkinSelectMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + if (m_bIgnoreInput) return; + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + app.CheckGameSettingsChanged(true,iPad); + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + app.SetPlayerSkin(iPad, m_skinIndex); + app.SetPlayerCape(iPad, 0); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + setCharacterSelected(true); + ui.PlayUISFX(eSFX_Press); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(app.GetPlayerFavoriteSkinsCount(iPad)>0) + { + // get the pack number from the skin id + wchar_t chars[256]; + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(iPad,m_skinIndex)); + + DLCPack *Pack=app.m_dlcManager.getPackContainingSkin(chars); + + if(Pack) + { + DLCSkinFile *skinFile = Pack->getSkinFile(chars); + app.SetPlayerSkin(iPad, skinFile->getPath()); + app.SetPlayerCape(iPad, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + setCharacterSelected(true); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + app.SetPlayerFavoriteSkinsPos(iPad,m_skinIndex); + } + } + break; + default: + if( m_currentPack != NULL ) + { + DLCSkinFile *skinFile = m_currentPack->getSkinFile(m_skinIndex); + + if ( !skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ) // Is this a free skin? + && !m_currentPack->hasPurchasedFile( DLCManager::e_DLCType_Skin, skinFile->getPath() ) // do we have a license? + ) + { + // 4J-PB - check for a patch +#ifdef __ORBIS__ + // 4J-PB - Check if there is a patch for the game + int errorCode = ProfileManager.getNPAvailability(ProfileManager.GetPrimaryPad()); + + bool bPatchAvailable; + switch(errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + bPatchAvailable=true; + break; + default: + bPatchAvailable=false; + break; + } + + if(bPatchAvailable) + { + int32_t ret=sceErrorDialogInitialize(); + m_bErrorDialogRunning=true; + if ( ret==SCE_OK ) + { + SceErrorDialogParam param; + sceErrorDialogParamInitialize( ¶m ); + // 4J-PB - We want to display the option to get the patch now + param.errorCode = SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED;//pClass->m_errorCode; + ret = sceUserServiceGetInitialUser( ¶m.userId ); + if ( ret == SCE_OK ) + { + ret=sceErrorDialogOpen( ¶m ); + break; + } + } + } +#endif + + // no + UINT uiIDA[1] = { IDS_OK }; +#ifdef __ORBIS__ + // Check if PSN is unavailable because of age restriction + int npAvailability = ProfileManager.getNPAvailability(iPad); + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable()); + } + else +#endif + // We need to upsell the full version + if(ProfileManager.IsGuest(iPad)) + { + // can't buy + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1,iPad,NULL,NULL,app.GetStringTable(),NULL,0,false); + } + // are we online? + else if(!ProfileManager.IsSignedInLive(iPad)) + { + showNotOnlineDialog(iPad); + } + else + { + // upsell +#ifdef _XBOX + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_currentPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=m_currentPack->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Skin_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(m_iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see the store + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad,NULL,this, app.GetStringTable(),NULL,0,false); +#endif + } + else + { + // 4J-PB - need to check for an empty store +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if(app.CheckForEmptyStore(iPad)==false) +#endif + { + this->m_bIgnoreInput = true; + + UINT uiIDA[2] = { IDS_CONFIRM_OK, IDS_CONFIRM_CANCEL }; + ui.RequestMessageBox(IDS_UNLOCK_DLC_TITLE, IDS_UNLOCK_DLC_SKIN, uiIDA, 2, iPad,&UIScene_SkinSelectMenu::UnlockSkinReturned,this,app.GetStringTable(),NULL,0,false); + } + } + } + } + else + { + app.SetPlayerSkin(iPad, skinFile->getPath()); + app.SetPlayerCape(iPad, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + setCharacterSelected(true); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + + // push this onto the favorite list + AddFavoriteSkin(iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + } + } + + ui.PlayUISFX(eSFX_Press); + break; + } + } + break; + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + if(pressed) + { + if(m_packIndex==SKIN_SELECT_PACK_FAVORITES) + { + if(app.GetPlayerFavoriteSkinsCount(iPad)==0) + { + // ignore this, since there are no skins being displayed + break; + } + } + + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + ui.PlayUISFX(eSFX_Scroll); + switch(m_currentNavigation) + { + case eSkinNavigation_Pack: + m_currentNavigation = eSkinNavigation_Skin; + break; + case eSkinNavigation_Skin: + m_currentNavigation = eSkinNavigation_Pack; + break; + }; + sendInputToMovie(key, repeat, pressed, released); + } + break; + case ACTION_MENU_LEFT: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + if(!m_bAnimatingMove) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + ui.PlayUISFX(eSFX_Scroll); + + m_skinIndex = getPreviousSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left, true); + m_characters[eCharacter_Previous1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward, true); + + // 4J Stu - Swapped nav buttons + sendInputToMovie(ACTION_MENU_RIGHT, repeat, pressed, released); + } + } + else if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + ui.PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getPreviousPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + } + break; + case ACTION_MENU_RIGHT: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + if(!m_bAnimatingMove) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + ui.PlayUISFX(eSFX_Scroll); + m_skinIndex = getNextSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right, true); + m_characters[eCharacter_Next1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward, true); + + // 4J Stu - Swapped nav buttons + sendInputToMovie(ACTION_MENU_LEFT, repeat, pressed, released); + } + } + else if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.AnimateKeyPress(iPad, key, repeat, pressed, released); + ui.PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getNextPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + } + break; + case ACTION_MENU_OTHER_STICK_PRESS: + if(pressed) + { + ui.PlayUISFX(eSFX_Press); + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_characters[eCharacter_Current].ResetRotation(); + } + } + break; + case ACTION_MENU_OTHER_STICK_LEFT: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_characters[eCharacter_Current].m_incYRot = true; + } + else + { + ui.PlayUISFX(eSFX_Scroll); + } + } + else if(released) + { + m_characters[eCharacter_Current].m_incYRot = false; + } + break; + case ACTION_MENU_OTHER_STICK_RIGHT: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_characters[eCharacter_Current].m_decYRot = true; + } + else + { + ui.PlayUISFX(eSFX_Scroll); + } + } + else if(released) + { + m_characters[eCharacter_Current].m_decYRot = false; + } + break; + case ACTION_MENU_OTHER_STICK_UP: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + //m_previewControl->m_incXRot = true; + m_characters[eCharacter_Current].CyclePreviousAnimation(); + } + else + { + ui.PlayUISFX(eSFX_Scroll); + } + } + break; + case ACTION_MENU_OTHER_STICK_DOWN: + if(pressed) + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + //m_previewControl->m_decXRot = true; + m_characters[eCharacter_Current].CycleNextAnimation(); + } + else + { + ui.PlayUISFX(eSFX_Scroll); + } + } + break; + } +} + +void UIScene_SkinSelectMenu::InputActionOK(unsigned int iPad) +{ + ui.AnimateKeyPress(iPad, ACTION_MENU_OK, false, true, false); + + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + app.SetPlayerSkin(iPad, m_skinIndex); + app.SetPlayerCape(iPad, 0); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + setCharacterSelected(true); + ui.PlayUISFX(eSFX_Press); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(app.GetPlayerFavoriteSkinsCount(iPad)>0) + { + // get the pack number from the skin id + wchar_t chars[256]; + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(iPad,m_skinIndex)); + + DLCPack *Pack=app.m_dlcManager.getPackContainingSkin(chars); + + if(Pack) + { + DLCSkinFile *skinFile = Pack->getSkinFile(chars); + app.SetPlayerSkin(iPad, skinFile->getPath()); + app.SetPlayerCape(iPad, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + setCharacterSelected(true); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + app.SetPlayerFavoriteSkinsPos(iPad,m_skinIndex); + } +} + break; + default: + if( m_currentPack != NULL ) + { + bool renableInputAfterOperation = true; + m_bIgnoreInput = true; + + DLCSkinFile *skinFile = m_currentPack->getSkinFile(m_skinIndex); + + // Is this a free skin? + + if(!skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free )) + { + // do we have a license? + //if(true) + if(!m_currentPack->hasPurchasedFile( DLCManager::e_DLCType_Skin, skinFile->getPath() )) + { + // no + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // We need to upsell the full version + if(ProfileManager.IsGuest(iPad)) + { + // can't buy + ui.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1,iPad,NULL,NULL,app.GetStringTable(),NULL,0,false); + } +#if defined(__PS3__) || defined(__ORBIS__) + // are we online? + else if(!ProfileManager.IsSignedInLive(iPad)) + { + showNotOnlineDialog(iPad); + } +#endif + else + { + // upsell +#ifdef _XBOX + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_currentPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=m_currentPack->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the skin pack + SentientManager.RecordUpsellPresented(iPad, eSet_UpsellID_Skin_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + bool bContentRestricted=false; +#if defined(__PS3__) || defined(__PSVITA__) + ProfileManager.GetChatAndContentRestrictions(m_iPad,true,NULL,&bContentRestricted,NULL); +#endif + if(bContentRestricted) + { +#if !(defined(_XBOX) || defined(_WIN64)) // 4J Stu - Temp to get the win build running, but so we check this for other platforms + // you can't see the store + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,this, app.GetStringTable(),NULL,0,false); +#endif + } + else + { + // 4J-PB - need to check for an empty store +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if(app.CheckForEmptyStore(iPad)==false) +#endif + { + m_bIgnoreInput = true; + renableInputAfterOperation = false; + + UINT uiIDA[2] = { IDS_CONFIRM_OK, IDS_CONFIRM_CANCEL }; + ui.RequestMessageBox(IDS_UNLOCK_DLC_TITLE, IDS_UNLOCK_DLC_SKIN, uiIDA, 2, iPad,&UIScene_SkinSelectMenu::UnlockSkinReturned,this,app.GetStringTable(),NULL,0,false); + } + } + } + } + else + { + app.SetPlayerSkin(iPad, skinFile->getPath()); + app.SetPlayerCape(iPad, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + setCharacterSelected(true); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + + // push this onto the favorite list + AddFavoriteSkin(m_iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + } + } + else + { + app.SetPlayerSkin(iPad, skinFile->getPath()); + app.SetPlayerCape(iPad, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + setCharacterSelected(true); + m_currentSkinPath = app.GetPlayerSkinName(iPad); + m_originalSkinId = app.GetPlayerSkinId(iPad); + + // push this onto the favorite list + AddFavoriteSkin(iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + } + + if (renableInputAfterOperation) m_bIgnoreInput = false; + } + + ui.PlayUISFX(eSFX_Press); + break; + } +} + +void UIScene_SkinSelectMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + int characterId = -1; + swscanf((wchar_t*)region->name,L"Character%d",&characterId); + if (characterId == -1) + { + app.DebugPrintf("Invalid character to render found\n"); + } + else + { + // Setup GDraw, normal game render states and matrices + CustomDrawData *customDrawRegion = ui.setupCustomDraw(this,region); + delete customDrawRegion; + + //app.DebugPrintf("Scissor x0= %d, y0= %d, x1= %d, y1= %d\n", region->scissor_x0, region->scissor_y0, region->scissor_x1, region->scissor_y1); + //app.DebugPrintf("Stencil mask= %d, stencil ref= %d, stencil write= %d\n", region->stencil_func_mask, region->stencil_func_ref, region->stencil_write_mask); +#ifdef __PS3__ + if(region->stencil_func_ref != 0) RenderManager.StateSetStencil(GL_EQUAL,region->stencil_func_ref,region->stencil_func_mask); +#elif __PSVITA__ + // AP - make sure the skins are only drawn inside the smokey panel + if(region->stencil_func_ref != 0) RenderManager.StateSetStencil(SCE_GXM_STENCIL_FUNC_EQUAL,region->stencil_func_mask,region->stencil_write_mask); +#else + if(region->stencil_func_ref != 0) RenderManager.StateSetStencil(GL_EQUAL,region->stencil_func_ref, region->stencil_func_mask,region->stencil_write_mask); +#endif + m_characters[characterId].render(region); + + // Finish GDraw and anything else that needs to be finalised + ui.endCustomDraw(region); + } +} + +void UIScene_SkinSelectMenu::handleSkinIndexChanged() +{ + BOOL showPrevious = FALSE, showNext = FALSE; + DWORD previousIndex = 0, nextIndex = 0; + wstring skinName = L""; + wstring skinOrigin = L""; + bool bSkinIsFree=false; + bool bLicensed=false; + DLCSkinFile *skinFile=NULL; + DLCPack *Pack=NULL; + BYTE sidePreviewControlsL,sidePreviewControlsR; + m_bNoSkinsToShow=false; + + TEXTURE_NAME backupTexture = TN_MOB_CHAR; + + setCharacterSelected(false); + + m_controlSkinNamePlate.setVisible( false ); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(m_skinIndex); + m_selectedSkinPath = skinFile->getPath(); + m_selectedCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + m_vAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + + skinName = skinFile->getParameterAsString( DLCManager::e_DLCParamType_DisplayName ); + skinOrigin = skinFile->getParameterAsString( DLCManager::e_DLCParamType_ThemeName ); + + if( m_selectedSkinPath.compare( m_currentSkinPath ) == 0 ) + { + setCharacterSelected(true); + } + + bSkinIsFree = skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ); + bLicensed = m_currentPack->hasPurchasedFile( DLCManager::e_DLCType_Skin, m_selectedSkinPath ); + + setCharacterLocked(!(bSkinIsFree || bLicensed)); + + m_characters[eCharacter_Current].setVisible(true); + m_controlSkinNamePlate.setVisible( true ); + } + else + { + m_selectedSkinPath = L""; + m_selectedCapePath = L""; + m_vAdditionalSkinBoxes = NULL; + + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(m_skinIndex); + + if( m_skinIndex == eDefaultSkins_ServerSelected ) + { + skinName = app.GetString(IDS_DEFAULT_SKINS); + } + else + { + skinName = wchDefaultNamesA[m_skinIndex]; + } + + if( m_originalSkinId == m_skinIndex ) + { + setCharacterSelected(true); + } + setCharacterLocked(false); + setCharacterLocked(false); + + m_characters[eCharacter_Current].setVisible(true); + m_controlSkinNamePlate.setVisible( true ); + + break; + case SKIN_SELECT_PACK_FAVORITES: + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + // get the pack number from the skin id + wchar_t chars[256]; + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,m_skinIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + m_selectedSkinPath = skinFile->getPath(); + m_selectedCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + m_vAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + + skinName = skinFile->getParameterAsString( DLCManager::e_DLCParamType_DisplayName ); + skinOrigin = skinFile->getParameterAsString( DLCManager::e_DLCParamType_ThemeName ); + + if( m_selectedSkinPath.compare( m_currentSkinPath ) == 0 ) + { + setCharacterSelected(true); + } + + bSkinIsFree = skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ); + bLicensed = Pack->hasPurchasedFile( DLCManager::e_DLCType_Skin, m_selectedSkinPath ); + + setCharacterLocked(!(bSkinIsFree || bLicensed)); + m_controlSkinNamePlate.setVisible( true ); + } + else + { + setCharacterSelected(false); + setCharacterLocked(false); + } + } + else + { + //disable the display + m_characters[eCharacter_Current].setVisible(false); + + // change the tooltips + m_bNoSkinsToShow=true; + } + break; + } + } + + m_labelSkinName.setLabel(skinName); + m_labelSkinOrigin.setLabel(skinOrigin); + + + if(m_vAdditionalSkinBoxes && m_vAdditionalSkinBoxes->size()!=0) + { + // add the boxes to the humanoid model, but only if we've not done this already + + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),m_vAdditionalSkinBoxes); + } + } + + if(skinFile!=NULL) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + + m_characters[eCharacter_Current].SetTexture(m_selectedSkinPath, backupTexture); + m_characters[eCharacter_Current].SetCapeTexture(m_selectedCapePath); + + showNext = TRUE; + showPrevious = TRUE; + nextIndex = getNextSkinIndex(m_skinIndex); + previousIndex = getPreviousSkinIndex(m_skinIndex); + + wstring otherSkinPath = L""; + wstring otherCapePath = L""; + vector *othervAdditionalSkinBoxes=NULL; + wchar_t chars[256]; + + // turn off all displays + for(unsigned int i = eCharacter_Current + 1; i < eCharacter_COUNT; ++i) + { + m_characters[i].setVisible(false); + } + + unsigned int uiCurrentFavoriteC=app.GetPlayerFavoriteSkinsCount(m_iPad); + + if(m_packIndex==SKIN_SELECT_PACK_FAVORITES) + { + // might not be enough to cycle through + if(uiCurrentFavoriteC<((sidePreviewControls*2)+1)) + { + if(uiCurrentFavoriteC==0) + { + sidePreviewControlsL=sidePreviewControlsR=0; + } + // might be an odd number + else if((uiCurrentFavoriteC-1)%2==1) + { + sidePreviewControlsL=1+(uiCurrentFavoriteC-1)/2; + sidePreviewControlsR=(uiCurrentFavoriteC-1)/2; + } + else + { + sidePreviewControlsL=sidePreviewControlsR=(uiCurrentFavoriteC-1)/2; + } + } + else + { + sidePreviewControlsL=sidePreviewControlsR=sidePreviewControls; + } + } + else + { + sidePreviewControlsL=sidePreviewControlsR=sidePreviewControls; + } + + for(BYTE i = 0; i < sidePreviewControlsR; ++i) + { + if(showNext) + { + skinFile=NULL; + + m_characters[eCharacter_Next1 + i].setVisible(true); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(nextIndex); + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + else + { + otherSkinPath = L""; + otherCapePath = L""; + othervAdditionalSkinBoxes=NULL; + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(nextIndex); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(uiCurrentFavoriteC>0) + { + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,nextIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + } + break; + default: + break; + } + + } + if(othervAdditionalSkinBoxes && othervAdditionalSkinBoxes->size()!=0) + { + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),othervAdditionalSkinBoxes); + } + } + // 4J-PB - anim override needs set before SetTexture + if(skinFile!=NULL) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + m_characters[eCharacter_Next1 + i].SetTexture(otherSkinPath, backupTexture); + m_characters[eCharacter_Next1 + i].SetCapeTexture(otherCapePath); + } + + nextIndex = getNextSkinIndex(nextIndex); + } + + + + for(BYTE i = 0; i < sidePreviewControlsL; ++i) + { + if(showPrevious) + { + skinFile=NULL; + + m_characters[eCharacter_Previous1 + i].setVisible(true); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(previousIndex); + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + else + { + otherSkinPath = L""; + otherCapePath = L""; + othervAdditionalSkinBoxes=NULL; + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(previousIndex); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(uiCurrentFavoriteC>0) + { + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,previousIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + } + + break; + default: + break; + } + } + if(othervAdditionalSkinBoxes && othervAdditionalSkinBoxes->size()!=0) + { + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),othervAdditionalSkinBoxes); + } + } + // 4J-PB - anim override needs set before SetTexture + if(skinFile) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + m_characters[eCharacter_Previous1 + i].SetTexture(otherSkinPath, backupTexture); + m_characters[eCharacter_Previous1 + i].SetCapeTexture(otherCapePath); + } + + previousIndex = getPreviousSkinIndex(previousIndex); + } + + updateTooltips(); +} + +TEXTURE_NAME UIScene_SkinSelectMenu::getTextureId(int skinIndex) +{ + TEXTURE_NAME texture = TN_MOB_CHAR; + switch(skinIndex) + { + case eDefaultSkins_ServerSelected: + case eDefaultSkins_Skin0: + texture = TN_MOB_CHAR; + break; + case eDefaultSkins_Skin1: + texture = TN_MOB_CHAR1; + break; + case eDefaultSkins_Skin2: + texture = TN_MOB_CHAR2; + break; + case eDefaultSkins_Skin3: + texture = TN_MOB_CHAR3; + break; + case eDefaultSkins_Skin4: + texture = TN_MOB_CHAR4; + break; + case eDefaultSkins_Skin5: + texture = TN_MOB_CHAR5; + break; + case eDefaultSkins_Skin6: + texture = TN_MOB_CHAR6; + break; + case eDefaultSkins_Skin7: + texture = TN_MOB_CHAR7; + break; + }; + + return texture; +} + +int UIScene_SkinSelectMenu::getNextSkinIndex(DWORD sourceIndex) +{ + int nextSkin = sourceIndex; + + // special case for favourites + switch(m_packIndex) + { + + case SKIN_SELECT_PACK_FAVORITES: + ++nextSkin; + if(nextSkin>=app.GetPlayerFavoriteSkinsCount(m_iPad)) + { + nextSkin=0; + } + + break; + default: + ++nextSkin; + + if(m_packIndex == SKIN_SELECT_PACK_DEFAULT && nextSkin >= eDefaultSkins_Count) + { + nextSkin = eDefaultSkins_ServerSelected; + } + else if(m_currentPack != NULL && nextSkin>=m_currentPack->getSkinCount()) + { + nextSkin = 0; + } + break; + } + + + return nextSkin; +} + +int UIScene_SkinSelectMenu::getPreviousSkinIndex(DWORD sourceIndex) +{ + int previousSkin = sourceIndex; + switch(m_packIndex) + { + + case SKIN_SELECT_PACK_FAVORITES: + if(previousSkin==0) + { + previousSkin = app.GetPlayerFavoriteSkinsCount(m_iPad) - 1; + } + else + { + --previousSkin; + } + break; + default: + if(previousSkin==0) + { + if(m_packIndex == SKIN_SELECT_PACK_DEFAULT) + { + previousSkin = eDefaultSkins_Count - 1; + } + else if(m_currentPack != NULL) + { + previousSkin = m_currentPack->getSkinCount()-1; + } + } + else + { + --previousSkin; + } + break; + } + + + return previousSkin; +} + +void UIScene_SkinSelectMenu::handlePackIndexChanged() +{ + if(m_packIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + m_currentPack = app.m_dlcManager.getPack(m_packIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + } + else + { + m_currentPack = NULL; + } + m_skinIndex = 0; + if(m_currentPack != NULL) + { + bool found; + DWORD currentSkinIndex = m_currentPack->getSkinIndexAt(m_currentSkinPath, found); + if(found) m_skinIndex = currentSkinIndex; + } + else + { + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + if( !GET_IS_DLC_SKIN_FROM_BITMASK(m_originalSkinId) ) + { + DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(m_originalSkinId); + DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(m_originalSkinId); + if( ugcSkinIndex == 0 ) + { + m_skinIndex = (EDefaultSkins) defaultSkinIndex; + } + } + break; + case SKIN_SELECT_PACK_FAVORITES: + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + bool found; + wchar_t chars[256]; + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,app.GetPlayerFavoriteSkinsPos(m_iPad))); + + DLCPack *Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + DWORD currentSkinIndex = Pack->getSkinIndexAt(m_currentSkinPath, found); + if(found) m_skinIndex = app.GetPlayerFavoriteSkinsPos(m_iPad); + } + } + break; + default: + break; + } + } + handleSkinIndexChanged(); + updatePackDisplay(); +} + +void UIScene_SkinSelectMenu::updatePackDisplay() +{ + m_currentPackCount = app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + + if(m_packIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(m_packIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + setCentreLabel(thisPack->getName().c_str()); + } + else + { + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + setCentreLabel(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + setCentreLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + + int nextPackIndex = getNextPackIndex(m_packIndex); + if(nextPackIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(nextPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + setRightLabel(thisPack->getName().c_str()); + } + else + { + switch(nextPackIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + setRightLabel(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + setRightLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + + int previousPackIndex = getPreviousPackIndex(m_packIndex); + if(previousPackIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(previousPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + setLeftLabel(thisPack->getName().c_str()); + } + else + { + switch(previousPackIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + setLeftLabel(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + setLeftLabel(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + +} + +int UIScene_SkinSelectMenu::getNextPackIndex(DWORD sourceIndex) +{ + int nextPack = sourceIndex; + ++nextPack; + if(nextPack > app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) - 1 + SKIN_SELECT_MAX_DEFAULTS) + { + nextPack = SKIN_SELECT_PACK_DEFAULT; + } + + return nextPack; +} + +int UIScene_SkinSelectMenu::getPreviousPackIndex(DWORD sourceIndex) +{ + int previousPack = sourceIndex; + if (previousPack == SKIN_SELECT_PACK_DEFAULT) + { + DWORD packCount = app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin); + + if (packCount > 0) + { + previousPack = packCount + SKIN_SELECT_MAX_DEFAULTS - 1; + } + else + { + previousPack = SKIN_SELECT_MAX_DEFAULTS - 1; + } + } + else + { + --previousPack; + } + + return previousPack; +} + +void UIScene_SkinSelectMenu::setCharacterSelected(bool selected) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = selected; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetPlayerCharacterSelected , 1 , value ); +} + +void UIScene_SkinSelectMenu::setCharacterLocked(bool locked) +{ + IggyDataValue result; + IggyDataValue value[1]; + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = locked; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetCharacterLocked , 1 , value ); +} + +void UIScene_SkinSelectMenu::setLeftLabel(const wstring &label) +{ + if(label.compare(m_leftLabel) != 0) + { + m_leftLabel = label; + + IggyDataValue result; + IggyDataValue value[1]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetLeftLabel , 1 , value ); + } +} + +void UIScene_SkinSelectMenu::setCentreLabel(const wstring &label) +{ + if(label.compare(m_centreLabel) != 0) + { + m_centreLabel = label; + + IggyDataValue result; + IggyDataValue value[1]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetCentreLabel , 1 , value ); + } +} + +void UIScene_SkinSelectMenu::setRightLabel(const wstring &label) +{ + if(label.compare(m_rightLabel) != 0) + { + m_rightLabel = label; + + IggyDataValue result; + IggyDataValue value[1]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)label.c_str(); + stringVal.length = label.length(); + + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetRightLabel , 1 , value ); + } +} + +#ifdef __PSVITA__ +void UIScene_SkinSelectMenu::handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased) +{ + if(bPressed) + { + switch(iId) + { + case ETouchInput_TabLeft: + case ETouchInput_TabRight: + case ETouchInput_TabCenter: + // change to pack navigation if not already there! + if(m_currentNavigation != eSkinNavigation_Pack) + { + ui.PlayUISFX(eSFX_Scroll); + m_currentNavigation = eSkinNavigation_Pack; + sendInputToMovie(ACTION_MENU_UP, false, true, false); + } + break; + case ETouchInput_IggyCharacters: + if(m_packIndex == SKIN_SELECT_PACK_FAVORITES) + { + if(app.GetPlayerFavoriteSkinsCount(m_iPad)==0) + { + // ignore this, since there are no skins being displayed + break; + } + } + // change to skin navigation if not already there! + if(m_currentNavigation != eSkinNavigation_Skin) + { + ui.PlayUISFX(eSFX_Scroll); + m_currentNavigation = eSkinNavigation_Skin; + sendInputToMovie(ACTION_MENU_DOWN, false, true, false); + } + // remember touch x start + m_iTouchXStart = x; + m_bTouchScrolled = false; + break; + } + } + else if(bRepeat) + { + switch(iId) + { + case ETouchInput_TabLeft: + /* no action */ + break; + case ETouchInput_TabRight: + /* no action */ + break; + case ETouchInput_IggyCharacters: + if(m_currentNavigation != eSkinNavigation_Skin) + { + // not in skin select mode + break; + } + if(x < m_iTouchXStart - 50) + { + if(!m_bAnimatingMove && !m_bTouchScrolled) + { + ui.PlayUISFX(eSFX_Scroll); + m_skinIndex = getNextSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Right, true); + m_characters[eCharacter_Next1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward, true); + + // 4J Stu - Swapped nav buttons + sendInputToMovie(ACTION_MENU_LEFT, false, true, false); + + m_bTouchScrolled = true; + } + } + else if(x > m_iTouchXStart + 50) + { + if(!m_bAnimatingMove && !m_bTouchScrolled) + { + ui.PlayUISFX(eSFX_Scroll); + + m_skinIndex = getPreviousSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_characters[eCharacter_Current].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Left, true); + m_characters[eCharacter_Previous1].SetFacing(UIControl_PlayerSkinPreview::e_SkinPreviewFacing_Forward, true); + + // 4J Stu - Swapped nav buttons + sendInputToMovie(ACTION_MENU_RIGHT, false, true, false); + + m_bTouchScrolled = true; + } + } + break; + } + } + else if(bReleased) + { + switch(iId) + { + case ETouchInput_TabLeft: + if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getPreviousPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + break; + case ETouchInput_TabRight: + if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getNextPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + break; + case ETouchInput_IggyCharacters: + if(!m_bTouchScrolled) + { + InputActionOK(iPad); + } + break; + } + } +} +#endif + +void UIScene_SkinSelectMenu::HandleDLCInstalled() +{ +#ifdef __PSVITA__ + EnterCriticalSection(&m_DLCInstallCS); // to prevent a race condition between the install and the mounted callback +#endif + + app.DebugPrintf(4,"UIScene_SkinSelectMenu::HandleDLCInstalled\n"); + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + app.DebugPrintf(4,"UIScene_SkinSelectMenu::HandleDLCInstalled - not doing a mount, so re-enable input\n"); + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_controlTimer.setVisible( true ); + m_controlIggyCharacters.setVisible( false ); + m_controlSkinNamePlate.setVisible( false ); + } + + // this will send a CustomMessage_DLCMountingComplete when done + +#ifdef __PSVITA__ + LeaveCriticalSection(&m_DLCInstallCS); +#endif + +} + + +void UIScene_SkinSelectMenu::HandleDLCMountingComplete() +{ +#ifdef __PSVITA__ + EnterCriticalSection(&m_DLCInstallCS); // to prevent a race condition between the install and the mounted callback +#endif + app.DebugPrintf(4,"UIScene_SkinSelectMenu::HandleDLCMountingComplete\n"); + m_controlTimer.setVisible( false ); + m_controlIggyCharacters.setVisible( true ); + m_controlSkinNamePlate.setVisible( true ); + + m_packIndex = SKIN_SELECT_PACK_DEFAULT; + + if(app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin)>0) + { + m_currentPack = app.m_dlcManager.getPackContainingSkin(m_currentSkinPath); + if(m_currentPack != NULL) + { + bool bFound = false; + m_packIndex = app.m_dlcManager.getPackIndex(m_currentPack,bFound,DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + } + } + + // If we have any favourites, set this to the favourites + // first validate the favorite skins - we might have uninstalled the DLC needed for them + app.ValidateFavoriteSkins(m_iPad); + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + m_packIndex = SKIN_SELECT_PACK_FAVORITES; + } + + handlePackIndexChanged(); + + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + bool bInGame=(Minecraft::GetInstance()->level!=NULL); + +#if TO_BE_IMPLEMENTED + if(bInGame) XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); +#endif +#ifdef __PSVITA__ + LeaveCriticalSection(&m_DLCInstallCS); +#endif +} + +void UIScene_SkinSelectMenu::showNotOnlineDialog(int iPad) +{ + // need to be signed in to live. get them to sign in to online +#if defined(__PS3__) + SQRNetworkManager_PS3::AttemptPSNSignIn(NULL, this); + +#elif defined(__PSVITA__) + SQRNetworkManager_Vita::AttemptPSNSignIn(NULL, this); + +#elif defined(__ORBIS__) + SQRNetworkManager_Orbis::AttemptPSNSignIn(NULL, this, false, iPad); + +#elif defined(_DURANGO) + + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad, NULL, NULL, app.GetStringTable() ); + +#endif +} + +int UIScene_SkinSelectMenu::UnlockSkinReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_SkinSelectMenu* pScene = (UIScene_SkinSelectMenu*)pParam; + + if ( (result == C4JStorage::EMessage_ResultAccept) + && ProfileManager.IsSignedIn(iPad) + ) + { + if (ProfileManager.IsSignedInLive(iPad)) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined __PSVITA__ + // need to get info on the pack to see if the user has already downloaded it + + // retrieve the store name for the skin pack + wstring wStrPackName=pScene->m_currentPack->getName(); + const char *pchPackName=wstringtofilename(wStrPackName); + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo((char *)pchPackName); + + if (pSONYDLCInfo != NULL) + { + char chName[42]; + char chKeyName[20]; + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + // find the info on the skin pack + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + // So we assume the first sku for the product is the one we want + + // while the store is screwed, hardcode the sku + //sprintf(chName,"%s-%s-%s",app.GetCommerceCategory(),pSONYDLCInfo->chDLCKeyname,"EURO"); + + // MGH - keyname in the DLC file is 16 chars long, but there's no space for a NULL terminating char + memset(chKeyName, 0, sizeof(chKeyName)); + strncpy(chKeyName, pSONYDLCInfo->chDLCKeyname, 16); + +#ifdef __ORBIS__ + strcpy(chName, chKeyName); +#else + sprintf(chName,"%s-%s",app.GetCommerceCategory(),chKeyName); +#endif + app.GetDLCSkuIDFromProductList(chName,chSkuID); + +#if defined __ORBIS__ || defined __PSVITA__ || defined __PS3__ + if (app.CheckForEmptyStore(iPad) == false) +#endif + { + if (app.DLCAlreadyPurchased(chSkuID)) + { + app.DebugPrintf("Already purchased this DLC - DownloadAlreadyPurchased \n"); + app.DownloadAlreadyPurchased(chSkuID); + } + else + { + app.DebugPrintf("Not yet purchased this DLC - Checkout \n"); + app.Checkout(chSkuID); + } + } + // need to re-enable input because the user can back out of the store purchase, and we'll be stuck + pScene->m_bIgnoreInput = false; + } +#elif defined _XBOX_ONE + StorageManager.InstallOffer(1,(WCHAR *)(pScene->m_currentPack->getPurchaseOfferId().c_str()), &RenableInput, pScene, NULL); +#endif + } + else // Is signed in, but not live. + { + pScene->showNotOnlineDialog(iPad); + pScene->m_bIgnoreInput = false; + } + } + else + { + pScene->m_bIgnoreInput = false; + } + + return 0; +} + +int UIScene_SkinSelectMenu::RenableInput(LPVOID lpVoid, int, int) +{ + ((UIScene_SkinSelectMenu*) lpVoid)->m_bIgnoreInput = false; + return 0; +} + +void UIScene_SkinSelectMenu::AddFavoriteSkin(int iPad,int iSkinID) +{ + // Is this favorite skin already in the array? + unsigned int uiCurrentFavoriteSkinsCount=app.GetPlayerFavoriteSkinsCount(iPad); + + for(int i=0;i0) + { + ucPos++; + } + else + { + ucPos=0; + } + } + + app.SetPlayerFavoriteSkin(iPad,(int)ucPos,iSkinID); + app.SetPlayerFavoriteSkinsPos(m_iPad,ucPos); +} + + +void UIScene_SkinSelectMenu::handleReload() +{ + // Reinitialise a few values to prevent problems on reload + m_bIgnoreInput=false; + + m_currentNavigation = eSkinNavigation_Skin; + m_currentPackCount = 0; + + m_labelSkinName.init(L""); + m_labelSkinOrigin.init(L""); + + m_leftLabel = L""; + m_centreLabel = L""; + m_rightLabel = L""; + + handlePackIndexChanged(); +} + +#ifdef _XBOX_ONE +void UIScene_SkinSelectMenu::HandleDLCLicenseChange() +{ + // update the lock flag + handleSkinIndexChanged(); +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.h b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.h new file mode 100644 index 0000000..c9ed669 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_SkinSelectMenu.h @@ -0,0 +1,189 @@ +#pragma once +#include "..\..\..\Minecraft.World\Definitions.h" +#include "UIScene.h" +#include "UIControl_PlayerSkinPreview.h" + +class UIScene_SkinSelectMenu : public UIScene +{ +private: + static WCHAR *wchDefaultNamesA[eDefaultSkins_Count]; + + // 4J Stu - How many to show on each side of the main control + static const BYTE sidePreviewControls = 4; + +#ifdef __PSVITA__ + enum ETouchInput + { + ETouchInput_TabLeft = 10, + ETouchInput_TabRight, + ETouchInput_TabCenter, + ETouchInput_IggyCharacters, + + ETouchInput_Count, + }; +#endif + + enum ESkinSelectNavigation + { + eSkinNavigation_Pack, + eSkinNavigation_Skin, + + eSkinNavigation_Count, + }; + + enum ECharacters + { + eCharacter_Current, + eCharacter_Next1, + eCharacter_Next2, + eCharacter_Next3, + eCharacter_Next4, + eCharacter_Previous1, + eCharacter_Previous2, + eCharacter_Previous3, + eCharacter_Previous4, + + eCharacter_COUNT, + }; + + UIControl_PlayerSkinPreview m_characters[eCharacter_COUNT]; + UIControl_Label m_labelSkinName, m_labelSkinOrigin; + UIControl_Label m_labelSelected; + UIControl m_controlSkinNamePlate, m_controlSelectedPanel, m_controlIggyCharacters, m_controlTimer; +#ifdef __PSVITA__ + UIControl_Touch m_TouchTabLeft, m_TouchTabRight, m_TouchTabCenter, m_TouchIggyCharacters; +#endif + IggyName m_funcSetPlayerCharacterSelected, m_funcSetCharacterLocked; + IggyName m_funcSetLeftLabel, m_funcSetRightLabel, m_funcSetCentreLabel; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) +#ifdef __PSVITA__ + UI_MAP_ELEMENT( m_TouchTabLeft, "TouchTabLeft" ) + UI_MAP_ELEMENT( m_TouchTabRight, "TouchTabRight" ) + UI_MAP_ELEMENT( m_TouchTabCenter, "TouchTabCenter" ) + UI_MAP_ELEMENT( m_TouchIggyCharacters, "TouchIggyCharacters" ) +#endif + UI_MAP_ELEMENT( m_controlSkinNamePlate, "SkinNamePlate") + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlSkinNamePlate ) + UI_MAP_ELEMENT( m_labelSkinName, "SkinTitle1") + UI_MAP_ELEMENT( m_labelSkinOrigin, "SkinTitle2") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_controlSelectedPanel, "SelectedPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlSelectedPanel ) + UI_MAP_ELEMENT( m_labelSelected, "SelectedPanelLabel" ) + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_controlTimer, "Timer" ) + + // 4J Stu - These aren't really used a AS3 controls, but adding here means that they get ticked by the scene + UI_MAP_ELEMENT( m_controlIggyCharacters, "IggyCharacters" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlIggyCharacters ) + UI_MAP_ELEMENT( m_characters[eCharacter_Current], "iggy_Character0" ) + + UI_MAP_ELEMENT( m_characters[eCharacter_Next1], "iggy_Character1" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Next2], "iggy_Character2" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Next3], "iggy_Character3" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Next4], "iggy_Character4" ) + + UI_MAP_ELEMENT( m_characters[eCharacter_Previous1], "iggy_Character5" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Previous2], "iggy_Character6" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Previous3], "iggy_Character7" ) + UI_MAP_ELEMENT( m_characters[eCharacter_Previous4], "iggy_Character8" ) + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME( m_funcSetPlayerCharacterSelected, L"SetPlayerCharacterSelected" ) + UI_MAP_NAME( m_funcSetCharacterLocked, L"SetCharacterLocked" ) + + UI_MAP_NAME( m_funcSetLeftLabel, L"SetLeftLabel" ) + UI_MAP_NAME( m_funcSetCentreLabel, L"SetCenterLabel" ) + UI_MAP_NAME( m_funcSetRightLabel, L"SetRightLabel" ) + UI_END_MAP_ELEMENTS_AND_NAMES() + + DLCPack *m_currentPack; + DWORD m_packIndex, m_skinIndex; + DWORD m_originalSkinId; + wstring m_currentSkinPath, m_selectedSkinPath, m_selectedCapePath; + vector *m_vAdditionalSkinBoxes; + + bool m_bSlidingSkins, m_bAnimatingMove; + ESkinSelectNavigation m_currentNavigation; + + bool m_bNoSkinsToShow; + DWORD m_currentPackCount; + bool m_bIgnoreInput; + bool m_bSkinIndexChanged; + wstring m_leftLabel, m_centreLabel, m_rightLabel; + + S32 m_iTouchXStart; + bool m_bTouchScrolled; +public: + UIScene_SkinSelectMenu(int iPad, void *initData, UILayer *parentLayer); +#ifdef __PSVITA__ + virtual ~UIScene_SkinSelectMenu() { DeleteCriticalSection(&m_DLCInstallCS); } +#endif + + virtual void tick(); + + virtual void updateTooltips(); + virtual void updateComponents(); + + virtual EUIScene getSceneType() { return eUIScene_SkinSelectMenu;} + + virtual void handleAnimationEnd(); + + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void customDraw(IggyCustomDrawCallbackRegion *region); + +private: + void handleSkinIndexChanged(); + int getNextSkinIndex(DWORD sourceIndex); + int getPreviousSkinIndex(DWORD sourceIndex); + + TEXTURE_NAME getTextureId(int skinIndex); + + void handlePackIndexChanged(); + void updatePackDisplay(); + int getNextPackIndex(DWORD sourceIndex); + int getPreviousPackIndex(DWORD sourceIndex); + + void setCharacterSelected(bool selected); + void setCharacterLocked(bool locked); + + void setLeftLabel(const wstring &label); + void setCentreLabel(const wstring &label); + void setRightLabel(const wstring &label); + + virtual void HandleDLCMountingComplete(); + virtual void HandleDLCInstalled(); +#ifdef _XBOX_ONE + virtual void HandleDLCLicenseChange(); +#endif + + void showNotOnlineDialog(int iPad); + + static int UnlockSkinReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int RenableInput(LPVOID lpVoid, int, int); + void AddFavoriteSkin(int iPad,int iSkinID); + + void InputActionOK(unsigned int iPad); +#ifdef __PSVITA__ + virtual void handleTouchInput(unsigned int iPad, S32 x, S32 y, int iId, bool bPressed, bool bRepeat, bool bReleased); +#endif //__PSVITA__ + virtual void handleReload(); + +#ifdef __ORBIS__ + bool m_bErrorDialogRunning; +#endif + +#ifdef __PSVITA__ + CRITICAL_SECTION m_DLCInstallCS; // to prevent a race condition between the install and the mounted callback +#endif +}; diff --git a/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp new file mode 100644 index 0000000..f6916d1 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.cpp @@ -0,0 +1,344 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_TeleportMenu.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "TeleportCommand.h" + +UIScene_TeleportMenu::UIScene_TeleportMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + TeleportMenuInitData *initParam = (TeleportMenuInitData *)initData; + + m_teleportToPlayer = initParam->teleportToPlayer; + + delete initParam; + + if(m_teleportToPlayer) + { + m_labelTitle.init(app.GetString(IDS_TELEPORT_TO_PLAYER)); + } + else + { + m_labelTitle.init(app.GetString(IDS_TELEPORT_TO_ME)); + } + + m_playerList.init(eControl_GamePlayers); + + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + m_playerNames[i] = L""; + } + + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL && !(player->IsLocal() && player->GetUserIndex() == m_iPad) ) + { + m_players[m_playersCount] = player->GetSmallId(); + ++m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( player->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + m_playersVoiceState[m_playersCount] = voiceStatus; + m_playersColourState[m_playersCount] = app.GetPlayerColour( m_players[m_playersCount] ); + m_playerNames[m_playersCount] = playerName; + m_playerList.addItem( playerName, app.GetPlayerColour( m_players[m_playersCount] ), voiceStatus); + } + } + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &UIScene_TeleportMenu::OnPlayerChanged, this); + + parentLayer->addComponent(iPad,eUIComponent_MenuBackground); + + // get rid of the quadrant display if it's on + ui.HidePressStart(); +} + +wstring UIScene_TeleportMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"InGameTeleportMenuSplit"; + } + else + { + return L"InGameTeleportMenu"; + } +} + +void UIScene_TeleportMenu::updateTooltips() +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); +} + +void UIScene_TeleportMenu::handleDestroy() +{ + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &UIScene_TeleportMenu::OnPlayerChanged, this); + + m_parentLayer->removeComponent(eUIComponent_MenuBackground); +} + +void UIScene_TeleportMenu::handleGainFocus(bool navBack) +{ + if( navBack ) g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &UIScene_TeleportMenu::OnPlayerChanged, this); +} + +void UIScene_TeleportMenu::handleReload() +{ + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL && !(player->IsLocal() && player->GetUserIndex() == m_iPad) ) + { + m_players[m_playersCount] = player->GetSmallId(); + ++m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( player->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + m_playersVoiceState[m_playersCount] = voiceStatus; + m_playersColourState[m_playersCount] = app.GetPlayerColour( m_players[m_playersCount] ); + m_playerNames[m_playersCount] = playerName; + m_playerList.addItem( playerName, app.GetPlayerColour( m_players[m_playersCount] ), voiceStatus); + } + } + + if(controlHasFocus(eControl_GamePlayers)) + { + m_playerList.setCurrentSelection(getControlChildFocus()); + } +} + +void UIScene_TeleportMenu::tick() +{ + UIScene::tick(); + + for(DWORD i = 0; i < m_playersCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId( m_players[i] ); + + if( player != NULL ) + { + m_players[i] = player->GetSmallId(); + + short icon = app.GetPlayerColour( m_players[i] ); + + if(icon != m_playersColourState[i]) + { + m_playersColourState[i] = icon; + m_playerList.setPlayerIcon( i, (int)app.GetPlayerColour( m_players[i] ) ); + } + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + if(playerName.compare( m_playerNames[i] ) != 0 ) + { + m_playerList.setButtonLabel(i, playerName); + m_playerNames[i] = playerName; + } + } + } +} + +void UIScene_TeleportMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + if(pressed && !repeat) + { + ui.PlayUISFX(eSFX_Back); + navigateBack(); + } + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + case ACTION_MENU_UP: + case ACTION_MENU_DOWN: + case ACTION_MENU_PAGEUP: + case ACTION_MENU_PAGEDOWN: + sendInputToMovie(key, repeat, pressed, released); + break; + } +} + +void UIScene_TeleportMenu::handlePress(F64 controlId, F64 childId) +{ + app.DebugPrintf("Pressed = %d, %d\n", (int)controlId, (int)childId); + switch((int)controlId) + { + case eControl_GamePlayers: + int currentSelection = (int)childId; + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ currentSelection ] ); + INetworkPlayer *thisPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(m_iPad); + + shared_ptr packet; + if(m_teleportToPlayer) + { + packet = TeleportCommand::preparePacket(thisPlayer->GetUID(),selectedPlayer->GetUID()); + } + else + { + packet = TeleportCommand::preparePacket(selectedPlayer->GetUID(),thisPlayer->GetUID()); + } + ClientConnection *conn = Minecraft::GetInstance()->getConnection(m_iPad); + conn->send( packet ); + break; + } +} + +void UIScene_TeleportMenu::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + UIScene_TeleportMenu *scene = (UIScene_TeleportMenu *)callbackParam; + bool playerFound = false; + int foundIndex = 0; + for(int i = 0; i < scene->m_playersCount; ++i) + { + if(!playerFound && scene->m_players[i] == pPlayer->GetSmallId() ) + { + if( scene->m_playerList.getCurrentSelection() == scene->m_playerList.getItemCount() - 1 ) + { + scene->m_playerList.setCurrentSelection( scene->m_playerList.getItemCount() - 2 ); + } + // Player removed + playerFound = true; + foundIndex = i; + } + } + + if( playerFound ) + { + --scene->m_playersCount; + scene->m_playersVoiceState[scene->m_playersCount] = 0; + scene->m_playersColourState[scene->m_playersCount] = 0; + scene->m_playerNames[scene->m_playersCount] = L""; + scene->m_playerList.removeItem(scene->m_playersCount); + } + + if( !playerFound ) + { + // Player added + scene->m_players[scene->m_playersCount] = pPlayer->GetSmallId(); + ++scene->m_playersCount; + + wstring playerName = L""; +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<GetDisplayName(); + } + + int voiceStatus = 0; + if(pPlayer != NULL && pPlayer->HasVoice() ) + { + if( pPlayer->IsMutedByLocalUser(scene->m_iPad) ) + { + // Muted image + voiceStatus = 3; + } + else if( pPlayer->IsTalking() ) + { + // Talking image + voiceStatus = 2; + } + else + { + // Not talking image + voiceStatus = 1; + } + } + + scene->m_playerList.addItem( playerName, app.GetPlayerColour( scene->m_players[scene->m_playersCount - 1] ), voiceStatus); + } +} diff --git a/Minecraft.Client/Common/UI/UIScene_TeleportMenu.h b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.h new file mode 100644 index 0000000..ebbaa2a --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TeleportMenu.h @@ -0,0 +1,51 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_TeleportMenu : public UIScene +{ +private: + enum EControls + { + eControl_GamePlayers, + }; + + bool m_teleportToPlayer; + int m_playersCount; + BYTE m_players[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's + char m_playersVoiceState[MINECRAFT_NET_MAX_PLAYERS]; + short m_playersColourState[MINECRAFT_NET_MAX_PLAYERS]; + wstring m_playerNames[MINECRAFT_NET_MAX_PLAYERS]; + + UIControl_PlayerList m_playerList; + UIControl_Label m_labelTitle; + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_playerList, "GamePlayers") + UI_MAP_ELEMENT( m_labelTitle, "Title") + UI_END_MAP_ELEMENTS_AND_NAMES() +public: + UIScene_TeleportMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_TeleportMenu;} + + virtual void updateTooltips(); + virtual void handleReload(); + + virtual void tick(); + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + +protected: + virtual void handleGainFocus(bool navBack); + void handlePress(F64 controlId, F64 childId); + virtual void handleDestroy(); + +public: + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_Timer.cpp b/Minecraft.Client/Common/UI/UIScene_Timer.cpp new file mode 100644 index 0000000..61586e8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Timer.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_Timer.h" + + +UIScene_Timer::UIScene_Timer(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + // In normal usage, we want to hide the new background that's used during texture pack reloading + if(initData == 0) + { + m_controlBackground.setVisible(false); + } +} + +wstring UIScene_Timer::getMoviePath() +{ + return L"Timer"; +} + +void UIScene_Timer::reloadMovie() +{ + // Never needs reloaded +} + +bool UIScene_Timer::needsReloaded() +{ + // Never needs reloaded + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_Timer.h b/Minecraft.Client/Common/UI/UIScene_Timer.h new file mode 100644 index 0000000..5a75103 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_Timer.h @@ -0,0 +1,28 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_Timer : public UIScene +{ +private: + UIControl m_controlBackground; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT(m_controlBackground,"Background") + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + using UIScene::reloadMovie; + + UIScene_Timer(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_Timer;} + + // Returns true if lower scenes in this scenes layer, or in any layer below this scenes layers should be hidden + virtual bool hidesLowerScenes() { return true; } + virtual void reloadMovie(); + virtual bool needsReloaded(); + +protected: + virtual wstring getMoviePath(); +}; diff --git a/Minecraft.Client/Common/UI/UIScene_TradingMenu.cpp b/Minecraft.Client/Common/UI/UIScene_TradingMenu.cpp new file mode 100644 index 0000000..dc2bac4 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TradingMenu.cpp @@ -0,0 +1,268 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.trading.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "MultiPlayerLocalPlayer.h" +#include "..\..\Minecraft.h" +#include "UIScene_TradingMenu.h" + +UIScene_TradingMenu::UIScene_TradingMenu(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); + + m_showingLeftArrow = true; + m_showingRightArrow = true; + + // 4J-PB - "Villager" appears for a short time on opening the trading menu + //m_labelTrading.init( app.GetString(IDS_VILLAGER) ); + m_labelTrading.init( L"" ); + m_labelInventory.init( app.GetString(IDS_INVENTORY) ); + m_labelRequired.init( app.GetString(IDS_REQUIRED_ITEMS_FOR_TRADE) ); + + m_labelRequest1.init(L""); + m_labelRequest2.init(L""); + + TradingScreenInput *initData = (TradingScreenInput *)_initData; + m_merchant = initData->trader; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Trading_Menu, this); + } + + m_menu = new MerchantMenu( initData->inventory, initData->trader, initData->level ); + + Minecraft::GetInstance()->localplayers[iPad]->containerMenu = m_menu; + + m_slotListRequest1.addSlots(BUY_A,1); + m_slotListRequest2.addSlots(BUY_B,1); + + m_slotListTrades.addSlots(TRADES_START,DISPLAY_TRADES_COUNT); + + m_slotListInventory.addSlots(MerchantMenu::INV_SLOT_START, 27); + m_slotListHotbar.addSlots(MerchantMenu::USE_ROW_SLOT_START, 9); + + if(initData) delete initData; + + // in this scene, we override the press sound with our own for crafting success or fail + ui.OverrideSFX(m_iPad,ACTION_MENU_A,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,true); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,true); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,true); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,true); + + app.SetRichPresenceContext(iPad, CONTEXT_GAME_STATE_TRADING); +} + +wstring UIScene_TradingMenu::getMoviePath() +{ + if(app.GetLocalPlayerCount() > 1) + { + return L"TradingMenuSplit"; + } + else + { + return L"TradingMenu"; + } +} + +void UIScene_TradingMenu::updateTooltips() +{ + ui.SetTooltips(m_iPad, IDS_TOOLTIPS_TRADE, IDS_TOOLTIPS_BACK); +} + +void UIScene_TradingMenu::handleDestroy() +{ + app.DebugPrintf("UIScene_TradingMenu::handleDestroy\n"); + Minecraft *pMinecraft = Minecraft::GetInstance(); + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(pMinecraft->localplayers[m_iPad] != NULL) pMinecraft->localplayers[m_iPad]->closeContainer(); + + ui.OverrideSFX(m_iPad,ACTION_MENU_A,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_OK,false); +#ifdef __ORBIS__ + ui.OverrideSFX(m_iPad,ACTION_MENU_TOUCHPAD_PRESS,false); +#endif + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT_SCROLL,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_LEFT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_RIGHT,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_UP,false); + ui.OverrideSFX(m_iPad,ACTION_MENU_DOWN,false); +} + +void UIScene_TradingMenu::handleReload() +{ + m_slotListRequest1.addSlots(BUY_A,1); + m_slotListRequest2.addSlots(BUY_B,1); + + m_slotListTrades.addSlots(TRADES_START,DISPLAY_TRADES_COUNT); + + m_slotListInventory.addSlots(MerchantMenu::INV_SLOT_START, 27); + m_slotListHotbar.addSlots(MerchantMenu::USE_ROW_SLOT_START, 9); +} + +void UIScene_TradingMenu::tick() +{ + UIScene::tick(); + handleTick(); +} + +void UIScene_TradingMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_InventoryMenu handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + default: + if(pressed) + { + handled = handleKeyDown(m_iPad, key, repeat); + } + break; + }; +} + +void UIScene_TradingMenu::customDraw(IggyCustomDrawCallbackRegion *region) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return; + + shared_ptr item = nullptr; + int slotId = -1; + swscanf((wchar_t*)region->name,L"slot_%d",&slotId); + + if(slotId < MerchantMenu::USE_ROW_SLOT_END) + { + Slot *slot = m_menu->getSlot(slotId); + item = slot->getItem(); + } + else if(slotId >= TRADES_START) + { + int tradeId = (slotId - TRADES_START) + m_offersStartIndex; + if(tradeId < m_activeOffers.size()) + { + item = m_activeOffers.at(tradeId).first->getSellItem(); + } + } + else + { + int tradeId = m_selectedSlot + m_offersStartIndex; + if( tradeId < m_activeOffers.size() ) + { + switch(slotId) + { + case BUY_A: + item = m_activeOffers.at(tradeId).first->getBuyAItem(); + break; + case BUY_B: + item = m_activeOffers.at(tradeId).first->getBuyBItem(); + break; + }; + } + } + if(item != NULL) customDrawSlotControl(region,m_iPad,item,1.0f,item->isFoil(),true); +} + +void UIScene_TradingMenu::showScrollRightArrow(bool show) +{ + if(m_showingRightArrow != show) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowScrollRightArrow , 1 , value ); + + m_showingRightArrow = show; + } +} + +void UIScene_TradingMenu::showScrollLeftArrow(bool show) +{ + if(m_showingLeftArrow != show) + { + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = show; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcShowScrollLeftArrow , 1 , value ); + + m_showingLeftArrow = show; + } +} + +void UIScene_TradingMenu::moveSelector(bool right) +{ + IggyDataValue result; + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + value[0].boolval = right; + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcMoveSelector , 1 , value ); +} + +void UIScene_TradingMenu::setTitle(const wstring &name) +{ + m_labelTrading.setLabel(name); +} + +void UIScene_TradingMenu::setRequest1Name(const wstring &name) +{ + m_labelRequest1.setLabel(name); +} + +void UIScene_TradingMenu::setRequest2Name(const wstring &name) +{ + m_labelRequest2.setLabel(name); +} + +void UIScene_TradingMenu::setRequest1RedBox(bool show) +{ + m_slotListRequest1.showSlotRedBox(0,show); +} + +void UIScene_TradingMenu::setRequest2RedBox(bool show) +{ + m_slotListRequest2.showSlotRedBox(0,show); +} + +void UIScene_TradingMenu::setTradeRedBox(int index, bool show) +{ + m_slotListTrades.showSlotRedBox(index,show); +} + +void UIScene_TradingMenu::setOfferDescription(const wstring &name, vector &unformattedStrings) +{ + IggyDataValue result; + IggyDataValue value[1]; + + IggyStringUTF16 stringVal; + stringVal.string = (IggyUTF16*)name.c_str(); + stringVal.length = name.length(); + value[0].type = IGGY_DATATYPE_string_UTF16; + value[0].string16 = stringVal; + + IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcSetOfferDescription , 1 , value ); +} diff --git a/Minecraft.Client/Common/UI/UIScene_TradingMenu.h b/Minecraft.Client/Common/UI/UIScene_TradingMenu.h new file mode 100644 index 0000000..08263a1 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TradingMenu.h @@ -0,0 +1,78 @@ +#pragma once + +#include "IUIScene_TradingMenu.h" + +class InventoryMenu; + +class UIScene_TradingMenu : public UIScene, public IUIScene_TradingMenu +{ +private: + bool m_showingRightArrow, m_showingLeftArrow; + +public: + UIScene_TradingMenu(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_TradingMenu;} + +protected: + UIControl m_controlMainPanel; + UIControl_SlotList m_slotListTrades; + UIControl_SlotList m_slotListRequest1, m_slotListRequest2; + UIControl_SlotList m_slotListHotbar, m_slotListInventory; + UIControl_Label m_labelInventory; + UIControl_Label m_labelTrading, m_labelRequired; + UIControl_Label m_labelRequest1, m_labelRequest2; + + IggyName m_funcMoveSelector, m_funcShowScrollRightArrow, m_funcShowScrollLeftArrow, m_funcSetOfferDescription; + + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel" ) + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlMainPanel ) + UI_MAP_ELEMENT( m_slotListTrades, "TradingBar") + UI_MAP_ELEMENT( m_slotListRequest1, "Request1") + UI_MAP_ELEMENT( m_slotListRequest2, "Request2") + + UI_MAP_ELEMENT( m_labelTrading, "VillagerText") + UI_MAP_ELEMENT( m_labelRequired, "RequiredLabel") + + UI_MAP_ELEMENT( m_labelRequest1, "Request1Label") + UI_MAP_ELEMENT( m_labelRequest2, "Request2Label") + + UI_MAP_ELEMENT( m_slotListHotbar, "HotBar") + UI_MAP_ELEMENT( m_slotListInventory, "Inventory") + UI_MAP_ELEMENT( m_labelInventory, "InventoryLabel") + + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_NAME(m_funcMoveSelector, L"MoveSelector") + UI_MAP_NAME(m_funcShowScrollRightArrow, L"ShowScrollRightArrow") + UI_MAP_NAME(m_funcShowScrollLeftArrow, L"ShowScrollLeftArrow") + UI_MAP_NAME(m_funcSetOfferDescription, L"SetOfferDescription") + UI_END_MAP_ELEMENTS_AND_NAMES() + + virtual wstring getMoviePath(); + virtual void updateTooltips(); + virtual void handleDestroy(); + virtual void handleReload(); + + virtual void tick(); + + void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + void customDraw(IggyCustomDrawCallbackRegion *region); + + virtual void showScrollRightArrow(bool show); + virtual void showScrollLeftArrow(bool show); + virtual void moveSelector(bool right); + virtual void setTitle(const wstring &name); + virtual void setRequest1Name(const wstring &name); + virtual void setRequest2Name(const wstring &name); + + virtual void setRequest1RedBox(bool show); + virtual void setRequest2RedBox(bool show); + virtual void setTradeRedBox(int index, bool show); + + virtual void setOfferDescription(const wstring &name, vector &unformattedStrings); + + int getPad() { return m_iPad; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.cpp b/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.cpp new file mode 100644 index 0000000..b395bc4 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "UI.h" +#include "UIScene_TrialExitUpsell.h" + + +UIScene_TrialExitUpsell::UIScene_TrialExitUpsell(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) +{ + // Setup all the Iggy references we need for this scene + initialiseMovie(); +} + +wstring UIScene_TrialExitUpsell::getMoviePath() +{ + return L"TrialExitUpsell"; +} + +void UIScene_TrialExitUpsell::updateTooltips() +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_EXIT_GAME,IDS_TOOLTIPS_BACK, IDS_UNLOCK_TITLE); +} + +void UIScene_TrialExitUpsell::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) +{ + //app.DebugPrintf("UIScene_DebugOverlay handling input for pad %d, key %d, down- %s, pressed- %s, released- %s\n", iPad, key, down?"TRUE":"FALSE", pressed?"TRUE":"FALSE", released?"TRUE":"FALSE"); + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + switch(key) + { + case ACTION_MENU_CANCEL: + navigateBack(); + break; + case ACTION_MENU_OK: +#ifdef __ORBIS__ + case ACTION_MENU_TOUCHPAD_PRESS: +#endif + if(pressed) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + app.ExitGame(); + } + break; + case ACTION_MENU_X: + if(ProfileManager.IsSignedIn(iPad)) + { + //CD - Added for audio + ui.PlayUISFX(eSFX_Press); + + // 4J-PB - need to check this user can access the store +#if defined(__PS3__) || defined(__PSVITA__) + bool bContentRestricted; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),true,NULL,&bContentRestricted,NULL); + if(bContentRestricted) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,this, app.GetStringTable()); + } + else +#endif + { + TelemetryManager->RecordUpsellPresented(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + ProfileManager.DisplayFullVersionPurchase(false,iPad,eSen_UpsellID_Full_Version_Of_Game); + } + } + break; + } +} + +void UIScene_TrialExitUpsell::handleAnimationEnd() +{ + //ui.NavigateToHomeMenu(); + ui.NavigateToScene(0,eUIScene_SaveMessage); +} diff --git a/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.h b/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.h new file mode 100644 index 0000000..79e9edf --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_TrialExitUpsell.h @@ -0,0 +1,31 @@ +#pragma once + +#include "UIScene.h" + +class UIScene_TrialExitUpsell : public UIScene +{ +private: + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_END_MAP_ELEMENTS_AND_NAMES() + +public: + UIScene_TrialExitUpsell(int iPad, void *initData, UILayer *parentLayer); + + virtual EUIScene getSceneType() { return eUIScene_TrialExitUpsell;} + + // Returns true if this scene has focus for the pad passed in +#ifndef __PS3__ + virtual bool hasFocus(int iPad) { return bHasFocus; } +#endif + virtual void updateTooltips(); + +protected: + virtual wstring getMoviePath(); + +public: + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + + virtual void handleAnimationEnd(); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UIStructs.h b/Minecraft.Client/Common/UI/UIStructs.h new file mode 100644 index 0000000..2dd03c8 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIStructs.h @@ -0,0 +1,409 @@ +#pragma once + +#pragma message("UIStructs.h") + +#include "UIEnums.h" + +class Container; +class Inventory; +class BrewingStandTileEntity; +class DispenserTileEntity; +class FurnaceTileEntity; +class SignTileEntity; +class LevelGenerationOptions; +class LocalPlayer; +class Merchant; + +// 4J Stu - Structs shared by Iggy and Xui scenes. +typedef struct _UIVec2D +{ + float x; + float y; + + _UIVec2D& operator+=(const _UIVec2D &rhs) + { + x += rhs.x; + y += rhs.y; + return *this; + } +} UIVec2D; + +// Brewing +typedef struct _BrewingScreenInput +{ + shared_ptr inventory; + shared_ptr brewingStand; + int iPad; + bool bSplitscreen; +} BrewingScreenInput; + +// Chest +typedef struct _ContainerScreenInput +{ + shared_ptr inventory; + shared_ptr container; + int iPad; + bool bSplitscreen; +} ContainerScreenInput; + +// Dispenser +typedef struct _TrapScreenInput +{ + shared_ptr inventory; + shared_ptr trap; + int iPad; + bool bSplitscreen; +} TrapScreenInput; + +// Inventory and creative inventory +typedef struct _InventoryScreenInput +{ + shared_ptr player; + bool bNavigateBack; // If we came here from the crafting screen, go back to it, rather than closing the xui menus + int iPad; + bool bSplitscreen; +} InventoryScreenInput; + +// Enchanting +typedef struct _EnchantingScreenInput +{ + shared_ptr inventory; + Level *level; + int x; + int y; + int z; + int iPad; + bool bSplitscreen; +} EnchantingScreenInput; + +// Furnace +typedef struct _FurnaceScreenInput +{ + shared_ptr inventory; + shared_ptr furnace; + int iPad; + bool bSplitscreen; +} FurnaceScreenInput; + +// Crafting +typedef struct _CraftingPanelScreenInput +{ + shared_ptr player; + int iContainerType; // RECIPE_TYPE_2x2 or RECIPE_TYPE_3x3 + bool bSplitscreen; + int iPad; + int x; + int y; + int z; +} +CraftingPanelScreenInput; + +// Trading +typedef struct _TradingScreenInput +{ + shared_ptr inventory; + shared_ptr trader; + Level *level; + int iPad; + bool bSplitscreen; +} +TradingScreenInput; + +// Anvil +typedef struct _AnvilScreenInput +{ + shared_ptr inventory; + Level *level; + int x; + int y; + int z; + int iPad; + bool bSplitscreen; +} +AnvilScreenInput; + +// Sign +typedef struct _SignEntryScreenInput +{ + shared_ptr sign; + int iPad; +} SignEntryScreenInput; + +// Connecting progress +typedef struct _ConnectionProgressParams +{ + int iPad; + int stringId; + bool showTooltips; + bool setFailTimer; + int timerTime; + void (*cancelFunc)(LPVOID param); + LPVOID cancelFuncParam; + + _ConnectionProgressParams() + { + iPad = 0; + stringId = -1; + showTooltips = false; + setFailTimer = false; + timerTime = 0; + cancelFunc = NULL; + cancelFuncParam = NULL; + } +} ConnectionProgressParams; + +// Fullscreen progress +typedef struct _UIFullscreenProgressCompletionData +{ + BOOL bRequiresUserAction; + BOOL bShowBackground; + BOOL bShowLogo; + BOOL bShowTips; + ProgressionCompletionType type; + int iPad; + EUIScene scene; + + _UIFullscreenProgressCompletionData() + { + bRequiresUserAction = FALSE; + bShowBackground = TRUE; + bShowLogo = TRUE; + bShowTips = TRUE; + type = e_ProgressCompletion_NoAction; + } +} UIFullscreenProgressCompletionData; + +// Create world +typedef struct _CreateWorldMenuInitData +{ + BOOL bOnline; + BOOL bIsPrivate; + int iPad; +} +CreateWorldMenuInitData; + +// Join/Load saves list +typedef struct _SaveListDetails +{ + int saveId; + PBYTE pbThumbnailData; + DWORD dwThumbnailSize; +#ifdef _DURANGO + wchar_t UTF16SaveName[128]; + wchar_t UTF16SaveFilename[MAX_SAVEFILENAME_LENGTH]; +#else + char UTF8SaveName[128]; +#ifndef _XBOX + char UTF8SaveFilename[MAX_SAVEFILENAME_LENGTH]; +#endif +#endif + + _SaveListDetails() + { + saveId = 0; + pbThumbnailData = NULL; + dwThumbnailSize = 0; +#ifdef _DURANGO + ZeroMemory(UTF16SaveName,sizeof(wchar_t)*128); + ZeroMemory(UTF16SaveFilename,sizeof(wchar_t)*MAX_SAVEFILENAME_LENGTH); +#else + ZeroMemory(UTF8SaveName,128); +#ifndef _XBOX + ZeroMemory(UTF8SaveFilename,MAX_SAVEFILENAME_LENGTH); +#endif +#endif + } + +} SaveListDetails; + +// Load world +typedef struct _LoadMenuInitData +{ + int iPad; + int iSaveGameInfoIndex; + LevelGenerationOptions *levelGen; + SaveListDetails *saveDetails; +} +LoadMenuInitData; + +// Join Games +typedef struct _JoinMenuInitData +{ + FriendSessionInfo *selectedSession; + int iPad; +} JoinMenuInitData; + +// More Options +typedef struct _LaunchMoreOptionsMenuInitData +{ + BOOL bOnlineGame; + BOOL bInviteOnly; + BOOL bAllowFriendsOfFriends; + + BOOL bGenerateOptions; + BOOL bStructures; + BOOL bFlatWorld; + BOOL bBonusChest; + + BOOL bPVP; + BOOL bTrust; + BOOL bFireSpreads; + BOOL bTNT; + + BOOL bHostPrivileges; + BOOL bResetNether; + + BOOL bOnlineSettingChangedBySystem; + + int iPad; + + DWORD dwTexturePack; + + wstring seed; + int worldSize; + bool bDisableSaving; + + _LaunchMoreOptionsMenuInitData() + { + memset(this,0,sizeof(_LaunchMoreOptionsMenuInitData)); + bOnlineGame = TRUE; + bAllowFriendsOfFriends = TRUE; + bPVP = TRUE; + bFireSpreads = TRUE; + bTNT = TRUE; + iPad = -1; + worldSize = 3; + seed = L""; + bDisableSaving = false; + } +} +LaunchMoreOptionsMenuInitData; + +typedef struct _LoadingInputParams +{ + C4JThreadStartFunc* func; + LPVOID lpParam; + UIFullscreenProgressCompletionData *completionData; + + int cancelText; + void (*cancelFunc)(LPVOID param); + void (*completeFunc)(LPVOID param); + LPVOID m_cancelFuncParam; + LPVOID m_completeFuncParam; + bool waitForThreadToDelete; + + _LoadingInputParams() + { + func = NULL; + lpParam = NULL; + completionData = NULL; + + cancelText = -1; + cancelFunc = NULL; + completeFunc = NULL; + m_cancelFuncParam = NULL; + m_completeFuncParam = NULL; + waitForThreadToDelete = false; + } +} LoadingInputParams; + +// Tutorial +#ifndef _XBOX +class UIScene; +#endif +class Tutorial; +typedef struct _TutorialPopupInfo +{ +#ifdef _XBOX + CXuiScene *interactScene; +#else + UIScene *interactScene; +#endif + LPCWSTR desc; + LPCWSTR title; + int icon; + int iAuxVal /* = 0 */; + bool isFoil /* = false */; + bool allowFade /* = true */; + bool isReminder /*= false*/; + Tutorial *tutorial; + + _TutorialPopupInfo() + { + interactScene = NULL; + desc = L""; + title = L""; + icon = -1; + iAuxVal = 0; + isFoil = false; + allowFade = true; + isReminder = false; + tutorial = NULL; + } + +} TutorialPopupInfo; + +// Quadrant sign in +typedef struct _SignInInfo +{ + int( *Func)(LPVOID,const bool, const int iPad); + LPVOID lpParam; + bool requireOnline; +} SignInInfo; + +// Credits +typedef struct +{ + LPCWSTR m_Text; // Should contain string, optionally with %s to add in translated string ... e.g. "Andy West - %s" + int m_iStringID[2]; // May be NO_TRANSLATED_STRING if we do not require to add any translated string. + ECreditTextTypes m_eType; +} +SCreditTextItemDef; + +// Message box +typedef struct _MessageBoxInfo +{ + UINT uiTitle; + UINT uiText; + UINT *uiOptionA; + UINT uiOptionC; + DWORD dwPad; + int( *Func)(LPVOID,int,const C4JStorage::EMessageResult); + LPVOID lpParam; + //C4JStringTable *pStringTable; // 4J Stu - We don't need this for our internal message boxes + WCHAR *pwchFormatString; + DWORD dwFocusButton; +} MessageBoxInfo; + +typedef struct _DLCOffersParam +{ + int iPad; + int iOfferC; + int iType; +} +DLCOffersParam; + +typedef struct _InGamePlayerOptionsInitData +{ + int iPad; + BYTE networkSmallId; + unsigned int playerPrivileges; +} InGamePlayerOptionsInitData; + +typedef struct _DebugSetCameraPosition +{ + int player; + double m_camX, m_camY, m_camZ, m_yRot, m_elev; +} DebugSetCameraPosition; + +typedef struct _TeleportMenuInitData +{ + int iPad; + bool teleportToPlayer; +} TeleportMenuInitData; + +typedef struct _CustomDrawData +{ + float x0, y0, x1, y1; // the bounding box of the original DisplayObject, in object space + float mat[16]; +} CustomDrawData; diff --git a/Minecraft.Client/Common/UI/UITTFFont.cpp b/Minecraft.Client/Common/UI/UITTFFont.cpp new file mode 100644 index 0000000..eb34338 --- /dev/null +++ b/Minecraft.Client/Common/UI/UITTFFont.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "UI.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\File.h" +#include "UITTFFont.h" + +UITTFFont::UITTFFont(const string &path, S32 fallbackCharacter) +{ + app.DebugPrintf("UITTFFont opening %s\n",path.c_str()); + +#ifdef _UNICODE + wstring wPath = convStringToWstring(path); + HANDLE file = CreateFile(wPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#else + HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + if( file == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + app.DebugPrintf("Failed to open TTF file with error code %d (%x)\n", error, error); + assert(false); + } + + DWORD dwHigh=0; + DWORD dwFileSize = GetFileSize(file,&dwHigh); + + if(dwFileSize!=0) + { + DWORD bytesRead; + + pbData = (PBYTE) new BYTE[dwFileSize]; + BOOL bSuccess = ReadFile(file,pbData,dwFileSize,&bytesRead,NULL); + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + CloseHandle(file); + + IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Mojangles_TTF", -1, IGGY_FONTFLAG_none ); + + IggyFontInstallTruetypeFallbackCodepointUTF8( "Mojangles_TTF", -1, IGGY_FONTFLAG_none, fallbackCharacter ); + + // 4J Stu - These are so we can use the default flash controls + IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Times New Roman", -1, IGGY_FONTFLAG_none ); + IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Arial", -1, IGGY_FONTFLAG_none ); + } +} + +UITTFFont::~UITTFFont() +{ +} \ No newline at end of file diff --git a/Minecraft.Client/Common/UI/UITTFFont.h b/Minecraft.Client/Common/UI/UITTFFont.h new file mode 100644 index 0000000..0de7c4e --- /dev/null +++ b/Minecraft.Client/Common/UI/UITTFFont.h @@ -0,0 +1,12 @@ +#pragma once + +class UITTFFont +{ +private: + PBYTE pbData; + //DWORD dwDataSize; + +public: + UITTFFont(const string &path, S32 fallbackCharacter); + ~UITTFFont(); +}; diff --git a/Minecraft.Client/Common/XUI/SlotProgressControl.cpp b/Minecraft.Client/Common/XUI/SlotProgressControl.cpp new file mode 100644 index 0000000..91f362a --- /dev/null +++ b/Minecraft.Client/Common/XUI/SlotProgressControl.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\Slot.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" + +#include "SlotItemControlBase.h" +#include "SlotProgressControl.h" + +int SlotProgressControl::GetValue() +{ + int value = 0; + + HXUIOBJ hVisual, hParent; + this->GetParent( &hVisual ); + XuiElementGetParent( hVisual, &hParent); + + void* pvUserData; + XuiElementGetUserData( hParent, &pvUserData ); + + if( pvUserData != NULL ) + { + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + shared_ptr item = shared_ptr(); + + if( pUserDataContainer->slot != NULL ) + { + item = pUserDataContainer->slot->getItem(); + } + else + { + item = pUserDataContainer->item; + } + + if( item != NULL ) + { + // TODO Should use getDamage instead even though it returns the same value + if( item->isDamaged() ) + value = item->getDamageValue(); + else + value = 0; + } + } + else + { + LPCWSTR name; + XuiElementGetId( hParent, &name ); + + OutputDebugStringW( name ); + OutputDebugString( "\n" ); + } + + return value; +} + +void SlotProgressControl::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = 0; + + HXUIOBJ hVisual, hParent; + this->GetParent( &hVisual ); + XuiElementGetParent( hVisual, &hParent); + + void* pvUserData; + XuiElementGetUserData( hParent, &pvUserData ); + + if( pvUserData != NULL ) + { + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + shared_ptr item = shared_ptr(); + + if( pUserDataContainer->slot != NULL ) + { + item = pUserDataContainer->slot->getItem(); + } + else + { + item = pUserDataContainer->item; + } + + if( item != NULL ) + { + *pnRangeMax = item->getMaxDamage(); + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/SlotProgressControl.h b/Minecraft.Client/Common/XUI/SlotProgressControl.h new file mode 100644 index 0000000..fb5ddcc --- /dev/null +++ b/Minecraft.Client/Common/XUI/SlotProgressControl.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ProgressControlBase.h" + +class SlotProgressControl : public ProgressControlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( SlotProgressControl, L"SlotProgressControl", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_BasePlayer.cpp b/Minecraft.Client/Common/XUI/XUI_BasePlayer.cpp new file mode 100644 index 0000000..2134807 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_BasePlayer.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include +#include "XUI_BasePlayer.h" + +HRESULT CXuiSceneBasePlayer::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + return S_OK; +} + diff --git a/Minecraft.Client/Common/XUI/XUI_BasePlayer.h b/Minecraft.Client/Common/XUI/XUI_BasePlayer.h new file mode 100644 index 0000000..fd777e6 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_BasePlayer.h @@ -0,0 +1,13 @@ +#pragma once + +class CXuiSceneBasePlayer : public CXuiSceneImpl +{ + +protected: + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneBasePlayer, L"CXuiSceneBasePlayer", XUI_CLASS_SCENE ) +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Chat.cpp b/Minecraft.Client/Common/XUI/XUI_Chat.cpp new file mode 100644 index 0000000..3e3faa7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Chat.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "XUI_Chat.h" +#include "..\..\Minecraft.h" +#include "..\..\Gui.h" + +HRESULT CScene_Chat::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + this->SetTimer(0,100); + + XuiElementGetPosition(m_hObj,&m_OriginalPosition); + + return S_OK; +} + +HRESULT CScene_Chat::OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + Gui *pGui = pMinecraft->gui; + + //DWORD messagesToDisplay = min( CHAT_LINES_COUNT, pGui->getMessagesCount(m_iPad) ); + for( unsigned int i = 0; i < CHAT_LINES_COUNT; ++i ) + { + float opacity = pGui->getOpacity(m_iPad, i); + if( opacity > 0 ) + { + m_Backgrounds[i].SetOpacity(opacity); + m_Labels[i].SetOpacity(opacity); + m_Labels[i].SetText( pGui->getMessage(m_iPad,i).c_str() ); + } + else + { + m_Backgrounds[i].SetOpacity(0); + m_Labels[i].SetOpacity(0); + } + } + if(pMinecraft->localplayers[m_iPad]!= NULL) + { + m_Jukebox.SetText( pGui->getJukeboxMessage(m_iPad).c_str() ); + m_Jukebox.SetOpacity( pGui->getJukeboxOpacity(m_iPad) ); + } + return S_OK; +} + +HRESULT CScene_Chat::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + + app.ReloadChatScene(m_iPad, bJoining); + + return S_OK; +} + +HRESULT CScene_Chat::OffsetTextPosition( float xOffset, float yOffset /*= 0.0f*/ ) +{ + D3DXVECTOR3 vPos; + float fWidth, fHeight; + XuiElementGetBounds( m_Backgrounds[0], &fWidth, &fHeight ); + for(unsigned int i = 0; i < CHAT_LINES_COUNT; ++i) + { + XuiElementGetPosition( m_Labels[i], &vPos ); + vPos.x = xOffset; + vPos.y += yOffset; + XuiElementSetPosition( m_Labels[i], &vPos ); + XuiElementSetBounds( m_Labels[i], fWidth - xOffset, fHeight ); + } + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Chat.h b/Minecraft.Client/Common/XUI/XUI_Chat.h new file mode 100644 index 0000000..d685e58 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Chat.h @@ -0,0 +1,59 @@ +#pragma once +#include "../media/xuiscene_chat.h" +#include "XUI_CustomMessages.h" + +#define CHAT_LINES_COUNT 10 + +class CScene_Chat : public CXuiSceneImpl +{ + +protected: + CXuiControl m_Labels[CHAT_LINES_COUNT]; + CXuiControl m_Backgrounds[CHAT_LINES_COUNT]; + CXuiControl m_Jukebox; + + D3DXVECTOR3 m_OriginalPosition; + int m_iPad; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiLabel1, m_Labels[0]) + MAP_CONTROL(IDC_XuiLabel2, m_Labels[1]) + MAP_CONTROL(IDC_XuiLabel3, m_Labels[2]) + MAP_CONTROL(IDC_XuiLabel4, m_Labels[3]) + MAP_CONTROL(IDC_XuiLabel5, m_Labels[4]) + MAP_CONTROL(IDC_XuiLabel6, m_Labels[5]) + MAP_CONTROL(IDC_XuiLabel7, m_Labels[6]) + MAP_CONTROL(IDC_XuiLabel8, m_Labels[7]) + MAP_CONTROL(IDC_XuiLabel9, m_Labels[8]) + MAP_CONTROL(IDC_XuiLabel10, m_Labels[9]) + MAP_CONTROL(IDC_XuiBack1, m_Backgrounds[0]) + MAP_CONTROL(IDC_XuiBack2, m_Backgrounds[1]) + MAP_CONTROL(IDC_XuiBack3, m_Backgrounds[2]) + MAP_CONTROL(IDC_XuiBack4, m_Backgrounds[3]) + MAP_CONTROL(IDC_XuiBack5, m_Backgrounds[4]) + MAP_CONTROL(IDC_XuiBack6, m_Backgrounds[5]) + MAP_CONTROL(IDC_XuiBack7, m_Backgrounds[6]) + MAP_CONTROL(IDC_XuiBack8, m_Backgrounds[7]) + MAP_CONTROL(IDC_XuiBack9, m_Backgrounds[8]) + MAP_CONTROL(IDC_XuiBack10, m_Backgrounds[9]) + MAP_CONTROL(IDC_XuiLabelJukebox, m_Jukebox) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Chat, L"CScene_Chat", XUI_CLASS_SCENE ) + + HRESULT OffsetTextPosition( float xOffset, float yOffset = 0.0f ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.cpp b/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.cpp new file mode 100644 index 0000000..9a82a7b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.cpp @@ -0,0 +1,216 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\..\Minecraft.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_ConnectingProgress::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + ConnectionProgressParams *param = (ConnectionProgressParams *)pInitData->pvInitData; + m_iPad = param->iPad; + MapChildControls(); + + if( param->stringId >= 0 ) + { + m_title.SetText( app.GetString( param->stringId ) ); + } + else + { + m_title.SetText( L"" ); + } + + m_buttonConfirm.SetText( app.GetString( IDS_CONFIRM_OK ) ); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + } + + CXuiSceneBase::ShowBackground( m_iPad, TRUE ); + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + + m_showTooltips = param->showTooltips; + if( param->showTooltips ) + ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_CANCEL_JOIN, -1, -1 ); + else + ui.SetTooltips( m_iPad, -1 ); + + m_runFailTimer = param->setFailTimer; + m_timerTime = param->timerTime; + + return S_OK; +} + +// The framework calls this handler when the object is to be destroyed. +HRESULT CScene_ConnectingProgress::OnDestroy() +{ + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Updates the UI when the list selection changes. +//---------------------------------------------------------------------------------- +HRESULT CScene_ConnectingProgress::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_ConnectingProgress::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed == m_buttonConfirm) + { + if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() ) + { + // The connection failed if we see the button, so the temp player should be removed and the viewports updated again + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->removeLocalPlayerIdx(m_iPad); + } + else + { + app.NavigateToHomeMenu(); + //app.NavigateBack( ProfileManager.GetPrimaryPad() ); + } + } + + return S_OK; +} + +HRESULT CScene_ConnectingProgress::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if( m_showTooltips ) + { + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + if (pInputData->dwKeyCode == VK_PAD_B) + { + // Cancel the join + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->removeLocalPlayerIdx(m_iPad); + rfHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_ConnectingProgress::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + // This gets called every frame, so use it to update our two text boxes + + return S_OK; +} + +HRESULT CScene_ConnectingProgress::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(m_runFailTimer) XuiSetTimer(m_hObj,0,m_timerTime); + if( pTransition->dwTransType == XUI_TRANSITION_FROM ) + { + XuiKillTimer(m_hObj,0); + } + + return S_OK; +} + +HRESULT CScene_ConnectingProgress::OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + // are we being destroyed? If so, don't do anything + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) + { + return S_OK; + } + + if( pTransition->dwTransType == XUI_TRANSITION_TO ) + { + if(m_runFailTimer) XuiSetTimer(m_hObj,0,m_timerTime); + } + + return S_OK; +} + +HRESULT CScene_ConnectingProgress::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // Check if the connection failed + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->m_connectionFailed[m_iPad] || !g_NetworkManager.IsInSession() ) + { + app.RemoveBackScene(m_iPad); + + // 4J-PB - timers auto repeat, so kill it + XuiKillTimer(m_hObj,0); + + int exitReasonStringId; + switch(pMinecraft->m_connectionFailedReason[m_iPad]) + { + case DisconnectPacket::eDisconnect_LoginTooLong: + exitReasonStringId = IDS_DISCONNECTED_LOGIN_TOO_LONG; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + break; + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + break; + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + break; + case DisconnectPacket::eDisconnect_NoFlying: + exitReasonStringId = IDS_DISCONNECTED_FLYING; + break; + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + break; + default: + exitReasonStringId = IDS_CONNECTION_LOST_SERVER; + break; + } + + if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() ) + { + m_buttonConfirm.SetShow(TRUE); + m_buttonConfirm.SetFocus(m_iPad); + + // Set text + m_title.SetText( app.GetString( IDS_CONNECTION_FAILED ) ); + m_status.SetText( app.GetString( exitReasonStringId ) ); + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; +#ifdef _XBOX + StorageManager.RequestMessageBox( IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); +#endif + exitReasonStringId = -1; + + //app.NavigateToHomeMenu(); + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_ExitWorld,(void *)TRUE); + } + } + + return S_OK; +} diff --git a/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.h b/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.h new file mode 100644 index 0000000..360ca0b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_ConnectingProgress.h @@ -0,0 +1,55 @@ +#pragma once +#include "../media/xuiscene_connectingprogress.h" + +class CScene_ConnectingProgress : public CXuiSceneImpl +{ +private: + int m_iPad; + bool m_runFailTimer; + int m_timerTime; + bool m_showTooltips; +protected: + // Control and Element wrapper objects. + CXuiControl m_title, m_status, m_buttonConfirm; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_TRANSITION_START( OnTransitionStart ) + XUI_ON_XM_TRANSITION_END( OnTransitionEnd ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Title, m_title) + MAP_CONTROL(IDC_Status, m_status) + MAP_CONTROL(IDC_ButtonConfirm, m_buttonConfirm) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_ConnectingProgress, L"CScene_ConnectingProgress", XUI_CLASS_SCENE ) + +private: + bool m_threadCompleted; + D3DXVECTOR3 m_OriginalPosition; + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.cpp b/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.cpp new file mode 100644 index 0000000..0096da4 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "XUI_Control_ComboBox.h" +#include "..\Xbox_App.h" + +HRESULT CXuiControl4JComboBox::OnInit(XUIMessageInit *pInitData, BOOL& bHandled) +{ + m_ListData.nItems=0; + m_ListData.pItems=NULL; + + return S_OK; +} + +void CXuiControl4JComboBox::SetData(LIST_ITEM_INFO *pItems,int iCount) +{ + CXuiControl4JComboBox *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + + // copy the data in + pThis->m_ListData.pItems= new LIST_ITEM_INFO [iCount] ; + memcpy(pThis->m_ListData.pItems,pItems,sizeof(LIST_ITEM_INFO)*iCount); + pThis->m_ListData.nItems=iCount; + + //InsertItems( 0, iCount ); +} + +int CXuiControl4JComboBox::GetSelectedIndex() +{ + return XuiListGetCurSel(GetListObject(),NULL); +} + +// Gets called every frame +HRESULT CXuiControl4JComboBox::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData,BOOL& bHandled) +{ + if( ( 0 == pGetSourceTextData->iData ) && ( ( pGetSourceTextData->bItemData ) ) ) + { + pGetSourceTextData->szText = + m_ListData.pItems[pGetSourceTextData->iItem].pwszText; + bHandled = TRUE; + } + return S_OK; +} + +HRESULT CXuiControl4JComboBox::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData,BOOL& bHandled) +{ + pGetItemCountData->cItems = m_ListData.nItems; + bHandled = TRUE; + return S_OK; +} + +HRESULT CXuiControl4JComboBox::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + return S_OK; + + //if( ( 0 == pGetSourceImageData->iData ) && ( pGetSourceImageData->bItemData ) ) + //{ + // // Check for a brush + + // if(m_ListData.pItems[pGetSourceImageData->iItem].hXuiBrush!=NULL) + // { + // pGetSourceImageData->hBrush=m_ListData.pItems[pGetSourceImageData->iItem].hXuiBrush; + // } + // else + // { + // pGetSourceImageData->szPath = + // m_ListData.pItems[pGetSourceImageData->iItem].pwszImage; + // } + // bHandled = TRUE; + //} + //return S_OK; +} + +HRESULT CXuiControl4JComboBox::OnGetItemEnable(XUIMessageGetItemEnable *pGetItemEnableData,BOOL& bHandled) +{ + if(m_ListData.pItems!=NULL && m_ListData.nItems!=0) + { + pGetItemEnableData->bEnabled = + m_ListData.pItems[pGetItemEnableData->iItem].fEnabled; + } + bHandled = TRUE; + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CXuiControl4JComboBox::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + CScene_Base::HandleKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==GetValueObject()) + { + XuiElementSetShow(GetListObject(),TRUE); + XuiElementSetFocus(GetListObject()); + rfHandled = TRUE; + } + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.h b/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.h new file mode 100644 index 0000000..28b6118 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Control_ComboBox.h @@ -0,0 +1,53 @@ +#pragma once + +class CXuiControl4JComboBox : public CXuiComboBoxImpl +{ +public: + + // Information for one list item. + typedef struct _LIST_ITEM_INFO + { + LPCWSTR pwszText; + LPCWSTR pwszImage; + HXUIBRUSH hXuiBrush; + BOOL fChecked; + BOOL fEnabled; + } + LIST_ITEM_INFO; + + // List data. + typedef struct _tagListData + { + int nItems; + LIST_ITEM_INFO *pItems; + } + LIST_DATA; + + LIST_DATA m_ListData; + XUI_IMPLEMENT_CLASS(CXuiControl4JComboBox, L"CXuiControl4JComboBox", XUI_CLASS_COMBOBOX); + + void SetData(_LIST_ITEM_INFO *pItems,int iCount); + int GetSelectedIndex(); + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_GET_ITEMENABLE(OnGetItemEnable) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + + XUI_END_MSG_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnGetItemEnable(XUIMessageGetItemEnable *pGetItemEnableData,BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Controls.h b/Minecraft.Client/Common/XUI/XUI_Controls.h new file mode 100644 index 0000000..c1376ec --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Controls.h @@ -0,0 +1,26 @@ +#pragma once + +#include "XUI_Ctrl_4JEdit.h" +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_Ctrl_4JList.h" +#include "XUI_Ctrl_BrewProgress.h" +#include "XUI_Ctrl_BubblesProgress.h" +#include "XUI_Ctrl_BurnProgress.h" +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "XUI_Ctrl_EnchantButton.h" +#include "XUI_Ctrl_EnchantmentBook.h" +#include "XUI_Ctrl_EnchantmentButtonText.h" +#include "XUI_Ctrl_FireProgress.h" +#include "XUI_Ctrl_LoadingProgress.h" +#include "XUI_Ctrl_MinecraftPlayer.h" +#include "XUI_Ctrl_MinecraftSkinPreview.h" +#include "XUI_Ctrl_MinecraftSlot.h" +#include "XUI_Ctrl_MobEffect.h" +#include "XUI_Ctrl_PassthroughList.h" +#include "XUI_Ctrl_ProgressCtrlBase.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_Ctrl_SlotItem.h" +#include "XUI_Ctrl_SlotItemCtrlBase.h" +#include "XUI_Ctrl_SlotItemListItem.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Ctrl_SplashPulser.h" \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.cpp new file mode 100644 index 0000000..cc3afec --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.cpp @@ -0,0 +1,200 @@ +#include "stdafx.h" +#include "XUI_Ctrl_4JEdit.h" + + + +HRESULT CXuiCtrl4JEdit::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + // set a limit for the text box + m_uTextLimit=XUI_4JEDIT_MAX_CHARS-1; + XuiEditSetTextLimit(m_hObj,m_uTextLimit); + // Find the text limit. (Add one for NULL terminator) + //m_uTextLimit = min( XuiEditGetTextLimit(m_hObj) + 1, XUI_4JEDIT_MAX_CHARS); + + ZeroMemory( wchText , sizeof(WCHAR)*(m_uTextLimit+1) ); + + m_bReadOnly = false; + m_uiTitle = 0; + m_uiText =0; + m_eKeyboardMode=C_4JInput::EKeyboardMode_Default; + return hr; +} + + +HRESULT CXuiCtrl4JEdit::SetTextLimit(int iLimit) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + if(iLimitm_uTextLimit=iLimit; + XuiEditSetTextLimit(pThis->m_hObj,iLimit); + ZeroMemory( pThis->wchText , sizeof(WCHAR)*XUI_4JEDIT_MAX_CHARS ); + } + return S_OK; +} +HRESULT CXuiCtrl4JEdit::SetCaretPosition(int iPos) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + XuiEditSetCaretPosition(pThis->m_hObj,iPos); + return S_OK; +} + +HRESULT CXuiCtrl4JEdit::SetTitleAndText(unsigned int uiTitle, unsigned int uiText) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + pThis->m_uiTitle=uiTitle; + pThis->m_uiText=uiText; + + return S_OK; +} + +HRESULT CXuiCtrl4JEdit::SetReadOnly(bool bReadOnly) +{ + // Attempt to make the change on the actual original version of this object that XUI made itself, rather + // than the copy we make ourselves and then map to it, which shares the same handle + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + pThis->m_bReadOnly = bReadOnly; + + return S_OK; +} + +HRESULT CXuiCtrl4JEdit::SetKeyboardType(C_4JInput::EKeyboardMode eKeyboardMode) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + pThis->m_eKeyboardMode= eKeyboardMode; + return S_OK; +} + +// HRESULT CXuiCtrl4JEdit::SetIPMode(bool bIPMode) +// { +// // Attempt to make the change on the actual original version of this object that XUI made itself, rather +// // than the copy we make ourselves and then map to it, which shares the same handle +// CXuiCtrl4JEdit *pThis; +// HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); +// if (FAILED(hr)) +// return hr; +// pThis->m_bIPMode= bIPMode; +// +// return S_OK; +// } + +// HRESULT CXuiCtrl4JEdit::SetExtendedMode(bool bExtendedMode) +// { +// // Attempt to make the change on the actual original version of this object that XUI made itself, rather +// // than the copy we make ourselves and then map to it, which shares the same handle +// CXuiCtrl4JEdit *pThis; +// HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); +// if (FAILED(hr)) +// return hr; +// pThis->m_bExtendedMode= bExtendedMode; +// +// return S_OK; +// } + +HRESULT CXuiCtrl4JEdit::OnChar(XUIMessageChar* pInputData, BOOL& rfHandled) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + HXUIOBJ hBaseObj; + + // need to send the key down to the base object, so that when we notify the parent, the edit control has been updated with the right text + hr=XuiGetBaseObject(pThis->m_hObj,&hBaseObj); + + XUIMessage xuiMsg; + XUIMessageChar xuiMsgChar; + XuiMessageChar( &xuiMsg, &xuiMsgChar, pInputData->wch, pInputData->dwFlags, pInputData->UserIndex ); + // Send the XM_CHAR message. + XuiSendMessage( hBaseObj, &xuiMsg ); + + rfHandled = TRUE; + SendNotifyValueChanged((int)pInputData->wch); + + return hr; +} + +HRESULT CXuiCtrl4JEdit::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + //HRESULT hr = S_OK; + + if( pThis->m_bReadOnly ) return hr; + + // Find the text limit. (Add one for NULL terminator) + //m_uTextLimit = min( XuiEditGetTextLimit(m_hObj) + 1, XUI_4JEDIT_MAX_CHARS); + + if((((pInputData->dwKeyCode == VK_PAD_A) && (pInputData->wch == 0)) || (pInputData->dwKeyCode == VK_PAD_START)) && !(pInputData->dwFlags & XUI_INPUT_FLAG_REPEAT)) + { + pThis->RequestKeyboard(pInputData->UserIndex); + rfHandled = TRUE; + } + + return hr; +} + +void CXuiCtrl4JEdit::RequestKeyboard(int iPad) +{ + InputManager.RequestKeyboard(m_uiTitle,GetText(),m_uiText,iPad,wchText,m_uTextLimit+1,&CXuiCtrl4JEdit::KeyboardReturned,this,m_eKeyboardMode,app.GetStringTable()); +} + + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrl4JEdit::SendNotifyValueChanged(int iValue) +{ + CXuiCtrl4JEdit *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + HXUIOBJ hParent; + //HRESULT hr=S_OK; + XUIMessage msg; + XUINotify msgNotify; + XUINotifyValueChanged msgNotifyValueChanged; + + XuiElementGetParent(pThis->m_hObj, &hParent); + XuiNotifyValueChanged(&msg, &msgNotify, &msgNotifyValueChanged, XuiGetOuter(pThis->m_hObj), iValue); + XuiBubbleMessage(XuiGetOuter(hParent), &msg); + + return hr; +} + +int CXuiCtrl4JEdit::KeyboardReturned(void *pParam,bool bSet) +{ + CXuiCtrl4JEdit* pClass = (CXuiCtrl4JEdit*)pParam; + HRESULT hr = S_OK; + + if(bSet) + { + pClass->SetText(pClass->wchText); + // need to move the caret to the end of the newly set text + XuiEditSetCaretPosition(pClass->m_hObj, (int)wcsnlen(pClass->wchText, 50)); + pClass->SendNotifyValueChanged(10); // 10 for a return + } + + return hr; +} diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.h new file mode 100644 index 0000000..f6899d1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JEdit.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#define XUI_4JEDIT_MAX_CHARS 61 + +class CXuiCtrl4JEdit : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrl4JEdit, L"CXuiCtrl4JEdit", XUI_CLASS_EDIT) + +protected: + + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_CHAR(OnChar) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnChar(XUIMessageChar* pInputData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); +public: + HRESULT SetReadOnly(bool bReadOnly); +// HRESULT SetIPMode(bool bIPMode); +// HRESULT SetExtendedMode(bool bExtendedMode); + HRESULT SetKeyboardType(C_4JInput::EKeyboardMode eKeyboardMode); + HRESULT SetTextLimit(int iLimit); + HRESULT SetCaretPosition(int iPos); + HRESULT SetTitleAndText(unsigned int uiTitle, unsigned int uiText); + + void RequestKeyboard(int iPad); +protected: + bool m_bReadOnly; + C_4JInput::EKeyboardMode m_eKeyboardMode; + unsigned int m_uiTitle,m_uiText; + +private: + static int KeyboardReturned(void *pParam,bool bSet); + HRESULT SendNotifyValueChanged(int); + WCHAR wchText[XUI_4JEDIT_MAX_CHARS]; + unsigned int m_uTextLimit; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.cpp new file mode 100644 index 0000000..8895b60 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "XUI_Ctrl_4JIcon.h" + +HRESULT CXuiCtrl4JIcon::OnInit(XUIMessageInit *pInitData, BOOL& bHandled) +{ + m_hBrush=NULL; + return S_OK; +} + +HRESULT CXuiCtrl4JIcon::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + XUIMessage Message; + XUIMessageGetSourceImage MsgGetImage; + HRESULT hr; + HXUIOBJ hObj; + + if(m_hBrush) + { + pGetSourceImageData->hBrush = m_hBrush; + bHandled = TRUE; + } + else + { + XuiMessageGetSourceImage(&Message, &MsgGetImage, pGetSourceImageData->iItem, pGetSourceImageData->iData, TRUE); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + + if (Message.bHandled) + { + pGetSourceImageData->hBrush = MsgGetImage.hBrush; + bHandled = TRUE; + } + } + } + return S_OK; +} + +HRESULT CXuiCtrl4JIcon::UseBrush(HXUIBRUSH hBrush) +{ + if( m_hBrush ) + { + XuiDestroyBrush( m_hBrush ); + } + m_hBrush = hBrush; + return XuiControlSetImageBrush(m_hObj,hBrush); +} + +HRESULT CXuiCtrl4JIcon::OnDestroy() +{ + + if( m_hBrush ) + { + XuiDestroyBrush( m_hBrush ); + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.h new file mode 100644 index 0000000..d99f0c5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JIcon.h @@ -0,0 +1,25 @@ +#pragma once + + +class CXuiCtrl4JIcon : public CXuiControlImpl +{ +public: + + XUI_IMPLEMENT_CLASS(CXuiCtrl4JIcon, L"CXuiCtrl4JIcon", XUI_CLASS_LABEL); + HRESULT UseBrush(HXUIBRUSH hBrush); + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_END_MSG_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnDestroy(); + + HXUIBRUSH m_hBrush; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.cpp new file mode 100644 index 0000000..60c3290 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.cpp @@ -0,0 +1,405 @@ +#include "stdafx.h" +#include "XUI_Ctrl_4JList.h" + +static bool TimeSortFn(const void *a, const void *b); + +HRESULT CXuiCtrl4JList::OnInit(XUIMessageInit *pInitData, BOOL& bHandled) +{ + InitializeCriticalSection(&m_AccessListData); + + m_hSelectionChangedHandlerObj = NULL; + + return S_OK; +} + +void CXuiCtrl4JList::AddData( const LIST_ITEM_INFO& ItemInfo , int iSortListFromIndex, int iSortFunction) +{ + // need to allocate memory for the structure and its strings + // and remap the string pointers + DWORD dwBytes=0; + DWORD dwLen1=0; + DWORD dwLen2=0; + + if(ItemInfo.pwszText) + { + dwLen1=(int)wcslen(ItemInfo.pwszText)*sizeof(WCHAR); + dwBytes+=dwLen1+sizeof(WCHAR); + } + + if(ItemInfo.pwszImage) + { + dwLen2=(int)(wcslen(ItemInfo.pwszImage))*sizeof(WCHAR); + dwBytes+=dwLen2+sizeof(WCHAR); + } + + dwBytes+=sizeof( LIST_ITEM_INFO ); + LIST_ITEM_INFO *pItemInfo = (LIST_ITEM_INFO *)new BYTE[dwBytes]; + ZeroMemory(pItemInfo,dwBytes); + + XMemCpy( pItemInfo, &ItemInfo, sizeof( LIST_ITEM_INFO ) ); + if(dwLen1!=0) + { + XMemCpy( &pItemInfo[1], ItemInfo.pwszText, dwLen1 ); + pItemInfo->pwszText=(LPCWSTR)&pItemInfo[1]; + if(dwLen2!=0) + { + BYTE *pwszImage = ((BYTE *)&pItemInfo[1])+dwLen1+sizeof(WCHAR); + XMemCpy( pwszImage, ItemInfo.pwszImage, dwLen2 ); + pItemInfo->pwszImage=(LPCWSTR)pwszImage; + } + } + else if(dwLen2!=0) + { + XMemCpy( &pItemInfo[1], ItemInfo.pwszImage, dwLen2 ); + pItemInfo->pwszImage=(LPCWSTR)&pItemInfo[1]; + } + + EnterCriticalSection(&m_AccessListData); + + // need to remember the original index of this addition before it gets sorted - this will get used to load the game + if(iSortListFromIndex!=-1) + { + pItemInfo->iIndex=(int)m_vListData.size()-iSortListFromIndex; + } + else + { + pItemInfo->iIndex=(int)m_vListData.size(); + } + + // added to force a sort order for DLC + //pItemInfo->iSortIndex=iSortIndex; + + m_vListData.push_back(pItemInfo); + +#ifdef _DEBUG + + int iCount=0; + for (AUTO_VAR(it, m_vListData.begin()); it != m_vListData.end(); it++) + { + PLIST_ITEM_INFO pInfo=(PLIST_ITEM_INFO)*it; + app.DebugPrintf("%d. ",iCount++); + OutputDebugStringW(pInfo->pwszText); + app.DebugPrintf(" - %d\n",pInfo->iSortIndex); + + } +#endif + + + if(iSortListFromIndex!=-1) + { + switch(iSortFunction) + { + case eSortList_Date: + // sort from the index passed (to leave create world and tutorial in the saves list) + sort(m_vListData.begin()+iSortListFromIndex, m_vListData.end(),CXuiCtrl4JList::TimeSortFn); + break; + case eSortList_Alphabetical: + // alphabetical sort + sort(m_vListData.begin()+iSortListFromIndex, m_vListData.end(),CXuiCtrl4JList::AlphabeticSortFn); + break; + case eSortList_Index: + sort(m_vListData.begin()+iSortListFromIndex, m_vListData.end(),CXuiCtrl4JList::IndexSortFn); + break; + } + } + LeaveCriticalSection(&m_AccessListData); +// #ifdef _DEBUG +// +// iCount=0; +// for (AUTO_VAR(it, m_vListData.begin()); it != m_vListData.end(); it++) +// { +// PLIST_ITEM_INFO pInfo=(PLIST_ITEM_INFO)*it; +// app.DebugPrintf("After Sort - %d. ",iCount++); +// OutputDebugStringW(pInfo->pwszText); +// app.DebugPrintf(" - %d\n",pInfo->iSortIndex); +// +// } +// #endif + InsertItems( 0, 1 ); +} + +void CXuiCtrl4JList::RemoveAllData( ) +{ + EnterCriticalSection(&m_AccessListData); + + int iSize=(int)m_vListData.size(); + for(int i=0;ihXuiBrush ) + { + XuiDestroyBrush(pBack->hXuiBrush); + } + m_vListData.pop_back(); + DeleteItems( 0, 1 ); + } + + LeaveCriticalSection(&m_AccessListData); +} + +void CXuiCtrl4JList::SelectByUserData(int iData) +{ + for(unsigned int i = 0; i < m_vListData.size(); ++i) + { + if(m_vListData.at(i)->iData == iData) + { + SetCurSel(i); + SetTopItem(i); // scroll the item into view if it's not visible + break; + } + } +} + +int CXuiCtrl4JList::GetIndexByUserData(int iData) +{ + for(unsigned int i = 0; i < m_vListData.size(); ++i) + { + if(m_vListData.at(i)->iData == iData) + { + return i; + } + } + return 0; +} + +CXuiCtrl4JList::LIST_ITEM_INFO& CXuiCtrl4JList::GetData(DWORD dw) +{ + return *m_vListData[dw]; +} + +CXuiCtrl4JList::LIST_ITEM_INFO& CXuiCtrl4JList::GetDataiData(int iData) +{ + LIST_ITEM_INFO info; + + for(unsigned int i=0;idwHighDateTime)&&(info.fTime.dwLowDateTime==pFileTime->dwLowDateTime)) + { + return *m_vListData[i]; + } + } + + return *m_vListData[0]; +} + +bool CXuiCtrl4JList::TimeSortFn(const void *a, const void *b) +{ + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsA=(CXuiCtrl4JList::LIST_ITEM_INFO *)a; + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsB=(CXuiCtrl4JList::LIST_ITEM_INFO *)b; + + if(SaveDetailsA->fTime.dwHighDateTime > SaveDetailsB->fTime.dwHighDateTime) + { + return true; + } + else if(SaveDetailsA->fTime.dwHighDateTime == SaveDetailsB->fTime.dwHighDateTime) + { + if(SaveDetailsA->fTime.dwLowDateTime > SaveDetailsB->fTime.dwLowDateTime) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + +} + +bool CXuiCtrl4JList::AlphabeticSortFn(const void *a, const void *b) +{ + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsA=(CXuiCtrl4JList::LIST_ITEM_INFO *)a; + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsB=(CXuiCtrl4JList::LIST_ITEM_INFO *)b; + + wstring wstr1=SaveDetailsA->pwszText; + wstring wstr2=SaveDetailsB->pwszText; + if(wstr1.compare(wstr2)<0) + { + return true; + } + + return false; +} + +bool CXuiCtrl4JList::IndexSortFn(const void *a, const void *b) +{ + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsA=(CXuiCtrl4JList::LIST_ITEM_INFO *)a; + CXuiCtrl4JList::LIST_ITEM_INFO *SaveDetailsB=(CXuiCtrl4JList::LIST_ITEM_INFO *)b; + + int iA=SaveDetailsA->iSortIndex; + int iB=SaveDetailsB->iSortIndex; + if(iA>iB) + { + return true; + } + + return false; +} + +void CXuiCtrl4JList::UpdateGraphic(int iItem,HXUIBRUSH hXuiBrush ) +{ + // need to update the one with the matching filetime + EnterCriticalSection(&m_AccessListData); + if( GetData(iItem).hXuiBrush ) + { + XuiDestroyBrush( GetData(iItem).hXuiBrush ); + } + GetData(iItem).hXuiBrush=hXuiBrush; + LeaveCriticalSection(&m_AccessListData); +} + +void CXuiCtrl4JList::UpdateText(int iItem,LPCWSTR pwszText ) +{ + // need to update the one with the matching filetime + EnterCriticalSection(&m_AccessListData); + GetData(iItem).pwszText=pwszText; + LeaveCriticalSection(&m_AccessListData); +} + +void CXuiCtrl4JList::UpdateGraphicFromiData(int iData,HXUIBRUSH hXuiBrush ) +{ + // need to update the one with the matching iData + EnterCriticalSection(&m_AccessListData); + if( GetDataiData(iData).hXuiBrush ) + { + XuiDestroyBrush( GetDataiData(iData).hXuiBrush ); + } + GetDataiData(iData).hXuiBrush=hXuiBrush; + LeaveCriticalSection(&m_AccessListData); +} + +void CXuiCtrl4JList::UpdateGraphic(FILETIME *pfTime,HXUIBRUSH hXuiBrush ) +{ + // need to update the one with the matching filetime + EnterCriticalSection(&m_AccessListData); + if( GetData(pfTime).hXuiBrush ) + { + XuiDestroyBrush( GetData(pfTime).hXuiBrush ); + } + GetData(pfTime).hXuiBrush=hXuiBrush; + LeaveCriticalSection(&m_AccessListData); +} + +// Gets called every frame +HRESULT CXuiCtrl4JList::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData,BOOL& bHandled) +{ + if( ( 0 == pGetSourceTextData->iData ) && ( ( pGetSourceTextData->bItemData ) ) ) + { + EnterCriticalSection(&m_AccessListData); + pGetSourceTextData->szText = + GetData(pGetSourceTextData->iItem).pwszText; + LeaveCriticalSection(&m_AccessListData); + bHandled = TRUE; + } + return S_OK; +} + +HRESULT CXuiCtrl4JList::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData,BOOL& bHandled) +{ + pGetItemCountData->cItems = (int)m_vListData.size(); + bHandled = TRUE; + return S_OK; +} + +HRESULT CXuiCtrl4JList::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + if( ( 0 == pGetSourceImageData->iData ) && ( pGetSourceImageData->bItemData ) ) + { + // Check for a brush + EnterCriticalSection(&m_AccessListData); + if(GetData(pGetSourceImageData->iItem).hXuiBrush!=NULL) + { + pGetSourceImageData->hBrush=GetData(pGetSourceImageData->iItem).hXuiBrush; + } + else + { + pGetSourceImageData->szPath = + GetData(pGetSourceImageData->iItem).pwszImage; + } + LeaveCriticalSection(&m_AccessListData); + bHandled = TRUE; + } + return S_OK; +} + +HRESULT CXuiCtrl4JList::OnGetItemEnable(XUIMessageGetItemEnable *pGetItemEnableData,BOOL& bHandled) +{ + if(m_vListData.size()!=0) + { + EnterCriticalSection(&m_AccessListData); + pGetItemEnableData->bEnabled = + GetData(pGetItemEnableData->iItem).fEnabled; + LeaveCriticalSection(&m_AccessListData); + } + bHandled = TRUE; + return S_OK; +} + + +HRESULT CXuiCtrl4JList::SetBorder(DWORD dw,BOOL bShow) +{ + CXuiControl Control; + HXUIOBJ hVisual,hBorder; + GetItemControl(dw,&Control); + Control.GetVisual(&hVisual); + XuiElementGetChildById(hVisual,L"Border",&hBorder); + return XuiElementSetShow(hBorder,bShow); +} + +void CXuiCtrl4JList::SetSelectionChangedHandle(HXUIOBJ hObj) +{ + m_hSelectionChangedHandlerObj = hObj; +} + +HRESULT CXuiCtrl4JList::OnDestroy() +{ + DeleteCriticalSection(&m_AccessListData); + + if(m_vListData.size()!=0) + { + for (unsigned i = 0; i < m_vListData.size(); ++i) + { + if( m_vListData[i]->hXuiBrush ) + { + XuiDestroyBrush( m_vListData[i]->hXuiBrush ); + } + delete [] (BYTE *)m_vListData[i]; + } + } + return S_OK; +} + +HRESULT CXuiCtrl4JList::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if(m_hSelectionChangedHandlerObj) + { + XUIMessage xuiMsg; + XUINotify xuiNotify; + XUINotifySelChanged xuiNotifySel; + XuiNotifySelChanged( &xuiMsg, &xuiNotify, &xuiNotifySel, hObjSource, pNotifySelChangedData->iItem, pNotifySelChangedData->iOldItem ); + XuiSendMessage( m_hSelectionChangedHandlerObj, &xuiMsg ); + + bHandled = xuiMsg.bHandled; + } + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.h new file mode 100644 index 0000000..11bdd45 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_4JList.h @@ -0,0 +1,78 @@ +#pragma once + + +class CXuiCtrl4JList : public CXuiListImpl +{ +public: + enum + { + eSortList_Date = 0, + eSortList_Alphabetical, + eSortList_Index, + }; + + // Information for one list item. + typedef struct _LIST_ITEM_INFO + { + LPCWSTR pwszText; + LPCWSTR pwszImage; + HXUIBRUSH hXuiBrush; + BOOL fChecked; + BOOL fEnabled; + bool bIsDamaged; // damaged save + FILETIME fTime; + int iData; // user data + int iIndex; // used for internal list sorting + int iSortIndex; // used to force an order for DLC + } + LIST_ITEM_INFO,*PLIST_ITEM_INFO; + + typedef std::vector LISTITEMINFOARRAY; + + XUI_IMPLEMENT_CLASS(CXuiCtrl4JList, L"CXuiCtrl4JList", XUI_CLASS_LIST); + + void AddData( const LIST_ITEM_INFO& ItemInfo , int iSortListFromIndex=-1, int iSortFunction=CXuiCtrl4JList::eSortList_Date); + void RemoveAllData( ); + void UpdateText(int iItem,LPCWSTR pwszText ); + void SelectByUserData(int iData); + int GetIndexByUserData(int iData); + + void UpdateGraphic(int iItem,HXUIBRUSH hXuiBrush ); + void UpdateGraphic(FILETIME *pfTime,HXUIBRUSH hXuiBrush ); + void UpdateGraphicFromiData(int iData,HXUIBRUSH hXuiBrush ); + LIST_ITEM_INFO& GetData(DWORD dw); + LIST_ITEM_INFO& GetData(FILETIME *pFileTime); + LIST_ITEM_INFO& GetDataiData(int iData); + HRESULT SetBorder(DWORD dw,BOOL bShow); // for a highlight around the current selected item in the controls layout + void SetSelectionChangedHandle(HXUIOBJ hObj); +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_GET_ITEMENABLE(OnGetItemEnable) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_END_MSG_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnGetItemEnable(XUIMessageGetItemEnable *pGetItemEnableData,BOOL& bHandled); + HRESULT OnDestroy(); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + + LISTITEMINFOARRAY m_vListData; + CRITICAL_SECTION m_AccessListData; + +private: + static bool AlphabeticSortFn(const void *a, const void *b); + static bool TimeSortFn(const void *a, const void *b); + static bool IndexSortFn(const void *a, const void *b); + + HXUIOBJ m_hSelectionChangedHandlerObj; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.cpp new file mode 100644 index 0000000..074b730 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\SharedConstants.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.alchemy.h" +#include "XUI_Ctrl_BrewProgress.h" + +int CXuiCtrlBrewProgress::GetValue() +{ + void* pvUserData; + this->GetUserData( &pvUserData ); + + if( pvUserData != NULL ) + { + BrewingStandTileEntity *pBrewingStandTileEntity = (BrewingStandTileEntity *)pvUserData; + + return pBrewingStandTileEntity->getBrewTime(); + } + + return 0; +} + +void CXuiCtrlBrewProgress::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = PotionBrewing::BREWING_TIME_SECONDS * SharedConstants::TICKS_PER_SECOND; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.h new file mode 100644 index 0000000..2f057a7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BrewProgress.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +class CXuiCtrlBrewProgress : public CXuiCtrlProgressCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlBrewProgress, L"CXuiCtrlBrewProgress", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.cpp new file mode 100644 index 0000000..cc5d996 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "XUI_Ctrl_BubblesProgress.h" + +int CXuiCtrlBubblesProgress::GetValue() +{ + void* pvUserData; + this->GetUserData( &pvUserData ); + + if( pvUserData != NULL ) + { + BrewingStandTileEntity *pBrewingStandTileEntity = (BrewingStandTileEntity *)pvUserData; + + int value = 0; + int bubbleStep = (pBrewingStandTileEntity->getBrewTime() / 2) % 7; + switch (bubbleStep) + { + case 0: + value = 0; + break; + case 6: + value = 5; + break; + case 5: + value = 10; + break; + case 4: + value = 15; + break; + case 3: + value = 20; + break; + case 2: + value = 25; + break; + case 1: + value = 30; + break; + } + + return value; + } + + return 0; +} + +void CXuiCtrlBubblesProgress::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = 30; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.h new file mode 100644 index 0000000..4950cf7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BubblesProgress.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +class CXuiCtrlBubblesProgress : public CXuiCtrlProgressCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlBubblesProgress, L"CXuiCtrlBubblesProgress", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.cpp new file mode 100644 index 0000000..8e51409 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\FurnaceMenu.h" +#include "..\..\..\Minecraft.World\FurnaceTileEntity.h" +#include "XUI_Scene_Furnace.h" +#include "XUI_Ctrl_BurnProgress.h" + +int CXuiCtrlBurnProgress::GetValue() +{ + void* pvUserData; + this->GetUserData( &pvUserData ); + + if( pvUserData != NULL ) + { + FurnaceTileEntity *pFurnaceTileEntity = (FurnaceTileEntity *)pvUserData; + + // TODO This param is a magic number in Java but we should really define it somewhere with a name + // I think it is the number of states of the progress display (ie the max value) + return pFurnaceTileEntity->getBurnProgress( 24 ); + } + + return 0; +} + +void CXuiCtrlBurnProgress::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = 24; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.h new file mode 100644 index 0000000..1c403e3 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_BurnProgress.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +class CXuiCtrlBurnProgress : public CXuiCtrlProgressCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlBurnProgress, L"CXuiCtrlBurnProgress", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.cpp new file mode 100644 index 0000000..82b6c3e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" + +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSlot class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CXuiCtrlCraftIngredientSlot::CXuiCtrlCraftIngredientSlot() +{ + m_iID=0; + m_Desc=NULL; + m_isFoil = false; + m_isDirty = false; + m_item = nullptr; +} + + + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlCraftIngredientSlot::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + return hr; +} +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlCraftIngredientSlot::OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) +{ + if( m_iID != 0 || m_item != NULL ) + { + pData->item = m_item; + pData->iItemBitField = MAKE_SLOTDISPLAY_ITEM_BITMASK(m_iID,m_iAuxVal,m_isFoil); + pData->iDataBitField = MAKE_SLOTDISPLAY_DATA_BITMASK(m_iPad, m_uiAlpha,m_bDecorations,m_iCount,m_iScale,0); + } + else + { + pData->iDataBitField = 0; + pData->szPath = L""; + } + pData->bDirty = m_isDirty ? TRUE : FALSE; + m_isDirty = false; + + bHandled = TRUE; + return S_OK; +} + +HRESULT CXuiCtrlCraftIngredientSlot::OnGetSourceText(XUIMessageGetSourceText *pGetSourceTextData,BOOL& bHandled) +{ + pGetSourceTextData->szText=m_Desc; + bHandled = TRUE; + + return S_OK; +} + +void CXuiCtrlCraftIngredientSlot::SetRedBox(BOOL bVal) +{ + HRESULT hr=S_OK; + + HXUIOBJ hObj,hObjChild; + hr=GetVisual(&hObj); + XuiElementGetChildById(hObj,L"BoxRed",&hObjChild); + XuiElementSetShow(hObjChild,bVal); + XuiElementGetChildById(hObj,L"Exclaim",&hObjChild); + XuiElementSetShow(hObjChild,bVal); +} + +void CXuiCtrlCraftIngredientSlot::SetIcon(int iPad, int iId,int iAuxVal, int iCount, int iScale, unsigned int uiAlpha,bool bDecorations,bool isFoil, BOOL bShow) +{ + m_item = nullptr; + m_iID=iId; + m_iAuxVal=iAuxVal; + + // 4J Stu - For clocks and compasses we set the aux value to a special one that signals we should use a default texture + // rather than the dynamic one for the player + // not right... auxvals for diggables are damage values, can be a lot higher + if( (m_iAuxVal & 0xFF) == 0xFF && !( iId == Item::clock_Id || iId == Item::compass_Id ) ) // 4J Stu - If the aux value is set to match any + m_iAuxVal = 0; + + // if the count comes in as 0, make it 1 + m_iCount=iCount==0?1:iCount; + m_iScale=iScale; + m_uiAlpha=uiAlpha; + m_bDecorations=bDecorations; + m_isFoil = isFoil; + + m_iPad = iPad; + m_isDirty = true; + + XuiElementSetShow(m_hObj,bShow); +} + +void CXuiCtrlCraftIngredientSlot::SetIcon(int iPad, shared_ptr item, int iScale, unsigned int uiAlpha,bool bDecorations, BOOL bShow) +{ + if(item == NULL) SetIcon(iPad, 0,0,0,0,0,false,false,bShow); + else + { + m_item = item; + m_iID = item->id; + m_iScale = iScale; + m_uiAlpha = uiAlpha; + m_bDecorations = bDecorations; + + m_iPad = iPad; + m_isDirty = true; + + XuiElementSetShow(m_hObj,bShow); + } +} + +void CXuiCtrlCraftIngredientSlot::SetDescription(LPCWSTR Desc) +{ + HRESULT hr=S_OK; + + HXUIOBJ hObj,hObjChild; + hr=GetVisual(&hObj); + XuiElementGetChildById(hObj,L"text_name",&hObjChild); + XuiControlSetText(hObjChild,Desc); + XuiElementSetShow(hObjChild,Desc==NULL?FALSE:TRUE); + m_Desc=Desc; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.h new file mode 100644 index 0000000..5cf7a7f --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_CraftIngredientSlot.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSlot class +//----------------------------------------------------------------------------- +class CXuiCtrlCraftIngredientSlot : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlCraftIngredientSlot, L"CXuiCtrlCraftIngredientSlot", XUI_CLASS_LABEL) + + CXuiCtrlCraftIngredientSlot(); + virtual ~CXuiCtrlCraftIngredientSlot() { }; + void SetRedBox(BOOL bVal); + void SetIcon(int iPad, int iId,int iAuxVal, int iCount, int iScale, unsigned int uiAlpha, bool bDecorations, bool isFoil = false, BOOL bShow=TRUE); + void SetIcon(int iPad, shared_ptr item, int iScale, unsigned int uiAlpha,bool bDecorations, BOOL bShow=TRUE); + void SetDescription(LPCWSTR Desc); +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GETSLOTITEM_MESSAGE(OnCustomMessage_GetSlotItem) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceText) + XUI_END_MSG_MAP() + + HRESULT OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled); + HRESULT OnGetSourceText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + +private: + shared_ptr m_item; + int m_iID; + int m_iAuxVal; + int m_iCount; + int m_iScale; + unsigned int m_uiAlpha; + int m_iPad; + bool m_bDecorations; + bool m_isFoil; + LPCWSTR m_Desc; + bool m_isDirty; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.cpp new file mode 100644 index 0000000..dfb9b5c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Font.h" +#include "..\..\Lighting.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "XUI_Scene_Enchant.h" +#include "XUI_Ctrl_EnchantButton.h" + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlEnchantmentButton::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + HXUIOBJ parent = m_hObj; + HXUICLASS hcInventoryClass = XuiFindClass( L"CXuiSceneEnchant" ); + HXUICLASS currentClass; + + do + { + XuiElementGetParent(parent,&parent); + currentClass = XuiGetObjectClass( parent ); + } while (parent != NULL && !XuiClassDerivesFrom( currentClass, hcInventoryClass ) ); + + assert( parent != NULL ); + + VOID *pObj; + XuiObjectFromHandle( parent, &pObj ); + m_containerScene = (CXuiSceneEnchant *)pObj; + + m_index = 0; + m_lastCost = 0; + m_iPad = 0; + m_costString = L""; + + return hr; +} + +void CXuiCtrlEnchantmentButton::SetData(int iPad, int index) +{ + m_iPad = iPad; + m_index = index; +} + +HRESULT CXuiCtrlEnchantmentButton::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + EnchantmentMenu *menu = m_containerScene->getMenu(); + + int cost = menu->costs[m_index]; + + if(cost != m_lastCost) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(cost > pMinecraft->localplayers[m_iPad]->experienceLevel && !pMinecraft->localplayers[m_iPad]->abilities.instabuild) + { + // Dark background + SetEnable(FALSE); + } + else + { + // Light background and focus background + SetEnable(TRUE); + } + m_costString = _toString(cost); + m_lastCost = cost; + } + if(cost == 0) + { + // Dark background + SetEnable(FALSE); + pGetSourceTextData->bDisplay = FALSE; + } + else + { + pGetSourceTextData->szText = m_costString.c_str(); + pGetSourceTextData->bDisplay = TRUE; + } + + bHandled = TRUE; + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.h new file mode 100644 index 0000000..0b414d6 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantButton.h @@ -0,0 +1,33 @@ +#pragma once + +class CXuiSceneEnchant; + +class CXuiCtrlEnchantmentButton : public CXuiControlImpl +{ + friend class CXuiCtrlEnchantmentButtonText; +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlEnchantmentButton, L"CXuiCtrlEnchantmentButton", XUI_CLASS_CONTROL) + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + +public: + void SetData(int iPad, int index); + +private: + int m_iPad; + int m_index; + int m_lastCost; + wstring m_costString; + CXuiSceneEnchant *m_containerScene; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.cpp new file mode 100644 index 0000000..8a56a0e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.cpp @@ -0,0 +1,346 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\TileEntityRenderDispatcher.h" +#include "..\..\EnchantTableRenderer.h" +#include "..\..\Lighting.h" +#include "..\..\LocalPlayer.h" + +#include "XUI_Scene_Enchant.h" + +#include "XUI_Ctrl_EnchantmentBook.h" +#include "..\..\BookModel.h" +#include "..\..\Options.h" + +//----------------------------------------------------------------------------- +// CXuiCtrlEnchantmentBook class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CXuiCtrlEnchantmentBook::CXuiCtrlEnchantmentBook() : + m_bDirty(FALSE), + m_fScale(1.0f), + m_fAlpha(1.0f) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + model = NULL; + + time = 0; + flip = oFlip = flipT = flipA = 0.0f; + open = oOpen = 0.0f; +} + +CXuiCtrlEnchantmentBook::~CXuiCtrlEnchantmentBook() +{ + //if(model != NULL) delete model; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlEnchantmentBook::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + HXUIOBJ parent = m_hObj; + HXUICLASS hcInventoryClass = XuiFindClass( L"CXuiSceneEnchant" ); + HXUICLASS currentClass; + + do + { + XuiElementGetParent(parent,&parent); + currentClass = XuiGetObjectClass( parent ); + } while (parent != NULL && !XuiClassDerivesFrom( currentClass, hcInventoryClass ) ); + + assert( parent != NULL ); + + VOID *pObj; + XuiObjectFromHandle( parent, &pObj ); + m_containerScene = (CXuiSceneEnchant *)pObj; + + last = nullptr; + + m_iPad = m_containerScene->getPad(); + + return hr; +} + +HRESULT CXuiCtrlEnchantmentBook::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ +#ifdef _XBOX + HXUIDC hDC = pRenderData->hDC; + + // build and render with the game call + + RenderManager.Set_matrixDirty(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + float alpha = 1.0f; + //GetOpacity( &alpha ); + + + D3DXMATRIX matrix; + GetFullXForm(&matrix); + + float bwidth,bheight; + GetBounds(&bwidth,&bheight); + + glColor4f(1, 1, 1, alpha); + + // Annoyingly, XUI renders everything to a z of 0 so if we want to render anything that needs the z-buffer on top of it, then we need to clear it. + // Clear just the region required for this control. + D3DRECT clearRect; + clearRect.x1 = (int)(matrix._41) - 2; + clearRect.y1 = (int)(matrix._42) - 2; + clearRect.x2 = (int)(matrix._41 + ( bwidth * matrix._11 )) + 2; + clearRect.y2 = (int)(matrix._42 + ( bheight * matrix._22 )) + 2; + + RenderManager.Clear(GL_DEPTH_BUFFER_BIT, &clearRect); + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fRawWidth, m_fRawHeight, 0, 1000, 3000); + //gluPerspective(90, (float) (320 / 240.0f), 0.5f, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + float xo = ( (matrix._41 + ( (bwidth*matrix._11)/2) ) / m_fScreenWidth ) * m_fRawWidth; + float yo = ( (matrix._42 + ((bheight*matrix._22)/2) ) / m_fScreenHeight ) * m_fRawHeight; + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glPushMatrix(); + glTranslatef(xo, yo, 50.0f); + float ss; + + // Base scale on height of this control + // Potentially we might want separate x & y scales here + ss = ( ( (bheight*matrix._22) / m_fScreenHeight ) * m_fRawHeight ) * 1.5f; + + glScalef(-ss, ss, ss); + //glRotatef(180, 0, 0, 1); + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + //float sss = 4; + + //glTranslatef(0, 3.3f, -16); + //glScalef(sss, sss, sss); + + int tex = pMinecraft->textures->loadTexture(TN_ITEM_BOOK); // 4J was L"/1_2_2/item/book.png" + pMinecraft->textures->bind(tex); + + glRotatef(20, 1, 0, 0); + + float a = 1; + float o = oOpen + (open - oOpen) * a; + glTranslatef((1 - o) * 0.2f, (1 - o) * 0.1f, (1 - o) * 0.25f); + glRotatef(-(1 - o) * 90 - 90, 0, 1, 0); + glRotatef(180, 1, 0, 0); + + float ff1 = oFlip + (flip - oFlip) * a + 0.25f; + float ff2 = oFlip + (flip - oFlip) * a + 0.75f; + ff1 = (ff1 - floor(ff1)) * 1.6f - 0.3f; + ff2 = (ff2 - floor(ff2)) * 1.6f - 0.3f; + + if (ff1 < 0) ff1 = 0; + if (ff2 < 0) ff2 = 0; + if (ff1 > 1) ff1 = 1; + if (ff2 > 1) ff2 = 1; + + glEnable(GL_CULL_FACE); + + if(model == NULL) + { + // Share the model the the EnchantTableRenderer + + EnchantTableRenderer *etr = (EnchantTableRenderer*)TileEntityRenderDispatcher::instance->getRenderer(eTYPE_ENCHANTMENTTABLEENTITY); + if(etr != NULL) + { + model = etr->bookModel; + } + else + { + model = new BookModel(); + } + } + + model->render(NULL, 0, ff1, ff2, o, 0, 1 / 16.0f,true); + glDisable(GL_CULL_FACE); + + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + XuiRenderRestoreState(hDC); + + tickBook(); + + bHandled = TRUE; + +#endif + return S_OK; +} + +//HRESULT CXuiCtrlEnchantmentBook::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +//{ +// HXUIDC hDC = pRenderData->hDC; +// +// RenderManager.Set_matrixDirty(); +// +// Minecraft *minecraft = Minecraft::GetInstance(); +// +// // 4J JEV: Inputs in the java, dunno what they are. +// float a = 1; +// +// D3DXMATRIX matrix; +// CXuiControl xuiControl(m_hObj); +// xuiControl.GetFullXForm(&matrix); +// float bwidth,bheight; +// xuiControl.GetBounds(&bwidth,&bheight); +// +// D3DXVECTOR3 vec, vecP, vecPP; +// xuiControl.GetPosition(&vec); +// +// CXuiElement parent, scene; +// xuiControl.GetParent(&parent); +// parent.GetPosition(&vecP); +// parent.GetParent(&scene); +// scene.GetPosition(&vecPP); +// +// float xo = ( (matrix._41 + ( (bwidth*matrix._11)/2) ) / m_fScreenWidth ) * m_fRawWidth; +// float yo = ( (matrix._42 + (bheight*matrix._22) ) / m_fScreenHeight ) * m_fRawHeight; +// +// glColor4f(1, 1, 1, 1); +// +// glPushMatrix(); +// glMatrixMode(GL_PROJECTION); +// glPushMatrix(); +// glLoadIdentity(); +// +// ScreenSizeCalculator ssc = ScreenSizeCalculator(minecraft->options, minecraft->width, minecraft->height); +// +// //glViewport((int) (ssc.getWidth() - 320) / 2 * ssc.scale, (int) (ssc.getHeight() - 240) / 2 * ssc.scale, (int) (320 * ssc.scale), (int) (240 * ssc.scale)); +// +// // 4J JEV: Trying to position it within the XUI element +// float xPos, yPos; +// xPos = (vec.x + vecP.x + vecPP.x) / (minecraft->width/2); xPos = xPos - 1; xPos = 2*xPos/3; +// yPos = (vec.y + vecP.y + vecPP.y) / (minecraft->height/2); yPos = 1 - yPos; yPos = 2*yPos/3; +// glTranslatef(xPos, yPos, 0); +// +// gluPerspective(90, (float) (320 / 240.0f), 9, 80); +// +// float sss = 1; +// glMatrixMode(GL_MODELVIEW); +// glLoadIdentity(); +// Lighting::turnOn(); +// +// glTranslatef(0, 3.3f, -16); +// glScalef(sss, sss, sss); +// +// float ss = 5; +// +// glScalef(ss, ss, ss); +// glRotatef(180, 0, 0, 1); +// +// int tex = minecraft->textures->loadTexture(TN_ITEM_BOOK); // 4J was L"/1_2_2/item/book.png" +// minecraft->textures->bind(tex); +// +// glRotatef(20, 1, 0, 0); +// +// float o = oOpen + (open - oOpen) * a; +// glTranslatef((1 - o) * 0.2f, (1 - o) * 0.1f, (1 - o) * 0.25f); +// glRotatef(-(1 - o) * 90 - 90, 0, 1, 0); +// glRotatef(180, 1, 0, 0); +// +// float ff1 = oFlip + (flip - oFlip) * a + 0.25f; +// float ff2 = oFlip + (flip - oFlip) * a + 0.75f; +// ff1 = (ff1 - floor(ff1)) * 1.6f - 0.3f; +// ff2 = (ff2 - floor(ff2)) * 1.6f - 0.3f; +// +// if (ff1 < 0) ff1 = 0; +// if (ff2 < 0) ff2 = 0; +// if (ff1 > 1) ff1 = 1; +// if (ff2 > 1) ff2 = 1; +// +// glEnable(GL_RESCALE_NORMAL); +// +// model.render(NULL, 0, ff1, ff2, o, 0, 1 / 16.0f,true); +// +// glDisable(GL_RESCALE_NORMAL); +// Lighting::turnOff(); +// glMatrixMode(GL_PROJECTION); +// //glViewport(0, 0, minecraft->width, minecraft->height); +// glPopMatrix(); +// glMatrixMode(GL_MODELVIEW); +// glPopMatrix(); +// +// Lighting::turnOff(); +// glColor4f(1, 1, 1, 1); +// +// XuiRenderRestoreState(hDC); +// +// tickBook(); +// +// bHandled = TRUE; +// +// return S_OK; +//} + +void CXuiCtrlEnchantmentBook::tickBook() +{ + EnchantmentMenu *menu = m_containerScene->getMenu(); + shared_ptr current = menu->getSlot(0)->getItem(); + if (!ItemInstance::matches(current, last)) + { + last = current; + + do + { + flipT += random.nextInt(4) - random.nextInt(4); + } while (flip <= flipT + 1 && flip >= flipT - 1); + } + + time++; + oFlip = flip; + oOpen = open; + + bool shouldBeOpen = false; + for (int i = 0; i < 3; i++) + { + if (menu->costs[i] != 0) + { + shouldBeOpen = true; + } + } + + if (shouldBeOpen) open += 0.2f; + else open -= 0.2f; + if (open < 0) open = 0; + if (open > 1) open = 1; + + + float diff = (flipT - flip) * 0.4f; + float max = 0.2f; + if (diff < -max) diff = -max; + if (diff > +max) diff = +max; + flipA += (diff - flipA) * 0.9f; + + flip = flip + flipA; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.h new file mode 100644 index 0000000..93da076 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentBook.h @@ -0,0 +1,51 @@ +#pragma once +#include "..\..\..\Minecraft.World\Random.h" + +using namespace std; + +class CXuiSceneEnchant; +class BookModel; + +//----------------------------------------------------------------------------- +// CXuiCtrlEnchantPanel class +//----------------------------------------------------------------------------- +class CXuiCtrlEnchantmentBook : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlEnchantmentBook, L"CXuiCtrlEnchantmentBook", XUI_CLASS_LABEL) + + CXuiCtrlEnchantmentBook(); + virtual ~CXuiCtrlEnchantmentBook(); + + void setChanged(); + void setOpen(bool); + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + BookModel *model; + Random random; + + // 4J JEV: Book animation variables. + int time; + float flip, oFlip, flipT, flipA; + float open, oOpen; + + BOOL m_bDirty; + float m_fScale,m_fAlpha; + int m_iPad; + CXuiSceneEnchant *m_containerScene; + shared_ptr last; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + + void tickBook(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.cpp new file mode 100644 index 0000000..702ef15 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Font.h" +#include "..\..\Lighting.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "XUI_Scene_Enchant.h" +#include "XUI_Ctrl_EnchantButton.h" +#include "XUI_Ctrl_EnchantmentButtonText.h" +#include "..\..\Minecraft.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" + +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlEnchantmentButtonText::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + HXUIOBJ parent = m_hObj; + HXUICLASS hcInventoryClass = XuiFindClass( L"CXuiCtrlEnchantmentButton" ); + HXUICLASS currentClass; + + do + { + XuiElementGetParent(parent,&parent); + currentClass = XuiGetObjectClass( parent ); + } while (parent != NULL && !XuiClassDerivesFrom( currentClass, hcInventoryClass ) ); + + assert( parent != NULL ); + + VOID *pObj; + XuiObjectFromHandle( parent, &pObj ); + m_parentControl = (CXuiCtrlEnchantmentButton *)pObj; + + m_lastCost = 0; + m_enchantmentString = L""; + + m_textColour = app.GetHTMLColour(eTextColor_Enchant); + m_textFocusColour = app.GetHTMLColour(eTextColor_EnchantFocus); + m_textDisabledColour = app.GetHTMLColour(eTextColor_EnchantDisabled); + + return hr; +} + +HRESULT CXuiCtrlEnchantmentButtonText::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ + HXUIDC hDC = pRenderData->hDC; + CXuiControl xuiControl(m_hObj); + + // build and render with the game call + + RenderManager.Set_matrixDirty(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + D3DXMATRIX matrix; + xuiControl.GetFullXForm(&matrix); + float bwidth,bheight; + xuiControl.GetBounds(&bwidth,&bheight); + + // Annoyingly, XUI renders everything to a z of 0 so if we want to render anything that needs the z-buffer on top of it, then we need to clear it. + // Clear just the region required for this control. + D3DRECT clearRect; + clearRect.x1 = (int)(matrix._41) - 2; + clearRect.y1 = (int)(matrix._42) - 2; + clearRect.x2 = (int)(matrix._41 + ( bwidth * matrix._11 )) + 2; + clearRect.y2 = (int)(matrix._42 + ( bheight * matrix._22 )) + 2; + + RenderManager.Clear(GL_DEPTH_BUFFER_BIT, &clearRect); + // glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fScreenWidth, m_fScreenHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + + glEnable(GL_RESCALE_NORMAL); + glPushMatrix(); + glRotatef(120, 1, 0, 0); + //Lighting::turnOnGui(); + glPopMatrix(); + + + + EnchantmentMenu *menu = m_parentControl->m_containerScene->getMenu(); + + float xo = matrix._41; + float yo = matrix._42; + glTranslatef(xo, yo, 50.0f); + + float ss = 1; + + if(!m_parentControl->m_containerScene->m_bSplitscreen) + { + ss = 2; + } + + glScalef(ss, ss, ss); + + int cost = menu->costs[m_parentControl->m_index]; + + if(cost != m_lastCost) + { + m_lastCost = cost; + m_enchantmentString = EnchantmentNames::instance.getRandomName(); + } + + glColor4f(1, 1, 1, 1); + if (cost != 0) + { + wstring line = _toString(cost); + Font *font = pMinecraft->altFont; + //int col = 0x685E4A; + unsigned int col = m_textColour; + if (pMinecraft->localplayers[m_parentControl->m_iPad]->experienceLevel < cost && !pMinecraft->localplayers[m_parentControl->m_iPad]->abilities.instabuild) + { + col = m_textDisabledColour; + font->drawWordWrap(m_enchantmentString, 0, 0, bwidth/ss, col, bheight/ss); + font = pMinecraft->font; + //col = (0x80ff20 & 0xfefefe) >> 1; + //font->drawShadow(line, (bwidth - font->width(line))/ss, 7, col); + } + else + { + if (m_parentControl->HasFocus()) + { + //col = 0xffff80; + col = m_textFocusColour; + } + font->drawWordWrap(m_enchantmentString, 0, 0, bwidth/ss, col, bheight/ss); + font = pMinecraft->font; + //col = 0x80ff20; + //font->drawShadow(line, (bwidth - font->width(line))/ss, 7, col); + } + } + else + { + } + + //Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + XuiRenderRestoreState(hDC); + + bHandled = TRUE; + + return S_OK; +} + +CXuiCtrlEnchantmentButtonText::EnchantmentNames CXuiCtrlEnchantmentButtonText::EnchantmentNames::instance; + +CXuiCtrlEnchantmentButtonText::EnchantmentNames::EnchantmentNames() +{ + wstring allWords = L"the elder scrolls klaatu berata niktu xyzzy bless curse light darkness fire air earth water hot dry cold wet ignite snuff embiggen twist shorten stretch fiddle destroy imbue galvanize enchant free limited range of towards inside sphere cube self other ball mental physical grow shrink demon elemental spirit animal creature beast humanoid undead fresh stale "; + std::wistringstream iss(allWords); + std::copy(std::istream_iterator< std::wstring, wchar_t, std::char_traits >(iss), std::istream_iterator< std::wstring, wchar_t, std::char_traits >(),std::back_inserter(words)); +} + +wstring CXuiCtrlEnchantmentButtonText::EnchantmentNames::getRandomName() +{ + int wordCount = random.nextInt(2) + 3; + wstring word = L""; + for (int i = 0; i < wordCount; i++) + { + if (i > 0) word += L" "; + word += words[random.nextInt(words.size())]; + } + return word; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.h new file mode 100644 index 0000000..c28b29e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_EnchantmentButtonText.h @@ -0,0 +1,45 @@ +#pragma once + +class CXuiSceneEnchant; + +class CXuiCtrlEnchantmentButtonText : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlEnchantmentButtonText, L"CXuiCtrlEnchantmentButtonText", XUI_CLASS_CONTROL) + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + CXuiCtrlEnchantmentButton *m_parentControl; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + + int m_lastCost; + wstring m_enchantmentString; + + unsigned int m_textColour, m_textFocusColour, m_textDisabledColour; + + class EnchantmentNames + { + public: + static EnchantmentNames instance; + + private: + Random random; + vector words; + + EnchantmentNames(); + + public: + wstring getRandomName(); + }; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.cpp new file mode 100644 index 0000000..b125af3 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\FurnaceMenu.h" +#include "..\..\..\Minecraft.World\FurnaceTileEntity.h" +#include "XUI_Scene_Furnace.h" +#include "XUI_Ctrl_FireProgress.h" + +int CXuiCtrlFireProgress::GetValue() +{ + void* pvUserData; + this->GetUserData( &pvUserData ); + + if( pvUserData != NULL ) + { + FurnaceTileEntity *pFurnaceTileEntity = (FurnaceTileEntity *)pvUserData; + + // TODO This param is a magic number in Java but we should really define it somewhere with a name + // I think it is the number of states of the progress display (ie the max value) + return pFurnaceTileEntity->getLitProgress( 12 ); + } + + return 0; +} + +void CXuiCtrlFireProgress::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = 12; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.h new file mode 100644 index 0000000..595072e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_FireProgress.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +class CXuiCtrlFireProgress : public CXuiCtrlProgressCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlFireProgress, L"CXuiCtrlFireProgress", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.cpp new file mode 100644 index 0000000..3bb3344 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" + +#include "XUI_Ctrl_LoadingProgress.h" +#include "..\..\Minecraft.h" +#include "..\..\ProgressRenderer.h" + +int CXuiCtrlLoadingProgress::GetValue() +{ + int currentValue = 0; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + currentValue = pMinecraft->progressRenderer->getCurrentPercent(); + //printf("About to render progress of %d\n", currentValue); + return currentValue; +} + +void CXuiCtrlLoadingProgress::GetRange(int *pnRangeMin, int *pnRangeMax) +{ + *pnRangeMin = 0; + *pnRangeMax = 100; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.h new file mode 100644 index 0000000..623393b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_LoadingProgress.h @@ -0,0 +1,18 @@ +#pragma once + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +class CXuiCtrlLoadingProgress : public CXuiCtrlProgressCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlLoadingProgress, L"CXuiCtrlLoadingProgress", XUI_CLASS_PROGRESSBAR ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + virtual int GetValue(); + virtual void GetRange(int *pnRangeMin, int *pnRangeMax); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.cpp new file mode 100644 index 0000000..02ee7c9 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.cpp @@ -0,0 +1,190 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\EntityRenderDispatcher.h" +#include "..\..\Lighting.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "XUI_Ctrl_MinecraftPlayer.h" +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_Scene_Inventory.h" +#include "..\..\Options.h" + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftPlayer class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CXuiCtrlMinecraftPlayer::CXuiCtrlMinecraftPlayer() : + m_bDirty(FALSE), + m_fScale(1.0f), + m_fAlpha(1.0f) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlMinecraftPlayer::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + HXUIOBJ parent = m_hObj; + HXUICLASS hcInventoryClass = XuiFindClass( L"CXuiSceneInventory" ); + HXUICLASS currentClass; + + do + { + XuiElementGetParent(parent,&parent); + currentClass = XuiGetObjectClass( parent ); + } while (parent != NULL && !XuiClassDerivesFrom( currentClass, hcInventoryClass ) ); + + assert( parent != NULL ); + + VOID *pObj; + XuiObjectFromHandle( parent, &pObj ); + m_containerScene = (CXuiSceneInventory *)pObj; + + m_iPad = m_containerScene->getPad(); + + return hr; +} + +HRESULT CXuiCtrlMinecraftPlayer::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ +#ifdef _XBOX + HXUIDC hDC = pRenderData->hDC; + + // build and render with the game call + + RenderManager.Set_matrixDirty(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + glColor4f(1, 1, 1, 1); + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fRawWidth, m_fRawHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + + D3DXMATRIX matrix; + CXuiControl xuiControl(m_hObj); + xuiControl.GetFullXForm(&matrix); + float bwidth,bheight; + xuiControl.GetBounds(&bwidth,&bheight); + + float xo = ( (matrix._41 + ( (bwidth*matrix._11)/2) ) / m_fScreenWidth ) * m_fRawWidth; + float yo = ( (matrix._42 + (bheight*matrix._22) ) / m_fScreenHeight ) * m_fRawHeight; + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glPushMatrix(); + glTranslatef(xo, yo - 3.5f, 50.0f); + float ss;// = 26; + + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + ss = 24; + } + else + { + if(app.GetLocalPlayerCount()>1) + { + ss = 13; + } + else + { + ss = 26; + } + } + // Base scale on height of this control + // Potentially we might want separate x & y scales here + //ss = ( ( bheight / m_fScreenHeight ) * m_fRawHeight ); + // For testing split screen - this scale is correct for 4 player split screen + + // how many local players do we have? +// int iPlayerC=0; +// +// for(int i=0;ilocalplayers[i] != NULL) +// { +// iPlayerC++; +// } +// } +// +// switch(iPlayerC) +// { +// case 1: +// break; +// case 2: +// case 3: +// case 4: +// ss *= 0.5f; +// break; +// } + + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + float oybr = pMinecraft->localplayers[m_iPad]->yBodyRot; + float oyr = pMinecraft->localplayers[m_iPad]->yRot; + float oxr = pMinecraft->localplayers[m_iPad]->xRot; + float oyhr = pMinecraft->localplayers[m_iPad]->yHeadRot; + + D3DXVECTOR3 pointerPosition = m_containerScene->GetCursorScreenPosition(); + //printf("Pointer screen position is x:%f, y:%f, z:%f\n",pointerPosition.x, pointerPosition.y, pointerPosition.z); + + float xd = ( matrix._41 + ( (bwidth*matrix._11)/2) ) - pointerPosition.x; + + // Need to base Y on head position, not centre of mass + float yd = ( matrix._42 + ( (bheight*matrix._22) / 2) - 40 ) - pointerPosition.y; + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float) atan(yd / 40.0f) * 20, 1, 0, 0); + + pMinecraft->localplayers[m_iPad]->yBodyRot = (float) atan(xd / 40.0f) * 20; + pMinecraft->localplayers[m_iPad]->yRot = (float) atan(xd / 40.0f) * 40; + pMinecraft->localplayers[m_iPad]->xRot = -(float) atan(yd / 40.0f) * 20; + pMinecraft->localplayers[m_iPad]->yHeadRot = pMinecraft->localplayers[m_iPad]->yRot; + //pMinecraft->localplayers[m_iPad]->glow = 1; + glTranslatef(0, pMinecraft->localplayers[m_iPad]->heightOffset, 0); + EntityRenderDispatcher::instance->playerRotY = 180; + + // 4J Stu - Turning on hideGui while we do this stops the name rendering in split-screen + bool wasHidingGui = pMinecraft->options->hideGui; + pMinecraft->options->hideGui = true; + EntityRenderDispatcher::instance->render(pMinecraft->localplayers[m_iPad], 0, 0, 0, 0, 1,false,false); + pMinecraft->options->hideGui = wasHidingGui; + //pMinecraft->localplayers[m_iPad]->glow = 0; + + pMinecraft->localplayers[m_iPad]->yBodyRot = oybr; + pMinecraft->localplayers[m_iPad]->yRot = oyr; + pMinecraft->localplayers[m_iPad]->xRot = oxr; + pMinecraft->localplayers[m_iPad]->yHeadRot = oyhr; + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + XuiRenderRestoreState(hDC); + + bHandled = TRUE; +#endif + return S_OK; + +} + + + diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.h new file mode 100644 index 0000000..fa28d6b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftPlayer.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +using namespace std; + +class TileRenderer; +class ItemRenderer; +class CXuiSceneInventory; + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftPlayer class +//----------------------------------------------------------------------------- +class CXuiCtrlMinecraftPlayer : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlMinecraftPlayer, L"CXuiCtrlMinecraftPlayer", XUI_CLASS_LABEL) + + CXuiCtrlMinecraftPlayer(); + virtual ~CXuiCtrlMinecraftPlayer() { }; + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + BOOL m_bDirty; + float m_fScale,m_fAlpha; + int m_iPad; + CXuiSceneInventory *m_containerScene; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.cpp new file mode 100644 index 0000000..80750de --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.cpp @@ -0,0 +1,551 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\EntityRenderDispatcher.h" +#include "..\..\PlayerRenderer.h" +#include "..\..\HumanoidModel.h" +#include "..\..\Lighting.h" +#include "..\..\..\Minecraft.World\Class.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "XUI_Ctrl_MinecraftSkinPreview.h" +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_Scene_Inventory.h" +#include "..\..\Options.h" +#include "..\..\stubs.h" +#include "..\..\ModelPart.h" + +//#define SKIN_PREVIEW_BOB_ANIM +#define SKIN_PREVIEW_WALKING_ANIM + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSkinPreview class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CXuiCtrlMinecraftSkinPreview::CXuiCtrlMinecraftSkinPreview() : + m_bDirty(FALSE), + m_fScale(1.0f), + m_fAlpha(1.0f) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; + + m_customTextureUrl = L"default"; + m_backupTexture = TN_MOB_CHAR; + m_capeTextureUrl = L""; + + m_yRot = 0; + m_xRot = 0; + + m_swingTime = 0.0f; + m_bobTick = 0.0f; + m_walkAnimSpeedO = 0.0f; + m_walkAnimSpeed = 0.0f; + m_walkAnimPos = 0.0f; + + m_bAutoRotate = false; + m_bRotatingLeft = false; + + m_incXRot = false; + m_decXRot = false; + m_incYRot = false; + m_decYRot = false; + + m_currentAnimation = e_SkinPreviewAnimation_Walking; + + m_fTargetRotation = 0.0f; + m_fOriginalRotation = 0.0f; + m_framesAnimatingRotation = 0; + m_bAnimatingToFacing = false; + m_pvAdditionalModelParts=NULL; + m_uiAnimOverrideBitmask=0L; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlMinecraftSkinPreview::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + + return hr; +} + +void CXuiCtrlMinecraftSkinPreview::SetTexture(const wstring &url, TEXTURE_NAME backupTexture) +{ + m_customTextureUrl = url; + m_backupTexture = backupTexture; + + unsigned int uiAnimOverrideBitmask = Player::getSkinAnimOverrideBitmask( app.getSkinIdFromPath(m_customTextureUrl) ); + + if(app.GetGameSettings(eGameSetting_CustomSkinAnim)==0 ) + { + // We have a force animation for some skins (claptrap) + // 4J-PB - treat all the eAnim_Disable flags as a force anim + + if((uiAnimOverrideBitmask & HumanoidModel::m_staticBitmaskIgnorePlayerCustomAnimSetting)!=0) + { + m_uiAnimOverrideBitmask=uiAnimOverrideBitmask; + } + else + { + m_uiAnimOverrideBitmask=0; + } + } + else + { + m_uiAnimOverrideBitmask = uiAnimOverrideBitmask; + } + + app.DebugPrintf("+++ SetTexture - %d, %8x\n",app.getSkinIdFromPath(m_customTextureUrl)&0xFFFFFFF,m_uiAnimOverrideBitmask); + m_pvAdditionalModelParts=app.GetAdditionalModelParts(app.getSkinIdFromPath(m_customTextureUrl)); +} + +void CXuiCtrlMinecraftSkinPreview::SetFacing(ESkinPreviewFacing facing, bool bAnimate /*= false*/) +{ + switch(facing) + { + case e_SkinPreviewFacing_Forward: + m_fTargetRotation = 0; + m_bRotatingLeft = true; + break; + case e_SkinPreviewFacing_Left: + m_fTargetRotation = LOOK_LEFT_EXTENT; + m_bRotatingLeft = false; + break; + case e_SkinPreviewFacing_Right: + m_fTargetRotation = LOOK_RIGHT_EXTENT; + m_bRotatingLeft = true; + break; + } + + if(!bAnimate) + { + m_yRot = m_fTargetRotation; + m_bAnimatingToFacing = false; + } + else + { + m_fOriginalRotation = m_yRot; + m_bAnimatingToFacing = true; + m_framesAnimatingRotation = 0; + } +} + +void CXuiCtrlMinecraftSkinPreview::CycleNextAnimation() +{ + m_currentAnimation = (ESkinPreviewAnimations)(m_currentAnimation + 1); + if(m_currentAnimation >= e_SkinPreviewAnimation_Count) m_currentAnimation = e_SkinPreviewAnimation_Walking; + + m_swingTime = 0.0f; +} + +void CXuiCtrlMinecraftSkinPreview::CyclePreviousAnimation() +{ + m_currentAnimation = (ESkinPreviewAnimations)(m_currentAnimation - 1); + if(m_currentAnimation < e_SkinPreviewAnimation_Walking) m_currentAnimation = (ESkinPreviewAnimations)(e_SkinPreviewAnimation_Count - 1); + + m_swingTime = 0.0f; +} + +HRESULT CXuiCtrlMinecraftSkinPreview::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ + if( m_bAnimatingToFacing ) + { + ++m_framesAnimatingRotation; + m_yRot = m_fOriginalRotation + m_framesAnimatingRotation * ( (m_fTargetRotation - m_fOriginalRotation) / CHANGING_SKIN_FRAMES ); + + //if(m_framesAnimatingRotation == CHANGING_SKIN_FRAMES) m_bAnimatingToFacing = false; + } + else + { + if( m_incXRot ) IncrementXRotation(); + if( m_decXRot ) DecrementXRotation(); + if( m_incYRot ) IncrementYRotation(); + if( m_decYRot ) DecrementYRotation(); + + if(m_bAutoRotate) + { + ++m_rotateTick; + + if(m_rotateTick%4==0) + { + if(m_yRot >= LOOK_LEFT_EXTENT) + { + m_bRotatingLeft = false; + } + else if(m_yRot <= LOOK_RIGHT_EXTENT) + { + m_bRotatingLeft = true; + } + + if(m_bRotatingLeft) + { + IncrementYRotation(); + } + else + { + DecrementYRotation(); + } + } + } + } + + HXUIDC hDC = pRenderData->hDC; + + // build and render with the game call + + RenderManager.Set_matrixDirty(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + float alpha = 1.0f; + //GetOpacity( &alpha ); + + glColor4f(1, 1, 1, alpha); + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fRawWidth, m_fRawHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + + D3DXMATRIX matrix; + GetFullXForm(&matrix); + float bwidth,bheight; + GetBounds(&bwidth,&bheight); + + float xo = ( (matrix._41 + ( (bwidth*matrix._11)/2) ) / m_fScreenWidth ) * m_fRawWidth; + float yo = ( (matrix._42 + (bheight*matrix._22) ) / m_fScreenHeight ) * m_fRawHeight; + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glPushMatrix(); + glTranslatef(xo, yo - 3.5f, 50.0f); + float ss; + + // Base scale on height of this control + // Potentially we might want separate x & y scales here + ss = ( ( (bheight*matrix._22) / m_fScreenHeight ) * m_fRawHeight )/2; + + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + //glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + //glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float)m_xRot, 1, 0, 0); + + // 4J Stu - Turning on hideGui while we do this stops the name rendering in split-screen + bool wasHidingGui = pMinecraft->options->hideGui; + pMinecraft->options->hideGui = true; + + //EntityRenderDispatcher::instance->render(pMinecraft->localplayers[0], 0, 0, 0, 0, 1); + EntityRenderer *renderer = EntityRenderDispatcher::instance->getRenderer(eTYPE_PLAYER); + if (renderer != NULL) + { + // 4J-PB - any additional parts to turn on for this player (skin dependent) + //vector *pAdditionalModelParts=mob->GetAdditionalModelParts(); + + if(m_pvAdditionalModelParts && m_pvAdditionalModelParts->size()!=0) + { + for(AUTO_VAR(it, m_pvAdditionalModelParts->begin()); it != m_pvAdditionalModelParts->end(); ++it) + { + ModelPart *pModelPart=*it; + + pModelPart->visible=true; + } + } + + render(renderer,0,0,0,0,1); + //renderer->postRender(entity, x, y, z, rot, a); + + // hide the additional parts + if(m_pvAdditionalModelParts && m_pvAdditionalModelParts->size()!=0) + { + for(AUTO_VAR(it, m_pvAdditionalModelParts->begin()); it != m_pvAdditionalModelParts->end(); ++it) + { + ModelPart *pModelPart=*it; + + pModelPart->visible=false; + } + } + } + + pMinecraft->options->hideGui = wasHidingGui; + + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + XuiRenderRestoreState(hDC); + + bHandled = TRUE; + + return S_OK; +} + +// 4J Stu - Modified version of MobRenderer::render that does not require an actual entity +void CXuiCtrlMinecraftSkinPreview::render(EntityRenderer *renderer, double x, double y, double z, float rot, float a) +{ + glPushMatrix(); + glDisable(GL_CULL_FACE); + + HumanoidModel *model = (HumanoidModel *)renderer->getModel(); + + //getAttackAnim(mob, a); + //if (armor != NULL) armor->attackTime = model->attackTime; + //model->riding = mob->isRiding(); + //if (armor != NULL) armor->riding = model->riding; + + // 4J Stu - Remember to reset these values once the rendering is done if you add another one + model->attackTime = 0; + model->sneaking = false; + model->holdingRightHand = false; + model->holdingLeftHand = false; + model->idle = false; + model->eating = false; + model->eating_swing = 0; + model->eating_t = 0; + model->young = false; + model->riding = false; + + model->m_uiAnimOverrideBitmask = m_uiAnimOverrideBitmask; + + if( !m_bAnimatingToFacing ) + { + switch( m_currentAnimation ) + { + case e_SkinPreviewAnimation_Sneaking: + model->sneaking = true; + break; + case e_SkinPreviewAnimation_Attacking: + model->holdingRightHand = true; + m_swingTime++; + if (m_swingTime >= (Player::SWING_DURATION * 3) ) + { + m_swingTime = 0; + } + model->attackTime = m_swingTime / (float) (Player::SWING_DURATION * 3); + break; + default: + break; + }; + } + + + float bodyRot = m_yRot; //(mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a); + float headRot = m_yRot; //(mob->yRotO + (mob->yRot - mob->yRotO) * a); + float headRotx = 0; //(mob->xRotO + (mob->xRot - mob->xRotO) * a); + + //setupPosition(mob, x, y, z); + // is equivalent to + glTranslatef((float) x, (float) y, (float) z); + + //float bob = getBob(mob, a); +#ifdef SKIN_PREVIEW_BOB_ANIM + float bob = (m_bobTick + a)/2; + + ++m_bobTick; + if(m_bobTick>=360*2) m_bobTick = 0; +#else + float bob = 0.0f; +#endif + + //setupRotations(mob, bob, bodyRot, a); + // is equivalent to + glRotatef(180 - bodyRot, 0, 1, 0); + + float _scale = 1 / 16.0f; + glEnable(GL_RESCALE_NORMAL); + glScalef(-1, -1, 1); + + //scale(mob, a); + // is equivalent to + float s = 15 / 16.0f; + glScalef(s, s, s); + + glTranslatef(0, -24 * _scale - 0.125f / 16.0f, 0); + +#ifdef SKIN_PREVIEW_WALKING_ANIM + m_walkAnimSpeedO = m_walkAnimSpeed; + m_walkAnimSpeed += (0.1f - m_walkAnimSpeed) * 0.4f; + m_walkAnimPos += m_walkAnimSpeed; + float ws = m_walkAnimSpeedO + (m_walkAnimSpeed - m_walkAnimSpeedO) * a; + float wp = m_walkAnimPos - m_walkAnimSpeed * (1 - a); +#else + float ws = 0; + float wp = 0; +#endif + + if (ws > 1) ws = 1; + + MemSect(31); + bindTexture(m_customTextureUrl, m_backupTexture); + MemSect(0); + glEnable(GL_ALPHA_TEST); + + //model->prepareMobModel(mob, wp, ws, a); + model->render(nullptr, wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + /*for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmor(mob, i, a)) + { + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + } + }*/ + + //additionalRendering(mob, a); + if (bindTexture(m_capeTextureUrl, L"" )) + { + glPushMatrix(); + glTranslatef(0, 0, 2 / 16.0f); + + double xd = 0;//(mob->xCloakO + (mob->xCloak - mob->xCloakO) * a) - (mob->xo + (mob->x - mob->xo) * a); + double yd = 0;//(mob->yCloakO + (mob->yCloak - mob->yCloakO) * a) - (mob->yo + (mob->y - mob->yo) * a); + double zd = 0;//(mob->zCloakO + (mob->zCloak - mob->zCloakO) * a) - (mob->zo + (mob->z - mob->zo) * a); + + float yr = 1;//mob->yBodyRotO + (mob->yBodyRot - mob->yBodyRotO) * a; + + double xa = sin(yr * PI / 180); + double za = -cos(yr * PI / 180); + + float flap = (float) yd * 10; + if (flap < -6) flap = -6; + if (flap > 32) flap = 32; + float lean = (float) (xd * xa + zd * za) * 100; + float lean2 = (float) (xd * za - zd * xa) * 100; + if (lean < 0) lean = 0; + + //float pow = 1;//mob->oBob + (bob - mob->oBob) * a; + + flap += 1;//sin((mob->walkDistO + (mob->walkDist - mob->walkDistO) * a) * 6) * 32 * pow; + if (model->sneaking) + { + flap += 25; + } + + glRotatef(6.0f + lean / 2 + flap, 1, 0, 0); + glRotatef(lean2 / 2, 0, 0, 1); + glRotatef(-lean2 / 2, 0, 1, 0); + glRotatef(180, 0, 1, 0); + model->renderCloak(1 / 16.0f,true); + glPopMatrix(); + } + /* + float br = mob->getBrightness(a); + int overlayColor = getOverlayColor(mob, br, a); + + if (((overlayColor >> 24) & 0xff) > 0 || mob->hurtTime > 0 || mob->deathTime > 0) + { + glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_EQUAL); + + // 4J - changed these renders to not use the compiled version of their models, because otherwise the render states set + // about (in particular the depth & alpha test) don't work with our command buffer versions + if (mob->hurtTime > 0 || mob->deathTime > 0) + { + glColor4f(br, 0, 0, 0.4f); + model->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a)) + { + glColor4f(br, 0, 0, 0.4f); + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + if (((overlayColor >> 24) & 0xff) > 0) + { + float r = ((overlayColor >> 16) & 0xff) / 255.0f; + float g = ((overlayColor >> 8) & 0xff) / 255.0f; + float b = ((overlayColor) & 0xff) / 255.0f; + float aa = ((overlayColor >> 24) & 0xff) / 255.0f; + glColor4f(r, g, b, aa); + model->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a)) + { + glColor4f(r, g, b, aa); + armor->render(wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + glDepthFunc(GL_LEQUAL); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_TEXTURE_2D); + } + */ + glDisable(GL_RESCALE_NORMAL); + + glEnable(GL_CULL_FACE); + + glPopMatrix(); + + MemSect(31); + //renderName(mob, x, y, z); + MemSect(0); + + // Reset the model values to stop the changes we made here affecting anything in game (like the player hand render) + model->attackTime = 0; + model->sneaking = false; + model->holdingRightHand = false; + model->holdingLeftHand = false; +} + +bool CXuiCtrlMinecraftSkinPreview::bindTexture(const wstring& urlTexture, int backupTexture) +{ + Textures *t = Minecraft::GetInstance()->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + t->bind(id); + return true; + } + else + { + return false; + } +} + +bool CXuiCtrlMinecraftSkinPreview::bindTexture(const wstring& urlTexture, const wstring& backupTexture) +{ + Textures *t = Minecraft::GetInstance()->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + t->bind(id); + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.h new file mode 100644 index 0000000..22f4991 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSkinPreview.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include "..\..\Textures.h" +//#include "..\..\Xbox\DLC\DLCSkinFile.h" +#include "..\..\Model.h" + +using namespace std; + +class EntityRenderer; + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSkinPreview class +//----------------------------------------------------------------------------- +class CXuiCtrlMinecraftSkinPreview : public CXuiControlImpl +{ +private: + static const int LOOK_LEFT_EXTENT = 45; + static const int LOOK_RIGHT_EXTENT = -45; + + static const int CHANGING_SKIN_FRAMES = 15; + + enum ESkinPreviewAnimations + { + e_SkinPreviewAnimation_Walking, + e_SkinPreviewAnimation_Sneaking, + e_SkinPreviewAnimation_Attacking, + + e_SkinPreviewAnimation_Count, + }; +public: + enum ESkinPreviewFacing + { + e_SkinPreviewFacing_Forward, + e_SkinPreviewFacing_Left, + e_SkinPreviewFacing_Right, + }; +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlMinecraftSkinPreview, L"CXuiCtrlMinecraftSkinPreview", XUI_CLASS_LABEL) + + CXuiCtrlMinecraftSkinPreview(); + virtual ~CXuiCtrlMinecraftSkinPreview() { }; + + void SetTexture(const wstring &url, TEXTURE_NAME backupTexture = TN_MOB_CHAR); + void SetCapeTexture(const wstring &url) { m_capeTextureUrl = url; } + void ResetRotation() { m_xRot = 0; m_yRot = 0; } + void IncrementYRotation() { m_yRot = (m_yRot+4); if(m_yRot >= 180) m_yRot = -180; } + void DecrementYRotation() { m_yRot = (m_yRot-4); if(m_yRot <= -180) m_yRot = 180; } + void IncrementXRotation() { m_xRot = (m_xRot+2); if(m_xRot > 22) m_xRot = 22; } + void DecrementXRotation() { m_xRot = (m_xRot-2); if(m_xRot < -22) m_xRot = -22; } + void SetAutoRotate(bool autoRotate) { m_bAutoRotate = autoRotate; } + void SetFacing(ESkinPreviewFacing facing, bool bAnimate = false); + + void CycleNextAnimation(); + void CyclePreviousAnimation(); + + bool m_incXRot, m_decXRot; + bool m_incYRot, m_decYRot; + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + void render(EntityRenderer *renderer, double x, double y, double z, float rot, float a); + bool bindTexture(const wstring& urlTexture, int backupTexture); + bool bindTexture(const wstring& urlTexture, const wstring& backupTexture); + + BOOL m_bDirty; + float m_fScale,m_fAlpha; + + wstring m_customTextureUrl; + TEXTURE_NAME m_backupTexture; + wstring m_capeTextureUrl; + unsigned int m_uiAnimOverrideBitmask; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + + int m_yRot,m_xRot; + + float m_bobTick; + + float m_walkAnimSpeedO; + float m_walkAnimSpeed; + float m_walkAnimPos; + + bool m_bAutoRotate, m_bRotatingLeft; + BYTE m_rotateTick; + float m_fTargetRotation, m_fOriginalRotation; + int m_framesAnimatingRotation; + bool m_bAnimatingToFacing; + + float m_swingTime; + + ESkinPreviewAnimations m_currentAnimation; + //vector *m_pvAdditionalBoxes; + vector *m_pvAdditionalModelParts; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.cpp new file mode 100644 index 0000000..84273eb --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.cpp @@ -0,0 +1,356 @@ +#include "stdafx.h" + +#include "..\..\ItemRenderer.h" +#include "..\..\GameRenderer.h" +#include "..\..\TileRenderer.h" +#include "..\..\Lighting.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\LocalPlayer.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" +#include "..\..\..\Minecraft.World\Item.h" +#include "..\..\..\Minecraft.World\Tile.h" +#include "XUI_Ctrl_MinecraftSlot.h" + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSlot class +//----------------------------------------------------------------------------- + +// The xzp path icons for the leaderboard + +LPCWSTR CXuiCtrlMinecraftSlot::xzpIcons[15]= +{ + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Skeleton.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Creeper.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_SpiderJockey.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Spider.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Zombie.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_ZombiePigman.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Swam.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Walked.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Fallen.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Portal.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Climbed.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Ghast.png", + L"Graphics\\Leaderboard\\LeaderBoard_Icon_Slime.png", + L"Graphics\\CraftIcons\\icon_structures.png", + L"Graphics\\CraftIcons\\icon_tools.png", +}; + + + +//----------------------------------------------------------------------------- +CXuiCtrlMinecraftSlot::CXuiCtrlMinecraftSlot() : + //m_hBrush(NULL), + m_bDirty(FALSE), + m_iPassThroughDataAssociation(0), + m_iPassThroughIdAssociation(0), + m_fScale(1.0f), + m_fAlpha(1.0f), + m_iID(0), + m_iCount(0), + m_iAuxVal(0), + m_bDecorations(false), + m_isFoil(false), + m_popTime(0) +{ + m_pItemRenderer = new ItemRenderer(); + m_item = nullptr; + + m_bScreenWidthSetup = false; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + if(pMinecraft != NULL) + { + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_bScreenWidthSetup = true; + } +} + +CXuiCtrlMinecraftSlot::~CXuiCtrlMinecraftSlot() +{ + delete m_pItemRenderer; +} + +VOID CXuiCtrlMinecraftSlot::SetPassThroughDataAssociation(unsigned int iID, unsigned int iData) +{ + m_item = nullptr; + m_iPassThroughIdAssociation = iID; + m_iPassThroughDataAssociation = iData; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlMinecraftSlot::OnGetSourceImage(XUIMessageGetSourceImage* pData, BOOL& rfHandled) +{ + XUIMessage Message; + CustomMessage_GetSlotItem_Struct MsgGetSlotItem; + HRESULT hr; + HXUIOBJ hObj; + + CustomMessage_GetSlotItem(&Message, &MsgGetSlotItem, m_iPassThroughIdAssociation, m_iPassThroughDataAssociation); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + + if(hr == XUI_ERR_SOURCEDATA_ITEM) + { + // Go up the parent chain one more + HXUIOBJ hParent; + hr = XuiElementGetParent(hObj,&hParent); + hr = XuiBubbleMessage(hParent, &Message); + } + + if (Message.bHandled) + { + pData->szPath = MsgGetSlotItem.szPath; + pData->bDirty = MsgGetSlotItem.bDirty; + + if(MsgGetSlotItem.item != NULL) + { + m_item = MsgGetSlotItem.item; + m_iID = m_item->id; + m_iPad = GET_SLOTDISPLAY_USERINDEX_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + m_fAlpha = ((float)GET_SLOTDISPLAY_ALPHA_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField))/31.0f; + m_bDecorations = GET_SLOTDISPLAY_DECORATIONS_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + m_fScale = ((float)GET_SLOTDISPLAY_SCALE_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField))/10.0f; + } + else + { + m_iID = GET_SLOTDISPLAY_ID_FROM_ITEM_BITMASK(MsgGetSlotItem.iItemBitField); + + // if the id is greater than or equal to 32000, then it's an xzp icon, not a game icon + if(m_iID<32000) + { + // 4J Stu - Some parent controls may overide this, others will leave it as what we passed in + + m_iPad = GET_SLOTDISPLAY_USERINDEX_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + m_fAlpha = ((float)GET_SLOTDISPLAY_ALPHA_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField))/31.0f; + m_bDecorations = GET_SLOTDISPLAY_DECORATIONS_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + m_iCount = GET_SLOTDISPLAY_COUNT_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + m_fScale = ((float)GET_SLOTDISPLAY_SCALE_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField))/10.0f; + m_popTime = GET_SLOTDISPLAY_POPTIME_FROM_DATA_BITMASK(MsgGetSlotItem.iDataBitField); + + m_iAuxVal = GET_SLOTDISPLAY_AUXVAL_FROM_ITEM_BITMASK(MsgGetSlotItem.iItemBitField); + + //m_iID = iID; + + m_isFoil = GET_SLOTDISPLAY_FOIL_FROM_ITEM_BITMASK(MsgGetSlotItem.iItemBitField); + } + else + { + pData->szPath = xzpIcons[m_iID-32000]; + } + + if(m_item != NULL && (m_item->id != m_iID || m_item->getAuxValue() != m_iAuxVal || m_item->GetCount() != m_iCount) ) m_item = nullptr; + } + + + rfHandled = TRUE; + return hr; + } + else + { + pData->szPath = L""; + } + } + + pData->bDirty = m_bDirty; + m_bDirty = FALSE; + rfHandled = TRUE; + return S_OK; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlMinecraftSlot::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + //DWORD dwPropId; + HRESULT hr=S_OK; + return hr; +} + +//----------------------------------------------------------------------------- + +HRESULT CXuiCtrlMinecraftSlot::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ + // Force an update of the id + XUIMessage Message; + XUIMessageGetSourceImage MsgGetImage; + HRESULT hr; + XuiMessageGetSourceImage(&Message, &MsgGetImage, m_iPassThroughIdAssociation, m_iPassThroughDataAssociation, FALSE); + hr = XuiSendMessage(m_hObj, &Message); + + // We cannot have an Item with id 0 + if(m_item != NULL || (m_iID > 0 && m_iID<32000) ) + { + HXUIDC hDC = pRenderData->hDC; + CXuiControl xuiControl(m_hObj); + if(m_item == NULL) m_item = shared_ptr( new ItemInstance(m_iID, m_iCount, m_iAuxVal) ); + + // build and render with the game call + + RenderManager.Set_matrixDirty(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + D3DXMATRIX matrix; + xuiControl.GetFullXForm(&matrix); + float bwidth,bheight; + xuiControl.GetBounds(&bwidth,&bheight); + + float x = matrix._41; + float y = matrix._42; + + // Base scale on height of this control, compared to height of what the item renderer normally renders (16 pixels high). Potentially + // we might want separate x & y scales here + + float scaleX = bwidth / 16.0f; + float scaleY = bheight / 16.0f; + + // apply any scale in the matrix too + scaleX *= matrix._11; + scaleY *= matrix._22; + + // Annoyingly, XUI renders everything to a z of 0 so if we want to render anything that needs the z-buffer on top of it, then we need to clear it. + // Clear just the region required for this control. + D3DRECT clearRect; + clearRect.x1 = (int)(matrix._41) - 2; + clearRect.y1 = (int)(matrix._42) - 2; + clearRect.x2 = (int)(matrix._41 + ( bwidth * matrix._11 )) + 2; + clearRect.y2 = (int)(matrix._42 + ( bheight * matrix._22 )) + 2; + + if(!m_bScreenWidthSetup) + { + Minecraft *pMinecraft=Minecraft::GetInstance(); + if(pMinecraft != NULL) + { + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_bScreenWidthSetup = true; + } + } + + RenderManager.Clear(GL_DEPTH_BUFFER_BIT, &clearRect); + // glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, m_fScreenWidth, m_fScreenHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + + glEnable(GL_RESCALE_NORMAL); + glPushMatrix(); + glRotatef(120, 1, 0, 0); + Lighting::turnOn(); + glPopMatrix(); + + + + //Make sure that pMinecraft->player is the correct player so that player specific rendering + // eg clock and compass, are rendered correctly + shared_ptr oldPlayer = pMinecraft->player; + + if( m_iPad >= 0 && m_iPad < XUSER_MAX_COUNT ) pMinecraft->player = pMinecraft->localplayers[m_iPad]; + + float pop = m_popTime; + if (pop > 0) + { + glPushMatrix(); + float squeeze = 1 + pop / (float) Inventory::POP_TIME_DURATION; + float sx = x; + float sy = y; + float sxoffs = 8 * scaleX; + float syoffs = 12 * scaleY; + glTranslatef((float)(sx + sxoffs), (float)(sy + syoffs), 0); + glScalef(1 / squeeze, (squeeze + 1) / 2, 1); + glTranslatef((float)-(sx + sxoffs), (float)-(sy + syoffs), 0); + } + + m_pItemRenderer->renderAndDecorateItem(pMinecraft->font, pMinecraft->textures, m_item, x, y,scaleX,scaleY,m_fAlpha,m_isFoil,false); + + if (pop > 0) + { + glPopMatrix(); + } + + if(m_bDecorations) + { + if((scaleX!=1.0f) ||(scaleY!=1.0f)) + { + glPushMatrix(); + glScalef(scaleX, scaleY, 1.0f); + int iX= (int)(0.5f+((float)x)/scaleX); + int iY= (int)(0.5f+((float)y)/scaleY); + + m_pItemRenderer->renderGuiItemDecorations(pMinecraft->font, pMinecraft->textures, m_item, iX, iY, m_fAlpha); + glPopMatrix(); + } + else + { + m_pItemRenderer->renderGuiItemDecorations(pMinecraft->font, pMinecraft->textures, m_item, (int)x, (int)y, m_fAlpha); + } + } + + pMinecraft->player = oldPlayer; + + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + + XuiRenderRestoreState(hDC); + + bHandled = TRUE; + } + return S_OK; +} + + +void CXuiCtrlMinecraftSlot::SetIcon(int iPad, int iId,int iAuxVal, int iCount, int iScale, unsigned int uiAlpha,bool bDecorations,BOOL bShow, bool isFoil) +{ + m_item = nullptr; + m_iID=iId; + m_iAuxVal=iAuxVal; + + // aux value for diggers can go as high as 1561 + //const _Tier *_Tier::DIAMOND = new _Tier(3, 1561, 8, 3); // + // setMaxDamage(tier->getUses()); + + // int ItemInstance::getDamageValue() + // { + // return auxValue; + // } + + + if( (m_iAuxVal & 0xFF) == 0xFF) // 4J Stu - If the aux value is set to match any + m_iAuxVal = 0; + + m_iCount=iCount; + m_fScale = (float)(iScale)/10.0f; + //m_uiAlpha=uiAlpha; + m_fAlpha =((float)(uiAlpha)) / 255.0f; + m_bDecorations = bDecorations; + // mif(bDecorations) m_iDecorations=1; + // else m_iDecorations=0; + + m_iPad = iPad; + + m_isFoil = isFoil; + + XuiElementSetShow(m_hObj,bShow); +} + +void CXuiCtrlMinecraftSlot::SetIcon(int iPad, shared_ptr item, int iScale, unsigned int uiAlpha,bool bDecorations, BOOL bShow) +{ + m_item = item; + m_isFoil = item->isFoil(); + m_iPad = iPad; + m_fScale = (float)(iScale)/10.0f; + m_fAlpha =((float)(uiAlpha)) / 31; + m_bDecorations = bDecorations; + m_bDirty = TRUE; + XuiElementSetShow(m_hObj,bShow); +} diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.h new file mode 100644 index 0000000..5dafec5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MinecraftSlot.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +using namespace std; + +class TileRenderer; +class ItemRenderer; + +//----------------------------------------------------------------------------- +// CXuiCtrlMinecraftSlot class +//----------------------------------------------------------------------------- +class CXuiCtrlMinecraftSlot : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlMinecraftSlot, L"CXuiCtrlMinecraftSlot", XUI_CLASS_LABEL) + + VOID SetPassThroughDataAssociation(unsigned int iID, unsigned int iData); + CXuiCtrlMinecraftSlot(); + virtual ~CXuiCtrlMinecraftSlot(); + + void renderGuiItem(Font *font, Textures *textures,ItemInstance *item, int x, int y); + void RenderItem(); + void SetIcon(int iPad, int iId,int iAuxVal, int iCount, int iScale, unsigned int uiAlpha,bool bDecorations,BOOL bShow, bool isFoil); + void SetIcon(int iPad, shared_ptr item, int iScale, unsigned int uiAlpha,bool bDecorations, BOOL bShow=TRUE); + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceImage) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnGetSourceImage(XUIMessageGetSourceImage* pData, BOOL& rfHandled); + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + shared_ptr m_item; + BOOL m_bDirty; + INT m_iPassThroughDataAssociation; + INT m_iPassThroughIdAssociation; + float m_fScale,m_fAlpha; + int m_iPad; + int m_iID; + int m_iCount; + int m_iAuxVal; + bool m_bDecorations; + bool m_isFoil; + int m_popTime; + + bool m_bScreenWidthSetup; + float m_fScreenWidth,m_fScreenHeight; + ItemRenderer *m_pItemRenderer; + + static LPCWSTR xzpIcons[15]; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.cpp new file mode 100644 index 0000000..23b2257 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "XUI_Ctrl_MobEffect.h" + +LPCWSTR CXuiCtrlMobEffect::iconFrameNames[MobEffect::e_MobEffectIcon_COUNT]= +{ + L"Normal", + L"Blindness", + L"Fire_Resistance", + L"Haste", + L"Hunger", + L"Invisibility", + L"Jump_Boost", + L"Mining_Fatigue", + L"Nausea", + L"Night_Vision", + L"Poison", + L"Regeneration", + L"Resistance", + L"Slowness", + L"Speed", + L"Strength", + L"Water_Breathing", + L"Weakness", +}; + +HRESULT CXuiCtrlMobEffect::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + m_icon = MobEffect::e_MobEffectIcon_None; + m_name = L""; + m_duration = L""; + return S_OK; +} + +HRESULT CXuiCtrlMobEffect::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + if( pGetSourceTextData->iData == 1 ) + { + pGetSourceTextData->szText = m_name.c_str(); + pGetSourceTextData->bDisplay = TRUE; + + if(FAILED(PlayVisualRange(iconFrameNames[m_icon],NULL,iconFrameNames[m_icon]))) + { + PlayVisualRange(L"Normal",NULL,L"Normal"); + } + + bHandled = TRUE; + } + else if( pGetSourceTextData->iData == 2 ) + { + pGetSourceTextData->szText = m_duration.c_str(); + pGetSourceTextData->bDisplay = TRUE; + + bHandled = TRUE; + } + return S_OK; +} + +void CXuiCtrlMobEffect::setIcon(MobEffect::EMobEffectIcon icon) +{ + m_icon = icon; +} + +void CXuiCtrlMobEffect::setName(const wstring &name) +{ + m_name = name; +} + +void CXuiCtrlMobEffect::setDuration(const wstring &duration) +{ + m_duration = duration; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.h new file mode 100644 index 0000000..c43e7fe --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_MobEffect.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "..\..\..\Minecraft.World\MobEffect.h" + +class CXuiCtrlMobEffect : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlMobEffect, L"CXuiCtrlMobEffect", XUI_CLASS_CONTROL) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + +public: + void setIcon(MobEffect::EMobEffectIcon icon); + void setName(const wstring &name); + void setDuration(const wstring &duration); + +private: + MobEffect::EMobEffectIcon m_icon; + wstring m_name; + wstring m_duration; + + static LPCWSTR iconFrameNames[MobEffect::e_MobEffectIcon_COUNT]; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_PassThroughList.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_PassThroughList.cpp new file mode 100644 index 0000000..d00762b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_PassThroughList.cpp @@ -0,0 +1,109 @@ +#include "stdafx.h" +#include "XUI_Ctrl_PassThroughList.h" + +HRESULT CXuiCtrlPassThroughList::OnInit(XUIMessageInit *pInitData, BOOL& bHandled) +{ + return S_OK; +} + +HRESULT CXuiCtrlPassThroughList::OnKeyDown(XUIMessageInput* pInputData, BOOL& bHandled) +{ + XUIMessage message; + XUIMessageInput messageInput; + HRESULT hr; + HXUIOBJ hObj; + + XuiMessageInput( &message, &messageInput, XUI_KEYDOWN, pInputData->dwKeyCode, pInputData->wch, pInputData->dwFlags, pInputData->UserIndex ); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &message); + + if (message.bHandled) + { + bHandled = TRUE; + } + } + + return S_OK; +} + +// Gets called every frame +HRESULT CXuiCtrlPassThroughList::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData,BOOL& bHandled) +{ + XUIMessage Message; + XUIMessageGetSourceText MsgGetText; + HRESULT hr; + HXUIOBJ hObj; + + + XuiMessageGetSourceText(&Message, &MsgGetText, pGetSourceTextData->iItem, pGetSourceTextData->iData, pGetSourceTextData->bItemData); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + + if (Message.bHandled) + { + pGetSourceTextData->szText = MsgGetText.szText; + bHandled = TRUE; + } + } + return S_OK; +} + +// Gets called every frame +HRESULT CXuiCtrlPassThroughList::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + XUIMessage Message; + XUIMessageGetSourceImage MsgGetImage; + HRESULT hr; + HXUIOBJ hObj; + + + XuiMessageGetSourceImage(&Message, &MsgGetImage, pGetSourceImageData->iItem, pGetSourceImageData->iData, pGetSourceImageData->bItemData); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + + if (Message.bHandled) + { + pGetSourceImageData->szPath = MsgGetImage.szPath; + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CXuiCtrlPassThroughList::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData,BOOL& bHandled) +{ + XUIMessage Message; + XUIMessageGetItemCount MsgGetItemCountAll; + HRESULT hr; + HXUIOBJ hObj; + + + XuiMessageGetItemCount(&Message, &MsgGetItemCountAll, XUI_ITEMCOUNT_ALL); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + + if (Message.bHandled) + { + pGetItemCountData->cItems = MsgGetItemCountAll.cItems; + bHandled = TRUE; + } + } + bHandled = TRUE; + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_PassthroughList.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_PassthroughList.h new file mode 100644 index 0000000..9e2c1bd --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_PassthroughList.h @@ -0,0 +1,28 @@ +#pragma once + +// 4J Stu - A list control that responds by sending the message to it's parent and +// returning what the parent returns + +class CXuiCtrlPassThroughList : public CXuiListImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlPassThroughList, L"CXuiCtrlPassThroughList", XUI_CLASS_LIST); +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_END_MSG_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& bHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.cpp new file mode 100644 index 0000000..6d1c4ce --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" + +#include "XUI_Ctrl_ProgressCtrlBase.h" + +HRESULT CXuiCtrlProgressCtrlBase::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + // The Xui backend calls GetSourceDataText every frame to get the text for the indexed label + // We don't want to change the label, but take this opportunity to send out a message to ourself + // to update the value of the progress bar + this->SetValue( GetValue() ); + + int min, max; + this->GetRange( &min, &max ); + this->SetRange( min, max ); + + pGetSourceTextData->szText = L""; + pGetSourceTextData->bDisplay = FALSE; + + bHandled = TRUE; + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.h new file mode 100644 index 0000000..7a4302d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_ProgressCtrlBase.h @@ -0,0 +1,11 @@ +#pragma once + +class CXuiCtrlProgressCtrlBase : public CXuiProgressBar, public CXuiElementImplBase +{ +public: + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + + // Override these in the derived classes to return the values to be displayed on the control + virtual int GetValue() = 0; + virtual void GetRange(int *pnRangeMin, int *pnRangeMax) = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.cpp new file mode 100644 index 0000000..a4e9f6b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.cpp @@ -0,0 +1,174 @@ +#include "stdafx.h" +#include "XUI_Ctrl_SliderWrapper.h" + +#define NO_SOUND_TIMER 0 + +HRESULT CXuiCtrlSliderWrapper::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + VOID *pObj; + HXUIOBJ hObjChild; + + XuiElementGetChildById(m_hObj,L"FocusSink",&hObjChild); + XuiObjectFromHandle( hObjChild, &pObj ); + m_pFocusSink = (CXuiControl *)pObj; + + XuiElementGetChildById(m_hObj,L"XuiSlider",&hObjChild); + XuiObjectFromHandle( hObjChild, &pObj ); + m_pSlider = (CXuiSlider *)pObj; + + m_sliderActive = false; + m_bDisplayVal=true; + m_bPlaySound=false; // make this false to avoid a sound being played in the first setting of the slider value in a scene + XuiSetTimer( m_hObj,NO_SOUND_TIMER,50); + bHandled = TRUE; + return S_OK; +} + +HRESULT CXuiCtrlSliderWrapper::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + +// switch(pInputData->dwKeyCode) +// { + +// case VK_PAD_A: +// // 4J-PB - IGNORE ! +// if(m_sliderActive) +// { +// m_pFocusSink->SetFocus(pInputData->UserIndex); +// m_sliderActive = false; +// } +// else +// { +// m_pSlider->SetFocus(pInputData->UserIndex); +// m_sliderActive = true; +// } +// rfHandled = TRUE; +// +// break; +// default: +// m_pSlider->SetFocus(pInputData->UserIndex); +// m_sliderActive = false; +// break; +// +// } +// + return S_OK; +} + +HRESULT CXuiCtrlSliderWrapper::OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled) +{ + XUIMessage Message; + XUINotify Notify; + XUINotifyValueChanged MsgValueChanged; + HRESULT hr; + HXUIOBJ hObj; + + if(m_bPlaySound) + { + m_bPlaySound=false; + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + XuiSetTimer( m_hObj,NO_SOUND_TIMER,150); + } + + //app.DebugPrintf("Slider val changed - %d\n",pValueChangedData->nValue); + + XuiNotifyValueChanged(&Message,&Notify,&MsgValueChanged,hObjSource,pValueChangedData->nValue); + + hr = GetParent(&hObj); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(hObj, &Message); + rfHandled = TRUE; + } + return S_OK; +} + + +HRESULT CXuiCtrlSliderWrapper::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==NO_SOUND_TIMER) + { + XuiKillTimer(m_hObj,NO_SOUND_TIMER); + m_bPlaySound=true; + } + + return S_OK; +} + +HRESULT CXuiCtrlSliderWrapper::SetValue( int nValue ) +{ + CXuiCtrlSliderWrapper *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + pThis->m_pSlider->SetValue(nValue); + return S_OK; +} + +HRESULT CXuiCtrlSliderWrapper::SetValueDisplay( BOOL bShow ) +{ + CXuiCtrlSliderWrapper *pThis; + HXUIOBJ hVisual,hText; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + hr=XuiControlGetVisual(pThis->m_pSlider->m_hObj,&hVisual); + hr=XuiElementGetChildById(hVisual,L"Text_Value",&hText); + + if(hText!=NULL) + { + XuiElementSetShow(hText,bShow); + } + + return S_OK; +} + +LPCWSTR CXuiCtrlSliderWrapper::GetText( ) +{ + CXuiCtrlSliderWrapper *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return NULL; + return pThis->m_pSlider->GetText(); + //return S_OK; +} + +HRESULT CXuiCtrlSliderWrapper::SetText(LPCWSTR text , int iDataAssoc) +{ + CXuiCtrlSliderWrapper *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + // if there's a data assoc value, find the right control for it + if(iDataAssoc!=0) + { + + } + + pThis->m_pSlider->SetText(text); + return hr; +} + +HXUIOBJ CXuiCtrlSliderWrapper::GetSlider() +{ + CXuiCtrlSliderWrapper *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return NULL; + return pThis->m_pSlider->m_hObj; +} + +HRESULT CXuiCtrlSliderWrapper::SetRange( int nRangeMin, int nRangeMax) +{ + CXuiCtrlSliderWrapper *pThis; + HRESULT hr = XuiObjectFromHandle(m_hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + pThis->m_pSlider->SetRange(nRangeMin, nRangeMax); + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.h new file mode 100644 index 0000000..03659fc --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SliderWrapper.h @@ -0,0 +1,38 @@ +#pragma once + +class CXuiCtrlSliderWrapper : public CXuiSceneImpl +{ +private: + CXuiSlider *m_pSlider; + CXuiControl *m_pFocusSink; + bool m_sliderActive; + bool m_bDisplayVal; + bool m_bPlaySound; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_END_MSG_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlSliderWrapper, L"CXuiCtrlSliderWrapper", XUI_CLASS_SCENE ) + + HRESULT SetValue( int nValue ); + HXUIOBJ GetSlider(); + HRESULT SetRange( int nRangeMin, int nRangeMax); + LPCWSTR GetText( ); + HRESULT SetText(LPCWSTR text, int iDataAssoc=0 ); + HRESULT SetValueDisplay( BOOL bShow ); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItem.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItem.h new file mode 100644 index 0000000..1fad4a3 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItem.h @@ -0,0 +1,37 @@ +#pragma once +#include "XUI_Ctrl_SlotItemCtrlBase.h" + +class CXuiCtrlSlotItem : public CXuiControlImpl, public CXuiCtrlSlotItemCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlSlotItem, L"CXuiCtrlSlotItem", XUI_CLASS_CONTROL ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_GETSLOTITEM_MESSAGE(OnCustomMessage_GetSlotItem) + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_END_MSG_MAP() + + using CXuiCtrlSlotItemCtrlBase::OnInit; + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& bHandled) { return this->OnInit( m_hObj, pInitData, bHandled ); }; + + using CXuiCtrlSlotItemCtrlBase::OnDestroy; + HRESULT OnDestroy() { return this->OnDestroy( m_hObj ); }; + + using CXuiCtrlSlotItemCtrlBase::OnCustomMessage_GetSlotItem; + HRESULT OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) { return this->OnCustomMessage_GetSlotItem( m_hObj, pData, bHandled ); }; + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + using CXuiCtrlSlotItemCtrlBase::OnControlNavigate; + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) { return this->OnControlNavigate( m_hObj, pControlNavigateData, bHandled ); }; + + using CXuiCtrlSlotItemCtrlBase::OnKeyDown; + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) { return this->OnKeyDown( m_hObj, pInputData, bHandled ); }; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.cpp new file mode 100644 index 0000000..87abbcd --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.cpp @@ -0,0 +1,392 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Slot.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\Minecraft.h" + +#include "XUI_Ctrl_SlotItemCtrlBase.h" + +HRESULT CXuiCtrlSlotItemCtrlBase::OnInit( HXUIOBJ hObj, XUIMessageInit* pInitData, BOOL& bHandled ) +{ + HRESULT hr = S_OK; + SlotControlUserDataContainer* pvUserData = new SlotControlUserDataContainer(); + hr = XuiElementSetUserData(hObj, (void *)pvUserData ); + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + m_bSkipDefaultNavigation = false; + + return hr; +} + +HRESULT CXuiCtrlSlotItemCtrlBase::OnDestroy( HXUIOBJ hObj ) +{ + HRESULT hr = S_OK; + void* pvUserData; + hr = XuiElementGetUserData( hObj, &pvUserData ); + + if( pvUserData != NULL ) + { + delete pvUserData; + } + + return hr; +} + +HRESULT CXuiCtrlSlotItemCtrlBase::OnCustomMessage_GetSlotItem(HXUIOBJ hObj, CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) +{ + shared_ptr item = shared_ptr(); + + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + if( pUserDataContainer->slot != NULL ) + { + item = pUserDataContainer->slot->getItem(); + } + else if(pUserDataContainer->m_iPad >= 0 && pUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad] ); + if(player != NULL) item = player->inventory->getCarried(); + } + + if( item != NULL ) + { + pData->item = item; + pData->iItemBitField = MAKE_SLOTDISPLAY_ITEM_BITMASK(item->id,item->getAuxValue(),item->isFoil()); + //int iAuxVal=item->getAuxValue(); + //int iCount = item->GetCount(); + // 8 bits - alpha + // 1 bit - decorations on + // 11 bits - auxval + // 6 bits - count + // 6 bits - scale + pData->iDataBitField = MAKE_SLOTDISPLAY_DATA_BITMASK(pUserDataContainer->m_iPad, (int)(31*pUserDataContainer->m_fAlpha),true,item->GetCount(),7,item->popTime); + } + else + { + //pGetSourceImageData->iData = 0; + pData->szPath = L""; + } + + bHandled = TRUE; + + return S_OK; +} + +void CXuiCtrlSlotItemCtrlBase::SetSlot( HXUIOBJ hObj, Slot* slot ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + pUserDataContainer->slot = slot; +} + +void CXuiCtrlSlotItemCtrlBase::SetUserIndex( HXUIOBJ hObj, int iPad ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + pUserDataContainer->m_iPad = iPad; +} + +void CXuiCtrlSlotItemCtrlBase::SetAlpha( HXUIOBJ hObj, float fAlpha ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + pUserDataContainer->m_fAlpha = fAlpha; +} + +bool CXuiCtrlSlotItemCtrlBase::isEmpty( HXUIOBJ hObj ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + if(pUserDataContainer->slot != NULL) + { + return !pUserDataContainer->slot->hasItem(); + } + else if(pUserDataContainer->m_iPad >= 0 && pUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad] ); + if(player != NULL) return player->inventory->getCarried() == NULL; + + } + return true; +} + +wstring CXuiCtrlSlotItemCtrlBase::GetItemDescription( HXUIOBJ hObj, vector &unformattedStrings ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + if(pUserDataContainer->slot != NULL) + { + wstring desc = L""; + vector *strings = pUserDataContainer->slot->getItem()->getHoverText(Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad], false, unformattedStrings); + bool firstLine = true; + for(AUTO_VAR(it, strings->begin()); it != strings->end(); ++it) + { + wstring thisString = *it; + if(!firstLine) + { + desc.append( L"
" ); + } + else + { + firstLine = false; + wchar_t formatted[256]; + eMinecraftColour rarityColour = pUserDataContainer->slot->getItem()->getRarity()->color; + int colour = app.GetHTMLColour(rarityColour); + + if(pUserDataContainer->slot->getItem()->hasCustomHoverName()) + { + colour = app.GetHTMLColour(eTextColor_RenamedItemTitle); + } + + swprintf(formatted, 256, L"%s",colour,thisString.c_str()); + thisString = formatted; + } + desc.append( thisString ); + } + strings->clear(); + delete strings; + return desc;//app.GetString( pUserDataContainer->slot->getItem()->getDescriptionId() ); + } + else if(pUserDataContainer->m_iPad >= 0 && pUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad] ); + if(player != NULL) + { + shared_ptr item = player->inventory->getCarried(); + if(item != NULL) return app.GetString( item->getDescriptionId() ); + } + + } + return L""; +} + +shared_ptr CXuiCtrlSlotItemCtrlBase::getItemInstance( HXUIOBJ hObj ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + if(pUserDataContainer->slot != NULL) + { + return pUserDataContainer->slot->getItem(); + } + else if(pUserDataContainer->m_iPad >= 0 && pUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad] ); + if(player != NULL) return player->inventory->getCarried(); + + } + return nullptr; +} + +Slot *CXuiCtrlSlotItemCtrlBase::getSlot( HXUIOBJ hObj ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + return pUserDataContainer->slot; +} + +HRESULT CXuiCtrlSlotItemCtrlBase::OnKeyDown(HXUIOBJ hObj, XUIMessageInput *pInputData, BOOL& bHandled) +{ + if( pInputData->dwKeyCode == VK_PAD_DPAD_LEFT || + pInputData->dwKeyCode == VK_PAD_DPAD_RIGHT || + pInputData->dwKeyCode == VK_PAD_DPAD_UP || + pInputData->dwKeyCode == VK_PAD_DPAD_DOWN || + pInputData->dwKeyCode == VK_PAD_LTRIGGER || + pInputData->dwKeyCode == VK_PAD_RTRIGGER) + { + HXUIOBJ parent; + HRESULT hr; + hr = XuiElementGetParent( hObj, &parent ); + + XUIMessage message; + XUIMessageInput messageInput; + + XuiMessageInput( &message, &messageInput, XUI_KEYDOWN, pInputData->dwKeyCode, pInputData->wch, pInputData->dwFlags, pInputData->UserIndex ); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(parent, &message); + + if (message.bHandled) + { + bHandled = TRUE; + } + } + } + + return S_OK; +} + +// 4J WESTY : Pointer Prototype : Added to support prototype only. +HRESULT CXuiCtrlSlotItemCtrlBase::OnControlNavigate( HXUIOBJ hObj, XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // Skip default navigation behaviour when navigation is by pointer. + if ( m_bSkipDefaultNavigation ) + { + pControlNavigateData->bSkipNavigate = TRUE; + bHandled = TRUE; + } + return S_OK; +} + +// 4J WESTY : Pointer Prototype : Added to support prototype only. +int CXuiCtrlSlotItemCtrlBase::GetObjectCount( HXUIOBJ hObj ) +{ + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + int iCount = 0; + + if(pUserDataContainer->slot != NULL) + { + if ( pUserDataContainer->slot->hasItem() ) + { + iCount = pUserDataContainer->slot->getItem()->GetCount(); + } + } + else if(pUserDataContainer->m_iPad >= 0 && pUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pUserDataContainer->m_iPad] ); + if(player != NULL && player->inventory->getCarried() != NULL) + { + iCount = player->inventory->getCarried()->count; + } + + } + return iCount; +} + + +// 4J WESTY : Pointer Prototype : Added to support prototype only. +bool CXuiCtrlSlotItemCtrlBase::IsSameItemAs( HXUIOBJ hThisObj, HXUIOBJ hOtherObj ) +{ + bool bThisItemExists = false; + int iThisID = 0; + int iThisAux = 0; + + bool bOtherItemExists = false; + int iOtherID = 0; + int iOtherAux = 0; + + bool bStackedByData = false; + + // Get the info on this item. + void* pvThisUserData; + XuiElementGetUserData( hThisObj, &pvThisUserData ); + SlotControlUserDataContainer* pThisUserDataContainer = (SlotControlUserDataContainer*)pvThisUserData; + + if(pThisUserDataContainer->slot != NULL) + { + if ( pThisUserDataContainer->slot->hasItem() ) + { + iThisID = pThisUserDataContainer->slot->getItem()->id; + iThisAux = pThisUserDataContainer->slot->getItem()->getAuxValue(); + bThisItemExists = true; + bStackedByData = pThisUserDataContainer->slot->getItem()->isStackedByData(); + } + } + else if(pThisUserDataContainer->m_iPad >= 0 && pThisUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pThisUserDataContainer->m_iPad] ); + if(player != NULL && player->inventory->getCarried() != NULL) + { + iThisID = player->inventory->getCarried()->id; + iThisAux = player->inventory->getCarried()->getAuxValue(); + bThisItemExists = true; + bStackedByData = player->inventory->getCarried()->isStackedByData(); + } + + } + + // Get the info on other item. + void* pvOtherUserData; + XuiElementGetUserData( hOtherObj, &pvOtherUserData ); + SlotControlUserDataContainer* pOtherUserDataContainer = (SlotControlUserDataContainer*)pvOtherUserData; + + if(pOtherUserDataContainer->slot != NULL) + { + if ( pOtherUserDataContainer->slot->hasItem() ) + { + iOtherID = pOtherUserDataContainer->slot->getItem()->id; + iOtherAux = pOtherUserDataContainer->slot->getItem()->getAuxValue(); + bOtherItemExists = true; + } + } + else if(pOtherUserDataContainer->m_iPad >= 0 && pOtherUserDataContainer->m_iPad < XUSER_MAX_COUNT) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->localplayers[pOtherUserDataContainer->m_iPad] ); + if(player != NULL && player->inventory->getCarried() != NULL) + { + iOtherID = player->inventory->getCarried()->id; + iOtherAux = player->inventory->getCarried()->getAuxValue(); + bOtherItemExists = true; + } + + } + + if ( bThisItemExists && bOtherItemExists ) + { + return ( ( iThisID == iOtherID ) && ( (bStackedByData && iThisAux == iOtherAux) || !bStackedByData ) ); + } + else + { + return false; + } +} + +// 4J WESTY : Pointer Prototype : Added to support prototype only. +// Returns number of items that can still be stacked into this slot. +int CXuiCtrlSlotItemCtrlBase::GetEmptyStackSpace( HXUIOBJ hObj ) +{ + int iResult = 0; + + void* pvUserData; + XuiElementGetUserData( hObj, &pvUserData ); + SlotControlUserDataContainer* pUserDataContainer = (SlotControlUserDataContainer*)pvUserData; + + int iCount = 0; + int iMaxStackSize = 0; + bool bStackable = false; + + if(pUserDataContainer->slot != NULL) + { + if ( pUserDataContainer->slot->hasItem() ) + { + bStackable = pUserDataContainer->slot->getItem()->isStackable(); + if ( bStackable ) + { + iCount = pUserDataContainer->slot->getItem()->GetCount(); + iMaxStackSize = min(pUserDataContainer->slot->getItem()->getMaxStackSize(), pUserDataContainer->slot->getMaxStackSize() ); + + iResult = iMaxStackSize - iCount; + + if(iResult < 0 ) iResult = 0; + } + } + } + + return iResult; +} + diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.h new file mode 100644 index 0000000..2fd2174 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemCtrlBase.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +class Slot; +class ItemInstance; + +#include "XUI_CustomMessages.h" + +class SlotControlUserDataContainer +{ +public: + SlotControlUserDataContainer() : slot( NULL ), hProgressBar( NULL ), m_iPad( -1 ), m_fAlpha(1.0f) {}; + Slot* slot; + HXUIOBJ hProgressBar; + float m_fAlpha; + int m_iPad; +}; + +// The base class for all controls with the "ItemButton" visual +// This could be a list item or just a button. +// We need this class to be able to easily access all the parts of the visual + +class CXuiCtrlSlotItemCtrlBase +{ +private: + // 4J WESTY : Pointer Prototype : Added to support prototype only. + BOOL m_bSkipDefaultNavigation; + +public: + // We define a lot of functions as virtual. These should be implemented to call the protected version by + // passing in the HXUIOBJ of the control as the first parameter. We do not have access to that normally + // due to the inheritance structure + virtual HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) = 0; + + virtual HRESULT OnDestroy() = 0; + + virtual HRESULT OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) = 0; + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + virtual HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) = 0; + + virtual HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) = 0; + + void SetSlot( HXUIOBJ hObj, Slot* slot ); + void SetAlpha( HXUIOBJ hObj, float fAlpha ); + void SetUserIndex( HXUIOBJ hObj, int iPad ); + + bool isEmpty( HXUIOBJ hObj ); + + wstring GetItemDescription( HXUIOBJ hObj, vector &unformattedStrings ); + + shared_ptr getItemInstance( HXUIOBJ hObj ); + Slot *getSlot( HXUIOBJ hObj ); + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + int GetObjectCount( HXUIOBJ hObj ); + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + void SetSkipsDefaultNavigation( BOOL bSkipDefaultNavigation ) { m_bSkipDefaultNavigation = bSkipDefaultNavigation; } + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + int GetEmptyStackSpace( HXUIOBJ hObj ); // Returns number of items that can still be stacked into this slot. + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + bool IsSameItemAs( HXUIOBJ hThisObj, HXUIOBJ hOtherObj ); + +protected: + HRESULT OnInit( HXUIOBJ hObj, XUIMessageInit* pInitData, BOOL& bHandled ); + + HRESULT OnDestroy( HXUIOBJ hObj ); + + HRESULT OnCustomMessage_GetSlotItem(HXUIOBJ hObj, CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled); + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + HRESULT OnControlNavigate(HXUIOBJ hObj, XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + + HRESULT OnKeyDown(HXUIOBJ hObj, XUIMessageInput *pInputData, BOOL& bHandled); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemListItem.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemListItem.h new file mode 100644 index 0000000..bee1b93 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotItemListItem.h @@ -0,0 +1,38 @@ +#pragma once + +#include "XUI_Ctrl_SlotItemCtrlBase.h" + +class CXuiCtrlSlotItemListItem : public CXuiListItemImpl, public CXuiCtrlSlotItemCtrlBase +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlSlotItemListItem, L"CXuiCtrlSlotItemListItem", XUI_CLASS_LISTITEM ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_GETSLOTITEM_MESSAGE(OnCustomMessage_GetSlotItem) + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_END_MSG_MAP() + + using CXuiCtrlSlotItemCtrlBase::OnInit; + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& bHandled) { return this->OnInit( m_hObj, pInitData, bHandled ); }; + + using CXuiCtrlSlotItemCtrlBase::OnDestroy; + HRESULT OnDestroy() { return this->OnDestroy( m_hObj ); }; + + using CXuiCtrlSlotItemCtrlBase::OnCustomMessage_GetSlotItem; + HRESULT OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) { return this->OnCustomMessage_GetSlotItem( m_hObj, pData, bHandled ); }; + + // 4J WESTY : Pointer Prototype : Added to support prototype only. + using CXuiCtrlSlotItemCtrlBase::OnControlNavigate; + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) { return this->OnControlNavigate( m_hObj, pControlNavigateData, bHandled ); } + + using CXuiCtrlSlotItemCtrlBase::OnKeyDown; + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) { return this->OnKeyDown( m_hObj, pInputData, bHandled ); }; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.cpp new file mode 100644 index 0000000..7e51c88 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.cpp @@ -0,0 +1,231 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" + +#include "XUI_Ctrl_SlotItemListItem.h" +#include "XUI_Ctrl_SlotList.h" + + +//-------------------------------------------------------------------------------------- +// Name: CXuiCtrlSlotList::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiCtrlSlotList::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + slotCount = 0; + + return S_OK; +} + +HRESULT CXuiCtrlSlotList::OnDestroy() +{ + return S_OK; +} + +HRESULT CXuiCtrlSlotList::OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) +{ + if( pInputData->dwKeyCode == VK_PAD_DPAD_LEFT || + pInputData->dwKeyCode == VK_PAD_DPAD_RIGHT || + pInputData->dwKeyCode == VK_PAD_DPAD_UP || + pInputData->dwKeyCode == VK_PAD_DPAD_DOWN || + pInputData->dwKeyCode == VK_PAD_LTRIGGER || + pInputData->dwKeyCode == VK_PAD_RTRIGGER) + { + HXUIOBJ parent; + HRESULT hr; + hr = XuiElementGetParent( m_hObj, &parent ); + + XUIMessage message; + XUIMessageInput messageInput; + + XuiMessageInput( &message, &messageInput, XUI_KEYDOWN, pInputData->dwKeyCode, pInputData->wch, pInputData->dwFlags, pInputData->UserIndex ); + + if (HRESULT_SUCCEEDED(hr)) + { + hr = XuiBubbleMessage(parent, &message); + + if (message.bHandled) + { + bHandled = TRUE; + } + } + } + + return S_OK; +} + +void CXuiCtrlSlotList::SetData(int m_iPad, AbstractContainerMenu* menu, int rows, int columns, int startIndex /*= 0*/, int endIndex /*= 0*/) +{ + assert( startIndex >= 0 && startIndex < menu->getSize() ); + assert( endIndex <= menu->getSize() ); + + if( startIndex < 0 ) + { + startIndex = 0; + } + else if( startIndex > menu->getSize() ) + { + startIndex = menu->getSize(); + } + + if( endIndex == 0 ) + { + endIndex = startIndex + (rows * columns); + } + + if( endIndex > menu->getSize() ) + { + endIndex = menu->getSize(); + } + + if( startIndex > endIndex ) + { + endIndex = startIndex; + } + + assert( (rows * columns) == (endIndex - startIndex) ); + + this->rows = rows; + this->columns = columns; + + this->startIndex = startIndex; + + this->slotCount = rows * columns; + + InsertItems( 0, slotCount ); + + for(int i = 0; i < slotCount; i++) + { + CXuiCtrlSlotItemListItem* slotControl; + GetCXuiCtrlSlotItem(i, &slotControl); + + slotControl->SetSlot( slotControl->m_hObj, menu->getSlot( i + startIndex ) ); + + slotControl->SetUserIndex( slotControl->m_hObj, m_iPad ); + + slotControl = NULL; + } +} + +HRESULT CXuiCtrlSlotList::OnGetItemCountAll( XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled ) +{ + // We don't need to look at the type of request. The message map + // has already filtered out a request to retrieve all items. + pGetItemCountData->cItems = slotCount; + bHandled = TRUE; + + return( S_OK ); +} + +HRESULT CXuiCtrlSlotList::OnGetItemCountMaxLines( XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled ) +{ + // We don't need to look at the type of request. The message map + // has already filtered out a request to retrieve max lines. + pGetItemCountData->cItems = rows; + bHandled = TRUE; + + return( S_OK ); +} + +HRESULT CXuiCtrlSlotList::OnGetItemCountMaxPerLine( XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled ) +{ + // We don't need to look at the type of request. The message map + // has already filtered out a request to retrieve max per line. + pGetItemCountData->cItems = columns; + bHandled = TRUE; + + return( S_OK ); +} + +int CXuiCtrlSlotList::GetCurrentColumn() +{ + int currentItemIndex = GetCurSel(); + + return currentItemIndex % columns; +} + +int CXuiCtrlSlotList::GetCurrentRow() +{ + int currentItemIndex = GetCurSel(); + + return (currentItemIndex/columns) % rows; +} + +// Return the index in the menu object +int CXuiCtrlSlotList::GetCurrentIndex() +{ + int currentSelected = GetCurSel(); + return currentSelected + this->startIndex; +} + +void CXuiCtrlSlotList::SetCurrentSlot(int row, int column) +{ + if( row >= rows ) + { + row = rows - 1; + } + else if ( row < 0 ) + { + row = 0; + } + if( column >= columns ) + { + column = columns - 1; + } + else if ( column < 0 ) + { + column = 0; + } + int newSlot = ( row * columns ) + column; + SetCurSel( newSlot ); +} + +void CXuiCtrlSlotList::SetEntrySlot(int row, int column, XUI_CONTROL_NAVIGATE direction) +{ + // The direction is the direction in which we are leaving the previous control to get to here + // So a Navigate up means we want to start at the bottom of ourself + switch( direction ) + { + case XUI_CONTROL_NAVIGATE_UP: + { + row = rows - 1; + break; + } + case XUI_CONTROL_NAVIGATE_DOWN: + { + row = 0; + break; + } + case XUI_CONTROL_NAVIGATE_LEFT: + case XUI_CONTROL_NAVIGATE_TABBACKWARD: + { + column = columns - 1; + break; + } + case XUI_CONTROL_NAVIGATE_RIGHT: + case XUI_CONTROL_NAVIGATE_TABFORWARD: + { + column = 0; + break; + } + } + SetCurrentSlot( row, column ); +} + +void CXuiCtrlSlotList::Clicked() +{ + CXuiCtrlSlotItemListItem* slot; + GetCXuiCtrlSlotItem( GetCurSel() , &slot); + + // To get the press animation + slot->Press(); +} + +void CXuiCtrlSlotList::GetCXuiCtrlSlotItem(int itemIndex, CXuiCtrlSlotItemListItem** CXuiCtrlSlotItem) +{ + HXUIOBJ itemControl = this->GetItemControl(itemIndex); + VOID *pObj; + XuiObjectFromHandle( itemControl, &pObj ); + *CXuiCtrlSlotItem = (CXuiCtrlSlotItemListItem *)pObj; +} + diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.h new file mode 100644 index 0000000..d428ab1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SlotList.h @@ -0,0 +1,73 @@ +#pragma once + +// Sig: HRESULT OnGetItemCountMaxLines(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +#define XUI_ON_XM_GET_ITEMCOUNT_MAX_LINES(MemberFunc)\ + if (pMessage->dwMessage == XM_GET_ITEMCOUNT && ((XUIMessageGetItemCount *) pMessage->pvData)->nType == XUI_ITEMCOUNT_MAX_LINES)\ + {\ + XUIMessageGetItemCount *pData = (XUIMessageGetItemCount *) pMessage->pvData;\ + return MemberFunc(pData, pMessage->bHandled);\ + } + +// Sig: HRESULT OnGetItemCountMaxPerLine(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +#define XUI_ON_XM_GET_ITEMCOUNT_MAX_PER_LINE(MemberFunc)\ + if (pMessage->dwMessage == XM_GET_ITEMCOUNT && ((XUIMessageGetItemCount *) pMessage->pvData)->nType == XUI_ITEMCOUNT_MAX_PER_LINE)\ + {\ + XUIMessageGetItemCount *pData = (XUIMessageGetItemCount *) pMessage->pvData;\ + return MemberFunc(pData, pMessage->bHandled);\ + } + +class AbstractContainerMenu; +class SlotListItemControl; +class CXuiCtrlSlotItemListItem; + +class CXuiCtrlSlotList : public CXuiListImpl +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiCtrlSlotList, L"CXuiCtrlSlotList", XUI_CLASS_LIST ) + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_GET_ITEMCOUNT_ALL( OnGetItemCountAll ) + XUI_ON_XM_GET_ITEMCOUNT_MAX_LINES(OnGetItemCountMaxLines) + XUI_ON_XM_GET_ITEMCOUNT_MAX_PER_LINE(OnGetItemCountMaxPerLine) + XUI_END_MSG_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnGetItemCountAll( XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled ); + HRESULT OnGetItemCountMaxLines(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnGetItemCountMaxPerLine(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +public: + void SetData(int m_iPad, AbstractContainerMenu* menu, int rows, int columns, int startIndex = 0, int endIndex = 0); + + int GetRows() { return rows; }; + int GetColumns() { return columns; }; + int GetCurrentColumn(); + int GetCurrentRow(); + + int GetCurrentIndex(); + + void SetCurrentSlot(int row, int column); + void SetEntrySlot(int row, int column, XUI_CONTROL_NAVIGATE direction); + + void Clicked(); + + // 4J WESTY : Pointer Prototype : Made public. + void GetCXuiCtrlSlotItem(int itemIndex, CXuiCtrlSlotItemListItem** CXuiCtrlSlotItem); + +private: + int slotCount; + int rows; + int columns; + int startIndex; + + // 4J WESTY : Pointer Prototype : Made public. + //void GetCXuiCtrlSlotItem(int itemIndex, CXuiCtrlSlotItemListItem** CXuiCtrlSlotItem); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.cpp b/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.cpp new file mode 100644 index 0000000..2e97171 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\ScreenSizeCalculator.h" +#include "..\..\Lighting.h" +#include "XUI_Ctrl_SplashPulser.h" +#include "..\..\Font.h" +#include "..\..\..\Minecraft.World\Mth.h" +#include "..\..\..\Minecraft.World\System.h" + +//----------------------------------------------------------------------------- +// CXuiCtrlSplashPulser class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +CXuiCtrlSplashPulser::CXuiCtrlSplashPulser() : + m_bDirty(FALSE), + m_fScale(1.0f), + m_fAlpha(1.0f) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); + m_fScreenWidth=(float)pMinecraft->width_phys; + m_fRawWidth=(float)ssc.rawWidth; + m_fScreenHeight=(float)pMinecraft->height_phys; + m_fRawHeight=(float)ssc.rawHeight; +} + +//----------------------------------------------------------------------------- +HRESULT CXuiCtrlSplashPulser::OnInit(XUIMessageInit* pInitData, BOOL& rfHandled) +{ + HRESULT hr=S_OK; + return hr; +} + +HRESULT CXuiCtrlSplashPulser::OnRender(XUIMessageRender *pRenderData, BOOL &bHandled ) +{ +#ifdef _XBOX + HXUIDC hDC = pRenderData->hDC; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + Font *font = pMinecraft->font; + + wstring splash( GetText() ); + + // build and render with the game call + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + RenderManager.Set_matrixDirty(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 1280.0f, 720.0f, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + glColor4f(1.0f,1.0f,1.0f,1.0f); + + glPushMatrix(); + + D3DXMATRIX matrix; + CXuiControl xuiControl(m_hObj); + xuiControl.GetFullXForm(&matrix); + float bwidth,bheight; + xuiControl.GetBounds(&bwidth,&bheight); + + float xo = (matrix._41 + (bwidth*matrix._11)/2 ); + float yo = (matrix._42 + (bheight*matrix._22) ); + glTranslatef(xo, yo, 0); + + glRotatef(-17, 0, 0, 1); + float sss = 1.8f - Mth::abs(Mth::sin(System::currentTimeMillis() % 1000 / 1000.0f * PI * 2) * 0.1f); + sss*=(m_fScreenWidth/m_fRawWidth); + + sss = sss * 100 / (font->width(splash) + 8 * 4); + glScalef(sss, sss, sss); + //drawCenteredString(font, splash, 0, -8, 0xffff00); + font->drawShadow(splash, 0 - (font->width(splash)) / 2, -8, 0xffff00); + glPopMatrix(); + + glDisable(GL_RESCALE_NORMAL); + + glEnable(GL_DEPTH_TEST); + + XuiRenderRestoreState(hDC); + + bHandled = TRUE; +#endif + return S_OK; +} + + + diff --git a/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.h b/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.h new file mode 100644 index 0000000..504cf4c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Ctrl_SplashPulser.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +using namespace std; + +//----------------------------------------------------------------------------- +// CXuiCtrlSplashPulser class +//----------------------------------------------------------------------------- +class CXuiCtrlSplashPulser : public CXuiControlImpl +{ +public: + XUI_IMPLEMENT_CLASS(CXuiCtrlSplashPulser, L"CXuiCtrlSplashPulser", XUI_CLASS_LABEL) + + CXuiCtrlSplashPulser(); + virtual ~CXuiCtrlSplashPulser() { }; + +protected: + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT(OnInit) + XUI_ON_XM_RENDER(OnRender) + XUI_END_MSG_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& rfHandled); + HRESULT OnRender(XUIMessageRender *pRenderData, BOOL &rfHandled); + +private: + BOOL m_bDirty; + float m_fScale,m_fAlpha; + + float m_fScreenWidth,m_fScreenHeight; + float m_fRawWidth,m_fRawHeight; + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_CustomMessages.h b/Minecraft.Client/Common/XUI/XUI_CustomMessages.h new file mode 100644 index 0000000..888f8ad --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_CustomMessages.h @@ -0,0 +1,193 @@ +#pragma once + +#define XM_SPLITSCREENPLAYER_MESSAGE XM_USER +#define XM_FONTRENDERERCHANGE_MESSAGE XM_USER + 1 +#define XM_DLCMOUNTED_MESSAGE XM_USER + 2 +#define XM_BASE_POSITION_CHANGED_MESSAGE XM_USER + 3 +#define XM_DLCSINSTALLED_MESSAGE XM_USER + 4 +#define XM_INVENTORYUPDATED_MESSAGE XM_USER + 5 +#define XM_TMS_DLCFILE_RETRIEVED_MESSAGE XM_USER + 6 +#define XM_TMS_BANFILE_RETRIEVED_MESSAGE XM_USER + 7 +#define XM_TMS_ALLFILES_RETRIEVED_MESSAGE XM_USER + 8 +#define XM_CUSTOMTICKSCENE_MESSAGE XM_USER + 9 +#define XM_GETSLOTITEM_MESSAGE XM_USER + 10 + +typedef struct +{ + shared_ptr item; + + // Legacy values for compatibility + int iDataBitField; + int iItemBitField; + LPCWSTR szPath; + BOOL bDirty; +} +CustomMessage_GetSlotItem_Struct; + + +// Define the prototype for your handler function +// Sig: HRESULT OnCustomMessage_GetSlotItem(CustomMessage_GetSlotItem_Struct *pData, BOOL& bHandled) + +// Define the message map macro +#define XUI_ON_XM_GETSLOTITEM_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_GETSLOTITEM_MESSAGE)\ +{\ + CustomMessage_GetSlotItem_Struct *pData = (CustomMessage_GetSlotItem_Struct *) pMessage->pvData;\ + return MemberFunc(pData, pMessage->bHandled);\ +} + +static __declspec(noinline) void CustomMessage_GetSlotItem(XUIMessage *pMsg, CustomMessage_GetSlotItem_Struct* pData, int iDataBitField, int iItemBitField) +{ + XuiMessage(pMsg,XM_GETSLOTITEM_MESSAGE); + _XuiMessageExtra(pMsg,(XUIMessageData*) pData, sizeof(*pData)); + pData->item = nullptr; + pData->iDataBitField = iDataBitField; + pData->iItemBitField = iItemBitField; + pData->szPath = NULL; + pData->bDirty = false; +} + +typedef struct +{ + bool bJoining; // if you're not joining, your leaving +} +CustomMessage_Splitscreenplayer_Struct; + + +// Define the prototype for your handler function +// Sig: HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) + +// Define the message map macro +#define XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_SPLITSCREENPLAYER_MESSAGE)\ +{\ + CustomMessage_Splitscreenplayer_Struct *pData = (CustomMessage_Splitscreenplayer_Struct *) pMessage->pvData;\ + return MemberFunc(pData->bJoining, pMessage->bHandled);\ +} + +static __declspec(noinline) void CustomMessage_Splitscreenplayer(XUIMessage *pMsg, CustomMessage_Splitscreenplayer_Struct* pData, bool bJoining) +{ + XuiMessage(pMsg,XM_SPLITSCREENPLAYER_MESSAGE); + _XuiMessageExtra(pMsg,(XUIMessageData*) pData, sizeof(*pData)); + pData->bJoining = bJoining; +} + +// Define the prototype for your handler function +// Sig: HRESULT OnFontRendererChange() + +// Define the message map macro +#define XUI_ON_XM_FONTRENDERERCHANGE_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_FONTRENDERERCHANGE_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_FontRendererChange(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_FONTRENDERERCHANGE_MESSAGE); +} + +// Define the prototype for your handler function +// Sig: HRESULT OnDLCMounted() + +// Define the message map macro +#define XUI_ON_XM_DLCLOADED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_DLCMOUNTED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_DLCMountingComplete(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_DLCMOUNTED_MESSAGE); +} + +// Define the prototype for your handler function +// Sig: HRESULT OnBasePositionChanged() + +// Define the message map macro +#define XUI_ON_XM_BASE_POSITION_CHANGED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_BASE_POSITION_CHANGED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_BasePositionChanged(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_BASE_POSITION_CHANGED_MESSAGE); +} + +// the prototype for your handler function +// Sig: HRESULT OnDLCInstalled() + +// Define the message map macro +#define XUI_ON_XM_DLCINSTALLED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_DLCSINSTALLED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_DLCInstalled(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_DLCSINSTALLED_MESSAGE); +} + +// the prototype for your handler function +// Sig: HRESULT OnCustomMessage_InventoryUpdated() + +// Define the message map macro +#define XUI_ON_XM_INVENTORYUPDATED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_INVENTORYUPDATED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_InventoryUpdated(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_INVENTORYUPDATED_MESSAGE); +} + +// the prototype for your handler function +// Sig: HRESULT OnCustomMessage_() + +// Define the message map macro +#define XUI_ON_XM_TMS_DLCFILE_RETRIEVED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_TMS_DLCFILE_RETRIEVED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_TMS_DLCFileRetrieved(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_TMS_DLCFILE_RETRIEVED_MESSAGE); +} + +// the prototype for your handler function +// Sig: HRESULT OnCustomMessage_() + +// Define the message map macro +#define XUI_ON_XM_TMS_BANFILE_RETRIEVED_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_TMS_BANFILE_RETRIEVED_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_TMS_BanFileRetrieved(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_TMS_BANFILE_RETRIEVED_MESSAGE); +} + +// Define the prototype for your handler function +// Sig: HRESULT OnCustomMessage_TickScene() + +// Define the message map macro +#define XUI_ON_XM_CUSTOMTICKSCENE_MESSAGE(MemberFunc)\ + if (pMessage->dwMessage == XM_CUSTOMTICKSCENE_MESSAGE)\ +{\ + return MemberFunc();\ +} + +static __declspec(noinline) void CustomMessage_TickScene(XUIMessage *pMsg) +{ + XuiMessage(pMsg,XM_CUSTOMTICKSCENE_MESSAGE); +} diff --git a/Minecraft.Client/Common/XUI/XUI_DLCOffers.cpp b/Minecraft.Client/Common/XUI/XUI_DLCOffers.cpp new file mode 100644 index 0000000..bd05ca1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DLCOffers.cpp @@ -0,0 +1,886 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\..\..\Minecraft.World\ByteArrayInputStream.h" +#include "..\..\..\Minecraft.World\BufferedReader.h" +#include "..\..\..\Minecraft.World\InputStreamReader.h" +#include "..\..\..\Minecraft.World\ArrayWithLength.h" +#include +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_DLCOffers.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#ifdef _XBOX +#include +#endif + +#define TIMER_ID_NETWORK_CONNECTION 1 +#define TIMER_ID_NAVIGATE_BACK 2 +// Constants + +//const wstring CScene_DLCOffers::DEFAULT_BANNER = L"Graphics/banner.png"; + +// DLC Main + +HRESULT CScene_DLCMain::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + iPad = *(int *) pInitData->pvInitData; + + MapChildControls(); + + app.SetTickTMSDLCFiles(true); + + XuiControlSetText(xList,app.GetString(IDS_DOWNLOADABLE_CONTENT_OFFERS)); + + //if(app.GetTMSDLCInfoRead()) + { + m_Timer.SetShow(FALSE); + m_bIgnoreInput=false; + + VOID *pObj; + XuiObjectFromHandle( xList, &pObj ); + list = (CXuiCtrl4JList *) pObj; + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); + + CXuiCtrl4JList::LIST_ITEM_INFO *pListInfo = new CXuiCtrl4JList::LIST_ITEM_INFO [e_DLC_MAX_MinecraftStore]; + ZeroMemory(pListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)*e_DLC_MAX_MinecraftStore); + + m_bAllDLCContentRetrieved=false; + pListInfo[e_DLC_SkinPack].pwszText = app.GetString(IDS_DLC_MENU_SKINPACKS); + pListInfo[e_DLC_SkinPack].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_SkinPack]); + + pListInfo[e_DLC_TexturePacks].pwszText = app.GetString(IDS_DLC_MENU_TEXTUREPACKS); + pListInfo[e_DLC_TexturePacks].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_TexturePacks]); + + pListInfo[e_DLC_MashupPacks].pwszText = app.GetString(IDS_DLC_MENU_MASHUPPACKS); + pListInfo[e_DLC_MashupPacks].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_MashupPacks]); + + pListInfo[e_DLC_Themes].pwszText = app.GetString(IDS_DLC_MENU_THEMES); + pListInfo[e_DLC_Themes].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_Themes]); + + pListInfo[e_DLC_AvatarItems].pwszText = app.GetString(IDS_DLC_MENU_AVATARITEMS); + pListInfo[e_DLC_AvatarItems].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_AvatarItems]); + + pListInfo[e_DLC_Gamerpics].pwszText = app.GetString(IDS_DLC_MENU_GAMERPICS); + pListInfo[e_DLC_Gamerpics].fEnabled=TRUE; + list->AddData(pListInfo[e_DLC_Gamerpics]); + + app.AddDLCRequest(e_Marketplace_Content); // content is skin packs, texture packs and mash-up packs + app.AddDLCRequest(e_Marketplace_Gamerpics); + app.AddDLCRequest(e_Marketplace_Themes); + app.AddDLCRequest(e_Marketplace_AvatarItems); + + // start retrieving the images needed from TMS + app.AddTMSPPFileTypeRequest(e_DLC_SkinPack); + app.AddTMSPPFileTypeRequest(e_DLC_Gamerpics); + app.AddTMSPPFileTypeRequest(e_DLC_Themes); + app.AddTMSPPFileTypeRequest(e_DLC_AvatarItems); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePacks); + app.AddTMSPPFileTypeRequest(e_DLC_MashupPacks); + } + + XuiElementInitUserFocus(xList, ProfileManager.GetPrimaryPad(), TRUE); + TelemetryManager->RecordMenuShown(iPad, eUIScene_DLCMainMenu, 0); // 4J JEV ? + + return S_OK; +} + +HRESULT CScene_DLCMain::OnDestroy() +{ + return S_OK; +} + +HRESULT CScene_DLCMain::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==TIMER_ID_NETWORK_CONNECTION) + { + if(ProfileManager.GetLiveConnectionStatus()!=XONLINE_S_LOGON_CONNECTION_ESTABLISHED) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + StorageManager.ClearDLCOffers(); + app.ClearAndResetDLCDownloadQueue(); + } + } + else if(pData->nId==TIMER_ID_NAVIGATE_BACK) + { + if(app.CheckTMSDLCCanStop()) + { + XuiKillTimer(m_hObj,TIMER_ID_NAVIGATE_BACK); + app.NavigateBack(XUSER_INDEX_ANY); + } + } + + return S_OK; +} + + +HRESULT CScene_DLCMain::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + app.SetTickTMSDLCFiles(false); + + // set the timer running to navigate back when any tms retrieval has come in + XuiSetTimer(m_hObj,TIMER_ID_NAVIGATE_BACK,50); + m_bIgnoreInput=true; + m_Timer.SetShow(TRUE); + //app.NavigateBack(XUSER_INDEX_ANY); + rfHandled = TRUE; + + break; + } + + return S_OK; +} + +HRESULT CScene_DLCMain::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==xList) + { + int iIndex; + CXuiControl pItem; + iIndex=xList.GetCurSel(&pItem); + + DLCOffersParam *param = new DLCOffersParam(); + param->iPad = iPad; + param->iType = iIndex; + + // promote the DLC content request type + app.AddDLCRequest((eDLCMarketplaceType)iIndex, true); + app.NavigateToScene(iPad,eUIScene_DLCOffersMenu, param); + } + return S_OK; +} + +HRESULT CScene_DLCMain::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); + + return S_OK; +} + +// DLC OFFERS + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_DLCOffers::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + DLCOffersParam *param = (DLCOffersParam *) pInitData->pvInitData; + m_iPad = param->iPad; + m_iType = param->iType; + m_iOfferC = app.GetDLCOffersCount(); + m_bIsFemale = false; + m_pNoImageFor_DLC=NULL; + bNoDLCToDisplay=true; + //hCostText=NULL; + + + // 4J JEV: Deleting this here seems simpler. + delete param; + + // If this is the avatar items, we need to init the avatar system to allow us to find out what the player avatar gender is (since we should only display tshirts etc with the right gender) + // The init reserves 3MB of memory, so we shut down on leaving this. + if(m_iType==e_DLC_AvatarItems) + { + XAVATAR_METADATA AvatarMetadata; + HRESULT hRes; + + + hRes=XAvatarInitialize(XAVATAR_COORDINATE_SYSTEM_LEFT_HANDED,0,0,0,NULL); + + // get the avatar gender + hRes=XAvatarGetMetadataLocalUser(m_iPad,&AvatarMetadata,NULL); + + m_bIsFemale= (XAVATAR_BODY_TYPE_FEMALE == XAvatarMetadataGetBodyType(&AvatarMetadata)); + // shutdown the avatar system + + XAvatarShutdown(); + } + + m_bIsSD=!RenderManager.IsHiDef() && !RenderManager.IsWidescreen(); + + MapChildControls(); + + XuiControlSetText(m_List,app.GetString(IDS_DOWNLOADABLE_CONTENT_OFFERS)); + + m_bIgnorePress=true; + + VOID *pObj; + m_hXuiBrush=NULL; + + XuiObjectFromHandle( m_List, &pObj ); + m_pOffersList = (CXuiCtrl4JList *)pObj; + m_bAllDLCContentRetrieved=false; + + XuiElementInitUserFocus(m_hObj,ProfileManager.GetPrimaryPad(),TRUE); + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_DLCOffersMenu, 0); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1,IDS_TOOLTIPS_BACK); + + // Disable the price tag display + m_PriceTag.SetShow(FALSE); + + // If we don't yet have this DLC, we need to display a timer + m_bDLCRequiredIsRetrieved=false; + + // Is the DLC we're looking for available? + if(!m_bDLCRequiredIsRetrieved) + { + if(app.DLCContentRetrieved((eDLCMarketplaceType)m_iType)) + { + m_bDLCRequiredIsRetrieved=true; + + // Retrieve the info + GetDLCInfo(app.GetDLCOffersCount(), false); + m_bIgnorePress=false; + } + } + + XuiSetTimer(m_hObj,TIMER_ID_NETWORK_CONNECTION,50); + + return S_OK; +} + +HRESULT CScene_DLCOffers::GetDLCInfo( int iOfferC, bool bUpdateOnly ) +{ + CXuiCtrl4JList::LIST_ITEM_INFO *pListInfo=NULL; + //XMARKETPLACE_CONTENTOFFER_INFO xOffer; + XMARKETPLACE_CURRENCY_CONTENTOFFER_INFO xOffer; + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + ZeroMemory(szResourceLocator,sizeof(WCHAR)*LOCATOR_SIZE); + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + int iCount=0; + + if(bUpdateOnly) // Just update the info on the current list + { + for(int i=0;iiGender==1) && (m_bIsFemale==true)) + { + app.DebugPrintf("Wrong gender\n"); + continue; + } + + // can't trust the offer type - partnernet is giving avatar items the CONTENT type + //if(Offer.dwOfferType==app.GetDLCContentType((eDLCContentType)m_iType)) + if(pDLC->eDLCType==(eDLCContentType)m_iType) + { + if(xOffer.fUserHasPurchased) + { + HXUIBRUSH hBrush; + + if(RenderManager.IsHiDef() || RenderManager.IsWidescreen()) + { + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/DLC_Tick.png"); + XuiCreateTextureBrush(szResourceLocator,&hBrush); + } + else + { + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/DLC_TickSmall.png"); + XuiCreateTextureBrush(szResourceLocator,&hBrush); + } + m_pOffersList->UpdateGraphic(i,hBrush ); + } + + iCount++; + } + } + + if(iCount>0) + { + bNoDLCToDisplay=false; + } + } + else + { + if(iOfferC!=0) + { + pListInfo = new CXuiCtrl4JList::LIST_ITEM_INFO [iOfferC]; + ZeroMemory(pListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)*iOfferC); + } + + for(int i = 0; i < iOfferC; i++) + { + xOffer = StorageManager.GetOffer(i); + + // Check that this is in the list of known DLC + DLC_INFO *pDLC=app.GetDLCInfoForFullOfferID(xOffer.qwOfferID); + if(pDLC==NULL) + { + // try the trial version + pDLC=app.GetDLCInfoForTrialOfferID(xOffer.qwOfferID); + } + + if(pDLC==NULL) + { + // skip this one +#ifdef _DEBUG + app.DebugPrintf("Unknown offer - "); + OutputDebugStringW(xOffer.wszOfferName); + app.DebugPrintf("\n"); +#endif + continue; + } + + // can't trust the offer type - partnernet is giving avatar items the CONTENT type + //if(Offer.dwOfferType==app.GetDLCContentType((eDLCContentType)m_iType)) + if(pDLC->eDLCType==(eDLCContentType)m_iType) + { + wstring wstrTemp=xOffer.wszOfferName; + + // If the string starts with Minecraft, removed that + + // Bug 49249 - JPN: Code Defect: Missing Text: String 'Minecraft' is missing in contents download screen. + // Looks like we shouldn't be removing this text for Japanese, and probably Chinese & Korean + + DWORD dwLanguage = XGetLanguage( ); + switch(dwLanguage) + { + case XC_LANGUAGE_KOREAN: + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + pListInfo[iCount].pwszText = xOffer.wszOfferName; + break; + default: + if(wstrTemp.compare(0,10,L"Minecraft ")==0) + { + pListInfo[iCount].pwszText = &xOffer.wszOfferName[10]; + } + else + { + pListInfo[iCount].pwszText = xOffer.wszOfferName; + } + break; + } + + pListInfo[iCount].fEnabled=TRUE; + + // store the offer index + pListInfo[iCount].iData=i; + pListInfo[iCount].iSortIndex=(int)pDLC->uiSortIndex; +#ifdef _DEBUG + app.DebugPrintf("Adding "); + OutputDebugStringW(pListInfo[iCount].pwszText); + app.DebugPrintf(" at %d\n",i); +#endif + + m_pOffersList->AddData(pListInfo[iCount],0,CXuiCtrl4JList::eSortList_Index); + //offerIndexes.push_back(i); + + if(xOffer.fUserHasPurchased) + { + HXUIBRUSH hBrush; + + if(RenderManager.IsHiDef() || RenderManager.IsWidescreen()) + { + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/DLC_Tick.png"); + XuiCreateTextureBrush(szResourceLocator,&hBrush); + } + else + { + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/DLC_TickSmall.png"); + XuiCreateTextureBrush(szResourceLocator,&hBrush); + } + m_pOffersList->UpdateGraphicFromiData(i,hBrush); + } + + /** 4J JEV: + * We've filtered results out from the list, need to keep track + * of the 'actual' list index. + */ + iCount++; + } + } + + // Check if there is nothing to display, and display the default "nothing available at this time" + if(iCount>0) + { + bNoDLCToDisplay=false; + } + } + + // turn off the timer display + m_Timer.SetShow(FALSE); + if(iCount!=0) + { + // get the right index for the first list item - it will have been re-sorted internally in the list + int iIndex=0; + xOffer=StorageManager.GetOffer(m_pOffersList->GetData(iIndex).iData); + m_pOffersList->SetCurSelVisible(0); + + DLC_INFO *dlc = app.GetDLCInfoForFullOfferID(xOffer.qwOfferID); + if (dlc != NULL) + { + BYTE *pData=NULL; + UINT uiSize=0; + DWORD dwSize=0; + + WCHAR *cString = dlc->wchBanner; + // is the file in the TMS XZP? + int iIndex = app.GetLocalTMSFileIndex(cString, true); + + if(iIndex!=-1) + { + // it's in the xzp + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + } + app.LoadLocalTMSFile(cString); + XuiCreateTextureBrushFromMemory(app.TMSFileA[iIndex].pbData,app.TMSFileA[iIndex].uiSize,&m_hXuiBrush); + } + else + { + bool bPresent = app.IsFileInMemoryTextures(cString); + if (!bPresent) + { + // Image has not come in yet + // Set the item monitored in the timer, so we can set the image when it comes in + m_pNoImageFor_DLC=dlc; + } + else + { + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + } + app.GetMemFileDetails(cString,&pData,&dwSize); + XuiCreateTextureBrushFromMemory(pData,dwSize,&m_hXuiBrush); + } + } + } + + wchar_t formatting[40]; + wstring wstrTemp = xOffer.wszSellText; + swprintf(formatting, 40, L"", m_bIsSD?12:14); + wstrTemp = formatting + wstrTemp; + + m_SellText.SetText(wstrTemp.c_str()); + m_SellText.SetShow(TRUE); + + // set the price info + m_PriceTag.SetShow(TRUE); +// swprintf(formatting, 40, L"%d",xOffer.dwPointsPrice); +// wstrTemp=wstring(formatting); +// m_PriceTag.SetText(wstrTemp.c_str()); + m_PriceTag.SetText(xOffer.wszCurrencyPrice); + + XuiElementSetShow(m_List,TRUE); + XuiElementSetFocus(m_List); + + UpdateTooltips(xOffer); + } + else if(bNoDLCToDisplay) + { + // set the default text + + wchar_t formatting[40]; + wstring wstrTemp = app.GetString(IDS_NO_DLCOFFERS); + swprintf(formatting, 40, L"", m_bIsSD?12:14); + wstrTemp = formatting + wstrTemp; + + m_SellText.SetText(wstrTemp.c_str()); + m_SellText.SetShow(TRUE); + } + return S_OK; +} + +HRESULT CScene_DLCOffers::OnDestroy() +{ + // 4J-PB - don't cancel the DLC anymore + //StorageManager.CancelGetDLCOffers(); + + // clear out any TMS images loaded from the XZP + app.FreeLocalTMSFiles(eTMSFileType_MinecraftStore); + return S_OK; +} + + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_DLCOffers::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnorePress) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_List) + { + CXuiControl pItem; + int iIndex; + CXuiCtrl4JList::LIST_ITEM_INFO ItemInfo; + // get the selected item + iIndex=m_List.GetCurSel(&pItem); + ItemInfo=m_pOffersList->GetData(iIndex); + + ULONGLONG ullIndexA[1]; + + // check if it's already installed + // if(StorageManager.GetOffer(iIndex).fUserHasPurchased) + // { + // + // } + // else + // if it's already been purchased, we need to let the user download it anyway + { + ullIndexA[0]=StorageManager.GetOffer(ItemInfo.iData).qwOfferID; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + + return S_OK; +} + +HRESULT CScene_DLCOffers::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + // Fix for Compliance fail - + // On a functional console, the game must not enter an extended unresponsive state, cause unintentional loss of player data, crash, or cause an unintended reboot of the machine. + + //if(m_bIgnorePress) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(XUSER_INDEX_ANY); + rfHandled = TRUE; + + break; + + case VK_PAD_RTHUMB_DOWN: + { + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_SellText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_SellText.m_hObj,1); + } + } + break; + case VK_PAD_RTHUMB_UP: + { + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_SellText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_SellText.m_hObj,-1); + } + } + break; + } + + return S_OK; +} + + +HRESULT CScene_DLCOffers::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + // re-enable button presses + m_bIgnorePress=false; + + return S_OK; +} + +//void CScene_DLCOffers::UpdateTooltips(XMARKETPLACE_CONTENTOFFER_INFO& xOffer) +void CScene_DLCOffers::UpdateTooltips(XMARKETPLACE_CURRENCY_CONTENTOFFER_INFO& xOffer) +{ + // if the current offer hasn't been purchased already, check if there's a trial version available + if(xOffer.fUserHasPurchased==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_INSTALL,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_REINSTALL,IDS_TOOLTIPS_BACK); + } +} + + +HRESULT CScene_DLCOffers::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + if( pGetSourceImageData->bItemData ) + { + pGetSourceImageData->hBrush = m_hXuiBrush; + + bHandled = TRUE; + } + return S_OK; +} + +HRESULT CScene_DLCOffers::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + //int index = pNotifySelChangedData->iItem; + // reset the image monitor, but not for the first selection + if(pNotifySelChangedData->iOldItem!=-1) + { + m_pNoImageFor_DLC=NULL; + } + + if (m_List.TreeHasFocus())// && offerIndexes.size() > index) + { + CXuiControl pItem; + int iIndex; + CXuiCtrl4JList::LIST_ITEM_INFO ItemInfo; + // get the selected item + iIndex=m_List.GetCurSel(&pItem); + ItemInfo=m_pOffersList->GetData(iIndex); + +// XMARKETPLACE_CONTENTOFFER_INFO xOffer= +// StorageManager.GetOffer(ItemInfo.iData); + XMARKETPLACE_CURRENCY_CONTENTOFFER_INFO xOffer= + StorageManager.GetOffer(ItemInfo.iData); + + + wchar_t formatting[40]; + wstring wstrTemp=xOffer.wszSellText; + swprintf(formatting, 40, L"",m_bIsSD?12:14); + wstrTemp = wstring(formatting) + wstrTemp; + + m_SellText.SetText(wstrTemp.c_str()); + + // set the price info + m_PriceTag.SetShow(TRUE); +// swprintf(formatting, 40, L"%d",xOffer.dwPointsPrice); +// wstrTemp=wstring(formatting); +// m_PriceTag.SetText(wstrTemp.c_str()); + m_PriceTag.SetText(xOffer.wszCurrencyPrice); + + DLC_INFO *dlc = app.GetDLCInfoForTrialOfferID(xOffer.qwOfferID); + if(dlc==NULL) + { + dlc = app.GetDLCInfoForFullOfferID(xOffer.qwOfferID); + } + + if (dlc != NULL) + { + BYTE *pImage=NULL; + UINT uiSize=0; + DWORD dwSize=0; + + WCHAR *cString = dlc->wchBanner; + + int iIndex = app.GetLocalTMSFileIndex(cString,true); + + if(iIndex!=-1) + { + // it's in the xzp + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + } + app.LoadLocalTMSFile(cString); + XuiCreateTextureBrushFromMemory(app.TMSFileA[iIndex].pbData,app.TMSFileA[iIndex].uiSize,&m_hXuiBrush); + } + else + { + bool bPresent = app.IsFileInMemoryTextures(cString); + if (!bPresent) + { + // Image has not come in yet + // Set the item monitored in the timer, so we can set the image when it comes in + m_pNoImageFor_DLC=dlc; + + // promote it to the top of the queue of images to be retrieved + // We can't trust the dwContentCategory from partnernet - it has avatar items as content instead of avatars + app.AddTMSPPFileTypeRequest(dlc->eDLCType,true); + } + else + { + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + } + app.GetMemFileDetails(cString,&pImage,&dwSize); + XuiCreateTextureBrushFromMemory(pImage,dwSize,&m_hXuiBrush); + } + } + } + else + { + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + + m_hXuiBrush=NULL; + } + } + + UpdateTooltips(xOffer); + } + else + { + m_SellText.SetText(L""); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_ACCEPT ,IDS_TOOLTIPS_BACK); + } + return S_OK; +} + + +HRESULT CScene_DLCOffers::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + // check the ethernet status - if it's disconnected, exit the xui + + if(ProfileManager.GetLiveConnectionStatus()!=XONLINE_S_LOGON_CONNECTION_ESTABLISHED) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + m_pOffersList->RemoveAllData(); + m_iOfferC=0; + StorageManager.ClearDLCOffers(); + app.ClearAndResetDLCDownloadQueue(); + + StorageManager.RequestMessageBox(IDS_CONNECTION_LOST, IDS_CONNECTION_LOST_LIVE, uiIDA, 1, ProfileManager.GetPrimaryPad(),&CConsoleMinecraftApp::EthernetDisconnectReturned,this, app.GetStringTable()); + } + + // Is the DLC we're looking for available? + if(!m_bDLCRequiredIsRetrieved) + { + if(app.DLCContentRetrieved((eDLCMarketplaceType)m_iType)) + { + m_bDLCRequiredIsRetrieved=true; + + // Retrieve the info + GetDLCInfo(app.GetDLCOffersCount(), false); + m_bIgnorePress=false; + } + } + + // Check for any TMS image we're waiting for + if(m_pNoImageFor_DLC!=NULL) + { + // Is it present now? + WCHAR *cString = m_pNoImageFor_DLC->wchBanner; + + bool bPresent = app.IsFileInMemoryTextures(cString); + + if(bPresent) + { + BYTE *pImage=NULL; + DWORD dwSize=0; + + if(m_hXuiBrush!=NULL) + { + XuiDestroyBrush(m_hXuiBrush); + // clear the TMS XZP vector memory + //app.FreeLocalTMSFiles(); + } + app.GetMemFileDetails(cString,&pImage,&dwSize); + XuiCreateTextureBrushFromMemory(pImage,dwSize,&m_hXuiBrush); + m_pNoImageFor_DLC=NULL; + } + } + + return S_OK; +} + +// int CScene_DLCOffers::EthernetDisconnectReturned(void *pParam,int iPad,const C4JStorage::EMessageResult) +// { +// CConsoleMinecraftApp* pApp = (CConsoleMinecraftApp*)pParam; +// +// pApp->NavigateBack(XUSER_INDEX_ANY); +// +// return 0; +// } + +HRESULT CScene_DLCOffers::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed - need to re-run the GetDLC + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, IDS_TOOLTIPS_BACK); + m_bIgnorePress=true; + m_pOffersList->RemoveAllData(); + m_iOfferC=0; + + // Update the dlc info + StorageManager.ClearDLCOffers(); + app.ClearAndResetDLCDownloadQueue(); + + // order these requests so the current DLC comes in first + switch(m_iType) + { + case e_DLC_Gamerpics: + app.AddDLCRequest(e_Marketplace_Gamerpics); + app.AddDLCRequest(e_Marketplace_Content); + app.AddDLCRequest(e_Marketplace_Themes); + app.AddDLCRequest(e_Marketplace_AvatarItems); + break; + case e_DLC_Themes: + app.AddDLCRequest(e_Marketplace_Themes); + app.AddDLCRequest(e_Marketplace_Content); + app.AddDLCRequest(e_Marketplace_Gamerpics); + app.AddDLCRequest(e_Marketplace_AvatarItems); + break; + case e_DLC_AvatarItems: + app.AddDLCRequest(e_Marketplace_AvatarItems); + app.AddDLCRequest(e_Marketplace_Content); + app.AddDLCRequest(e_Marketplace_Themes); + app.AddDLCRequest(e_Marketplace_Gamerpics); + break; + default: + app.AddDLCRequest(e_Marketplace_Content); + app.AddDLCRequest(e_Marketplace_Gamerpics); + app.AddDLCRequest(e_Marketplace_Themes); + app.AddDLCRequest(e_Marketplace_AvatarItems); + break; + } + + m_Timer.SetShow(TRUE); + m_bDLCRequiredIsRetrieved=false; + + return S_OK; +} diff --git a/Minecraft.Client/Common/XUI/XUI_DLCOffers.h b/Minecraft.Client/Common/XUI/XUI_DLCOffers.h new file mode 100644 index 0000000..412446d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DLCOffers.h @@ -0,0 +1,136 @@ +#pragma once + +#include "../media\xuiscene_DLCOffers.h" +#include "../media\xuiscene_DLCMain.h" + + +#include "XUI_CustomMessages.h" +#include "XUI_Ctrl_4JList.h" +#include "XUI_Ctrl_4JIcon.h" +//#include "XUI_Ctrl_DLCPrice.h" + +class CXuiCtrl4JList; +class CScene_DLCOffers; +class CXuiCtrlDLCPrice; + +class CScene_DLCMain : public CXuiSceneImpl +{ + // Xui Elements + CXuiList xList; + CXuiCtrl4JList *list; + CXuiControl m_Timer; + + // Misc + int iPad, iOfferC; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiOffersList, xList) + MAP_CONTROL(IDC_Timer, m_Timer) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + +public: + XUI_IMPLEMENT_CLASS( CScene_DLCMain, L"CScene_DLCMain", XUI_CLASS_SCENE ) + +private: + bool m_bAllDLCContentRetrieved; + bool m_bIgnoreInput; +}; + +class CScene_DLCOffers : public CXuiSceneImpl +{ +protected: + //static const wstring DEFAULT_BANNER; + + // Control and Element wrapper objects. + CXuiList m_List; + CXuiCtrl4JList *m_pOffersList; + CXuiImageElement m_Banner; + CXuiCtrl4JIcon m_TMSImage; + CXuiHtmlControl m_SellText; + CXuiControl m_PriceTag; + CXuiControl m_Timer; + HXUIBRUSH m_hXuiBrush; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_GET_SOURCE_IMAGE( OnGetSourceDataImage ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiOffersList, m_List) + MAP_CONTROL(IDC_XuiHTMLSellText, m_SellText) + MAP_CONTROL(IDC_XuiDLCPriceTag, m_PriceTag) + MAP_CONTROL(IDC_XuiDLCBanner, m_TMSImage) + MAP_CONTROL(IDC_Timer, m_Timer) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnDestroy(); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + + HRESULT GetDLCInfo( int iOfferC, bool bUpdateOnly=false ); + + //static int EthernetDisconnectReturned(void *pParam,int iPad,const C4JStorage::EMessageResult); + static int TMSReadCallback(void *pParam,int iPad,bool bResult); + + //void UpdateTooltips(XMARKETPLACE_CONTENTOFFER_INFO& xOffer); + void UpdateTooltips(XMARKETPLACE_CURRENCY_CONTENTOFFER_INFO& xOffer); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DLCOffers, L"CScene_DLCOffers", XUI_CLASS_SCENE ) + + typedef struct _DLCOffer + { + int iOfferC; + } + DLCOffer; + +private: + + //vector offerIndexes; + CScene_DLCMain *pMain; + bool m_bIgnorePress; + int m_iPad; + int m_iOfferC; + int m_iType; + bool m_bIsSD; + bool m_bAllDLCContentRetrieved; + bool m_bDLCRequiredIsRetrieved; + bool m_bIsFemale; // to only show the correct gender type offers for avatars + DLC_INFO *m_pNoImageFor_DLC; + bool bNoDLCToDisplay; // to display a default "No DLC available at this time" + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Death.cpp b/Minecraft.Client/Common/XUI/XUI_Death.cpp new file mode 100644 index 0000000..83275c1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Death.cpp @@ -0,0 +1,242 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\XUI\XUI_Death.h" +#include +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\..\Minecraft.Client\StatsCounter.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.Client\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.World\ChunkSource.h" +#include "..\..\..\Minecraft.Client\ProgressRenderer.h" +#include "..\..\..\Minecraft.Client\GameRenderer.h" +#include "..\..\..\Minecraft.Client\LevelRenderer.h" +#include "..\..\..\Minecraft.World\Pos.h" +#include "..\..\..\Minecraft.World\Dimension.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\LocalPlayer.h" +#include "..\..\..\Minecraft.World\compression.h" +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Death::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + m_bIgnoreInput = false; + + MapChildControls(); + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + XuiControlSetText(m_Title,app.GetString(IDS_YOU_DIED)); + XuiControlSetText(m_Buttons[BUTTON_DEATH_RESPAWN],app.GetString(IDS_RESPAWN)); + XuiControlSetText(m_Buttons[BUTTON_DEATH_EXITGAME],app.GetString(IDS_EXIT_GAME)); + + // Display the tooltips + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT); + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Updates the UI when the list selection changes. +//---------------------------------------------------------------------------------- +HRESULT CScene_Death::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if( hObjSource == m_Scene ) + { + /*int curSel = m_List.GetCurSel(); + + // Set the locale with the current language. + XuiSetLocale( Languages[curSel].pszLanguagePath ); + + // Apply the locale to the main scene. + XuiApplyLocale( m_hObj, NULL ); + + // Update the text for the current value. + m_Value.SetText( m_List.GetText( curSel ) );*/ + + + + bHandled = TRUE; + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Death::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + + while((uiButtonCounterUserIndex==ProfileManager.GetPrimaryPad()) + { + int playTime = -1; + if( pMinecraft->localplayers[pNotifyPressData->UserIndex] != NULL ) + { + playTime = (int)pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer(); + } + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Failed); + + if(StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + else + { + if( g_NetworkManager.IsHost() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + } + } + else + { + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Failed); + + // just exit the player + app.SetAction(pNotifyPressData->UserIndex,eAppAction_ExitPlayer); + } + } + else + { + // is it the primary player exiting? + if(pNotifyPressData->UserIndex==ProfileManager.GetPrimaryPad()) + { + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Failed); + + // adjust the trial time played + CXuiSceneBase::ReduceTrialTimerValue(); + + // exit the level + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + else + { + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Failed); + + // just exit the player + app.SetAction(pNotifyPressData->UserIndex,eAppAction_ExitPlayer); + } + } + } + break; + case BUTTON_DEATH_RESPAWN: + { + m_bIgnoreInput = true; + app.SetAction(pNotifyPressData->UserIndex,eAppAction_Respawn); + } + + break; + default: + break; + } + + + + return S_OK; +} + +HRESULT CScene_Death::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_ESCAPE: + + // kill the crafting xui + // 4J Stu - No back out, must choose + //app.CloseXuiScenes(); + + rfHandled = TRUE; + + break; + } + + return S_OK; +} + +int CScene_Death::RespawnThreadProc( void* lpParameter ) +{ + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + size_t iPad=(size_t)lpParameter; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + pMinecraft->localplayers[iPad]->respawn(); + + app.SetGameStarted(true); + pMinecraft->gameRenderer->EnableUpdateThread(); + + // If we are online, then we should wait here until the respawn is done + // If we are offline, this should release straight away + //WaitForSingleObject( pMinecraft->m_hPlayerRespawned, INFINITE ); + while(pMinecraft->localplayers[iPad]->GetPlayerRespawned()==false) + { + Sleep(50); + } + + return S_OK; +} + +HRESULT CScene_Death::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Death.h b/Minecraft.Client/Common/XUI/XUI_Death.h new file mode 100644 index 0000000..62d37d0 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Death.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../media/xuiscene_Death.h" +#include "XUI_CustomMessages.h" + +#define BUTTON_DEATH_RESPAWN 0 +#define BUTTON_DEATH_EXITGAME 1 +#define BUTTONS_DEATH_MAX BUTTON_DEATH_EXITGAME + 1 + + + +class CScene_Death : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiScene m_Scene; + CXuiControl m_Buttons[BUTTONS_DEATH_MAX]; + CXuiControl m_Title; + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Respawn, m_Buttons[BUTTON_DEATH_RESPAWN]) + MAP_CONTROL(IDC_ExitGame, m_Buttons[BUTTON_DEATH_EXITGAME]) + MAP_CONTROL(IDC_Title, m_Title) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Death, L"CScene_Death", XUI_CLASS_SCENE ) + + static int RespawnThreadProc( void* lpParameter ); +private: + bool m_bIgnoreInput; + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Debug.h b/Minecraft.Client/Common/XUI/XUI_Debug.h new file mode 100644 index 0000000..1fa6d3c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Debug.h @@ -0,0 +1,53 @@ +#pragma once + +#include "../media/xuiscene_debug.h" + +class CScene_Debug : public CXuiSceneImpl +{ + protected: + + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_CONTROL_NAVIGATE(OnControlNavigate) + XUI_END_MSG_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); +public: + + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Debug, L"CScene_Debug", XUI_CLASS_SCENE ) + +private: + + typedef struct + { + HXUIOBJ hXuiObj; + VOID *pvData; + } + DEBUGDATA; + + + static LPCWSTR m_DebugCheckboxTextA[eDebugSetting_Max+1]; + static LPCWSTR m_DebugButtonTextA[eDebugButton_Max+1]; + int m_iTotalCheckboxElements; + int m_iTotalButtonElements; + DEBUGDATA *m_DebugCheckboxDataA; + DEBUGDATA *m_DebugButtonDataA; + int m_iCurrentCheckboxElement; + int m_iCurrentButtonElement; + int m_iPad; + bool m_bOnCheckboxes; // for navigations +}; diff --git a/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.cpp b/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.cpp new file mode 100644 index 0000000..56b4126 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\Common\GameRules\ConsoleGameRules.h" +#include "XUI_DebugItemEditor.h" + +#ifdef _DEBUG_MENUS_ENABLED +HRESULT CScene_DebugItemEditor::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + MapChildControls(); + + ItemEditorInput *initData = (ItemEditorInput *)pInitData->pvInitData; + m_iPad = initData->iPad; + m_slot = initData->slot; + m_menu = initData->menu; + if(m_slot != NULL) m_item = m_slot->getItem(); + + if(m_item!=NULL) + { + m_icon->SetIcon(m_iPad, m_item->id,m_item->getAuxValue(),m_item->count,10,31,false,m_item->isFoil()); + m_itemName.SetText( app.GetString( Item::items[m_item->id]->getDescriptionId(m_item) ) ); + + m_itemId .SetText( _toString(m_item->id).c_str() ); + m_itemAuxValue .SetText( _toString(m_item->getAuxValue()).c_str() ); + m_itemCount .SetText( _toString(m_item->count).c_str() ); + m_item4JData .SetText( _toString(m_item->get4JData()).c_str() ); + } + + m_itemId .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_itemAuxValue .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_itemCount .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_item4JData .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + + m_generatedXml.SetText( CollectItemRuleDefinition::generateXml(m_item).c_str() ); + + delete initData; + + return S_OK; +} + +HRESULT CScene_DebugItemEditor::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_PAD_BACK: + // We need to send a packet to the server to update it's representation of this item + if(m_slot != NULL && m_menu != NULL) + { + m_slot->set(m_item); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player != NULL && player->connection) player->connection->send( shared_ptr( new ContainerSetSlotPacket(m_menu->containerId, m_slot->index, m_item) ) ); + } + // kill the crafting xui + app.NavigateBack(m_iPad); + + rfHandled = TRUE; + + break; + + } + + return S_OK; +} + +HRESULT CScene_DebugItemEditor::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled) +{ + if(m_item == NULL) m_item = shared_ptr( new ItemInstance(0,1,0) ); + if(hObjSource == m_itemId) + { + int id = 0; + wstring value = m_itemId.GetText(); + if(!value.empty()) id = _fromString( value ); + + // TODO Proper validation of the valid item ids + if(id > 0 && Item::items[id] != NULL) m_item->id = id; + } + else if(hObjSource == m_itemAuxValue) + { + int auxVal = 0; + wstring value = m_itemAuxValue.GetText(); + if(!value.empty()) auxVal = _fromString( value ); + if(auxVal >= 0) m_item->setAuxValue( auxVal ); + } + else if(hObjSource == m_itemCount) + { + int count = 0; + wstring value = m_itemCount.GetText(); + if(!value.empty()) count = _fromString( value ); + if(count > 0 && count <= Item::items[m_item->id]->getMaxStackSize()) m_item->count = count; + } + else if(hObjSource == m_item4JData) + { + int data = 0; + wstring value = m_item4JData.GetText(); + if(!value.empty()) data = _fromString( value ); + m_item->set4JData(data); + } + + m_icon->SetIcon(m_iPad, m_item->id,m_item->getAuxValue(),m_item->count,10,31,false,m_item->isFoil()); + + m_itemName.SetText( app.GetString( Item::items[m_item->id]->getDescriptionId(m_item) ) ); + + m_generatedXml.SetText( CollectItemRuleDefinition::generateXml(m_item).c_str() ); + return S_OK; +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.h b/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.h new file mode 100644 index 0000000..2e2f5b5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugItemEditor.h @@ -0,0 +1,57 @@ +#pragma once +using namespace std; +#include "../media/xuiscene_debug_item_editor.h" + +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "XUI_Ctrl_4JEdit.h" +#include "..\..\..\Minecraft.World\ItemInstance.h" + +class CScene_DebugItemEditor : public CXuiSceneImpl +{ +#ifdef _DEBUG_MENUS_ENABLED +public: + typedef struct _ItemEditorInput + { + int iPad; + Slot *slot; + AbstractContainerMenu *menu; + } ItemEditorInput; +private: + int m_iPad; + shared_ptr m_item; + Slot *m_slot; + AbstractContainerMenu *m_menu; + + CXuiCtrlCraftIngredientSlot *m_icon; + CXuiControl m_generatedXml, m_itemName; + CXuiCtrl4JEdit m_itemId, m_itemAuxValue, m_itemCount, m_item4JData; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_itemId, m_itemId) + MAP_CONTROL(IDC_itemAuxValue, m_itemAuxValue) + MAP_CONTROL(IDC_itemCount, m_itemCount) + MAP_CONTROL(IDC_item4JData, m_item4JData) + MAP_OVERRIDE(IDC_icon, m_icon) + MAP_CONTROL(IDC_ruleXml, m_generatedXml) + MAP_CONTROL(IDC_itemName, m_itemName) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DebugItemEditor, L"CScene_DebugItemEditor", XUI_CLASS_SCENE ) +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugOverlay.cpp b/Minecraft.Client/Common/XUI/XUI_DebugOverlay.cpp new file mode 100644 index 0000000..a23e97b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugOverlay.cpp @@ -0,0 +1,391 @@ +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\MultiplayerLevel.h" +#include "..\..\GameMode.h" +#include "..\..\SurvivalMode.h" +#include "..\..\CreativeMode.h" +#include "ClientConnection.h" +#include "MultiPlayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\ArrayWithLength.h" +#include "..\..\..\Minecraft.World\com.mojang.nbt.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.saveddata.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.enchantment.h" +#include "XUI_DebugOverlay.h" +#include "..\..\..\Minecraft.Client\GameRenderer.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\..\Minecraft.World\net.minecraft.commands.common.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileOriginal.h" + +#ifdef _DEBUG_MENUS_ENABLED +HRESULT CScene_DebugOverlay::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + MapChildControls(); + + m_items.InsertItems( 0, 512 ); + + for(unsigned int i = 0; i < Item::items.length; ++i) + { + if(Item::items[i] != NULL) + { + //m_items.InsertItems(m_items.GetItemCount(),1); + m_itemIds.push_back(i); + m_items.SetText( m_itemIds.size() - 1, app.GetString( Item::items[i]->getDescriptionId() ) ); + } + } + + m_enchantments.InsertItems( 0, Enchantment::validEnchantments.size() ); + for(unsigned int i = 0; i < Enchantment::validEnchantments.size(); ++i ) + { + Enchantment *ench = Enchantment::validEnchantments.at(i); + + m_enchantmentIds.push_back(ench->id); + m_enchantments.SetText( i, app.GetString( ench->getDescriptionId() ) ); + } + + m_mobs.InsertItems( 0, 21 ); + + m_mobs.SetText( m_mobFactories.size(), L"Chicken" ); + m_mobFactories.push_back(eTYPE_CHICKEN); + m_mobs.SetText( m_mobFactories.size(), L"Cow" ); + m_mobFactories.push_back(eTYPE_COW); + m_mobs.SetText( m_mobFactories.size(), L"Pig" ); + m_mobFactories.push_back(eTYPE_PIG); + m_mobs.SetText( m_mobFactories.size(), L"Sheep" ); + m_mobFactories.push_back(eTYPE_SHEEP); + m_mobs.SetText( m_mobFactories.size(), L"Squid" ); + m_mobFactories.push_back(eTYPE_SQUID); + m_mobs.SetText( m_mobFactories.size(), L"Wolf" ); + m_mobFactories.push_back(eTYPE_WOLF); + m_mobs.SetText( m_mobFactories.size(), L"Creeper" ); + m_mobFactories.push_back(eTYPE_CREEPER); + m_mobs.SetText( m_mobFactories.size(), L"Ghast" ); + m_mobFactories.push_back(eTYPE_GHAST); + m_mobs.SetText( m_mobFactories.size(), L"Pig Zombie" ); + m_mobFactories.push_back(eTYPE_PIGZOMBIE); + m_mobs.SetText( m_mobFactories.size(), L"Skeleton" ); + m_mobFactories.push_back(eTYPE_SKELETON); + m_mobs.SetText( m_mobFactories.size(), L"Slime" ); + m_mobFactories.push_back(eTYPE_SLIME); + m_mobs.SetText( m_mobFactories.size(), L"Spider" ); + m_mobFactories.push_back(eTYPE_SPIDER); + m_mobs.SetText( m_mobFactories.size(), L"Zombie" ); + m_mobFactories.push_back(eTYPE_ZOMBIE); + m_mobs.SetText( m_mobFactories.size(), L"Enderman" ); + m_mobFactories.push_back(eTYPE_ENDERMAN); + m_mobs.SetText( m_mobFactories.size(), L"Silverfish" ); + m_mobFactories.push_back(eTYPE_SILVERFISH); + m_mobs.SetText( m_mobFactories.size(), L"Cave Spider" ); + m_mobFactories.push_back(eTYPE_CAVESPIDER); + m_mobs.SetText( m_mobFactories.size(), L"Mooshroom" ); + m_mobFactories.push_back(eTYPE_MUSHROOMCOW); + m_mobs.SetText( m_mobFactories.size(), L"Snow Golem" ); + m_mobFactories.push_back(eTYPE_SNOWMAN); + m_mobs.SetText( m_mobFactories.size(), L"Ender Dragon" ); + m_mobFactories.push_back(eTYPE_ENDERDRAGON); + m_mobs.SetText( m_mobFactories.size(), L"Blaze" ); + m_mobFactories.push_back(eTYPE_BLAZE); + m_mobs.SetText( m_mobFactories.size(), L"Magma Cube" ); + m_mobFactories.push_back(eTYPE_LAVASLIME); + + + Minecraft *pMinecraft = Minecraft::GetInstance(); + m_setTime.SetValue( pMinecraft->level->getLevelData()->getTime() % 24000 ); + m_setFov.SetValue( (int)pMinecraft->gameRenderer->GetFovVal()); + + XuiSetTimer(m_hObj,0,DEBUG_OVERLAY_UPDATE_TIME_PERIOD); + + bHandled = TRUE; + return S_OK; +} + +// Handler for the XM_NOTIFY message +HRESULT CScene_DebugOverlay::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int nIndex; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if ( hObjPressed == m_items ) + { + nIndex = m_items.GetCurSel(); + if(nIndexUserIndex, eXuiServerAction_DropItem, (void *)id); + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( GiveItemCommand::preparePacket(dynamic_pointer_cast(Minecraft::GetInstance()->localplayers[ProfileManager.GetPrimaryPad()]), id) ); + } + } + else if ( hObjPressed == m_mobs ) + { + nIndex = m_mobs.GetCurSel(); + if(nIndexgetConnection(ProfileManager.GetPrimaryPad()); + conn->send( EnchantItemCommand::preparePacket(dynamic_pointer_cast(Minecraft::GetInstance()->localplayers[ProfileManager.GetPrimaryPad()]), m_enchantmentIds[nIndex]) ); + } + /*else if( hObjPressed == m_saveToDisc ) // 4J-JEV: Doesn't look like we use this debug option anymore. + { +#ifndef _CONTENT_PACKAGE + pMinecraft->level->save(true, NULL); + + int radius; + m_chunkRadius.GetValue(&radius); + if( radius > 0 ) + { + SaveLimitedFile(radius); + } + else + { + pMinecraft->level->getLevelStorage()->getSaveFile()->DebugFlushToFile(); + } +#endif + }*/ + else if( hObjPressed == m_createSchematic ) + { +#ifndef _CONTENT_PACKAGE + // load from the .xzp file + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + HXUIOBJ hScene; + HRESULT hr; + //const WCHAR XZP_SEPARATOR = L'#'; + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/"); + hr = XuiSceneCreate(szResourceLocator,app.GetSceneName(eUIScene_DebugCreateSchematic,false, false), NULL, &hScene); + this->NavigateForward(hScene); + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DebugCreateSchematic); +#endif + } + else if ( hObjPressed == m_setCamera ) + { +#ifndef _CONTENT_PACKAGE + // load from the .xzp file + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + HXUIOBJ hScene; + HRESULT hr; + //const WCHAR XZP_SEPARATOR = L'#'; + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/"); + hr = XuiSceneCreate(szResourceLocator,app.GetSceneName(eUIScene_DebugSetCamera, false, false), NULL, &hScene); + this->NavigateForward(hScene); + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_DebugCreateSchematic); +#endif + } + else if( hObjPressed == m_toggleRain ) + { + //app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_ToggleRain); + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( ToggleDownfallCommand::preparePacket() ); + } + else if( hObjPressed == m_toggleThunder ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_ToggleThunder); + } + else if( hObjPressed == m_resetTutorial ) + { + Tutorial::debugResetPlayerSavedProgress( ProfileManager.GetPrimaryPad() ); + } + else if( hObjPressed == m_setDay ) + { + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( TimeCommand::preparePacket(false) ); + } + else if( hObjPressed == m_setNight ) + { + ClientConnection *conn = Minecraft::GetInstance()->getConnection(ProfileManager.GetPrimaryPad()); + conn->send( TimeCommand::preparePacket(true) ); + } + + rfHandled = TRUE; + return S_OK; +} + +HRESULT CScene_DebugOverlay::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_PAD_BACK: + + // kill the crafting xui + app.EnableDebugOverlay(false,pInputData->UserIndex); + + rfHandled = TRUE; + + break; + + } + + return S_OK; +} + +HRESULT CScene_DebugOverlay::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled) +{ + if( hObjSource == m_setTime ) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // Need to set the time on both levels to stop the flickering as the local level + // tries to predict the time + // Only works if we are on the host machine, but shouldn't break if not + MinecraftServer::SetTime(pNotifyValueChangedData->nValue); + pMinecraft->level->getLevelData()->setTime(pNotifyValueChangedData->nValue); + } + if( hObjSource == m_setFov ) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->gameRenderer->SetFovVal((float)pNotifyValueChangedData->nValue); + } + return S_OK; +} + +HRESULT CScene_DebugOverlay::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->level != NULL) + { + m_setTime.SetValue( pMinecraft->level->getLevelData()->getTime() % 24000 ); + m_setFov.SetValue( (int)pMinecraft->gameRenderer->GetFovVal()); + } + return S_OK; +} + +void CScene_DebugOverlay::SetSpawnToPlayerPos() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + pMinecraft->level->getLevelData()->setXSpawn((int)pMinecraft->player->x); + pMinecraft->level->getLevelData()->setYSpawn((int)pMinecraft->player->y); + pMinecraft->level->getLevelData()->setZSpawn((int)pMinecraft->player->z); +} + +#ifndef _CONTENT_PACKAGE +void CScene_DebugOverlay::SaveLimitedFile(int chunkRadius) +{ + unordered_map newFileCache; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + ConsoleSaveFile *currentSave = pMinecraft->level->getLevelStorage()->getSaveFile(); + + // With a size of 0 but a value in the data pointer we should create a new save + ConsoleSaveFileOriginal newSave( currentSave->getFilename(), NULL, 0, true ); + + // TODO Make this only happen for the new save + //SetSpawnToPlayerPos(); + FileEntry *origFileEntry = currentSave->createFile( wstring( L"level.dat" ) ); + byteArray levelData( origFileEntry->getFileSize() ); + DWORD bytesRead; + currentSave->setFilePointer(origFileEntry,0,NULL,FILE_BEGIN); + currentSave->readFile( + origFileEntry, + levelData.data, // data buffer + origFileEntry->getFileSize(), // number of bytes to read + &bytesRead // number of bytes read + ); + + FileEntry *newFileEntry = newSave.createFile( wstring( L"level.dat" ) ); + DWORD bytesWritten; + newSave.writeFile( newFileEntry, + levelData.data, // data buffer + origFileEntry->getFileSize(), // number of bytes to write + &bytesWritten // number of bytes written + ); + + int playerChunkX = pMinecraft->player->xChunk; + int playerChunkZ = pMinecraft->player->zChunk; + + for(int xPos = playerChunkX - chunkRadius; xPos < playerChunkX + chunkRadius; ++xPos) + { + for(int zPos = playerChunkZ - chunkRadius; zPos < playerChunkZ + chunkRadius; ++zPos) + { + CompoundTag *chunkData=NULL; + + DataInputStream *is = RegionFileCache::getChunkDataInputStream(currentSave, L"", xPos, zPos); + if (is != NULL) + { + chunkData = NbtIo::read((DataInput *)is); + is->deleteChildStream(); + delete is; + } + app.DebugPrintf("Processing chunk (%d, %d)\n", xPos, zPos); + DataOutputStream *os = getChunkDataOutputStream(newFileCache, &newSave, L"", xPos, zPos); + if(os != NULL) + { + NbtIo::write(chunkData, os); + os->close(); + + // 4J Stu - getChunkDataOutputStream makes a new DataOutputStream that points to a new ChunkBuffer( ByteArrayOutputStream ) + // We should clean these up when we are done + os->deleteChildStream(); + delete os; + } + if(chunkData != NULL) + { + delete chunkData; + } + } + } + + newSave.DebugFlushToFile(); +} +#endif + +RegionFile *CScene_DebugOverlay::getRegionFile(unordered_map &newFileCache, ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - TODO was synchronized +{ + File file( prefix + wstring(L"r.") + _toString(chunkX>>5) + L"." + _toString(chunkZ>>5) + L".mcr" ); + + RegionFile *ref = NULL; + AUTO_VAR(it, newFileCache.find(file)); + if( it != newFileCache.end() ) + ref = it->second; + + // 4J Jev, put back in. + if (ref != NULL) + { + return ref; + } + + RegionFile *reg = new RegionFile(saveFile, &file); + newFileCache[file] = reg; // 4J - this was originally a softReferenc + return reg; +} + +DataOutputStream *CScene_DebugOverlay::getChunkDataOutputStream(unordered_map &newFileCache, ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) +{ + RegionFile *r = getRegionFile(newFileCache, saveFile, prefix, chunkX, chunkZ); + return r->getChunkDataOutputStream(chunkX & 31, chunkZ & 31); +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugOverlay.h b/Minecraft.Client/Common/XUI/XUI_DebugOverlay.h new file mode 100644 index 0000000..ed5f85c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugOverlay.h @@ -0,0 +1,72 @@ +#pragma once +using namespace std; +#include "../media/xuiscene_debugoverlay.h" + +#define DEBUG_OVERLAY_UPDATE_TIME_PERIOD 10000 + +class RegionFile; +class DataOutputStream; +class ConsoleSaveFile; +#include "..\..\..\Minecraft.World\File.h" +#include "..\..\..\Minecraft.World\Entity.h" + +class CScene_DebugOverlay : public CXuiSceneImpl +{ +#ifdef _DEBUG_MENUS_ENABLED +private: + CXuiList m_items, m_mobs, m_enchantments; + CXuiControl m_resetTutorial, m_createSchematic, m_toggleRain, m_toggleThunder, m_setCamera; + CXuiControl m_setDay, m_setNight; + CXuiSlider m_chunkRadius, m_setTime,m_setFov; + vector m_itemIds; + vector m_mobFactories; + vector m_enchantmentIds; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX( OnNotifyPressEx ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_ChunkRadius, m_chunkRadius) + MAP_CONTROL(IDC_ResetTutorial, m_resetTutorial) + MAP_CONTROL(IDC_CreateSchematic, m_createSchematic) + MAP_CONTROL(IDC_ToggleRain, m_toggleRain) + MAP_CONTROL(IDC_ToggleThunder, m_toggleThunder) + MAP_CONTROL(IDC_SetDay, m_setDay) + MAP_CONTROL(IDC_SetNight, m_setNight) + MAP_CONTROL(IDC_SliderTime, m_setTime) + MAP_CONTROL(IDC_SliderFov, m_setFov) + MAP_CONTROL(IDC_MobList, m_mobs) + MAP_CONTROL(IDC_EnchantmentsList, m_enchantments) + MAP_CONTROL(IDC_ItemsList, m_items) + MAP_CONTROL(IDC_SetCamera, m_setCamera) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DebugOverlay, L"CScene_DebugOverlay", XUI_CLASS_SCENE ) + +private: + void SetSpawnToPlayerPos(); +#ifndef _CONTENT_PACKAGE + void SaveLimitedFile(int chunkRadius); +#endif + RegionFile *getRegionFile(unordered_map &newFileCache, ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); + + DataOutputStream *getChunkDataOutputStream(unordered_map &newFileCache, ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.cpp b/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.cpp new file mode 100644 index 0000000..66279d1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.cpp @@ -0,0 +1,178 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XUI_DebugSchematicCreator.h" +#include "..\..\..\Minecraft.World\ChunkSource.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" + +#ifndef _CONTENT_PACKAGE +HRESULT CScene_DebugSchematicCreator::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + MapChildControls(); + + m_startX .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_startY .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_startZ .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_endX .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_endY .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_endZ .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + + m_data = new ConsoleSchematicFile::XboxSchematicInitParam(); + + return S_OK; +} + +HRESULT CScene_DebugSchematicCreator::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if ( hObjPressed == m_createButton ) + { + // We want the start to be even + if(m_data->startX > 0 && m_data->startX%2 != 0) + m_data->startX-=1; + else if(m_data->startX < 0 && m_data->startX%2 !=0) + m_data->startX-=1; + if(m_data->startY < 0) m_data->startY = 0; + else if(m_data->startY > 0 && m_data->startY%2 != 0) + m_data->startY-=1; + if(m_data->startZ > 0 && m_data->startZ%2 != 0) + m_data->startZ-=1; + else if(m_data->startZ < 0 && m_data->startZ%2 !=0) + m_data->startZ-=1; + + // We want the end to be odd to have a total size that is even + if(m_data->endX > 0 && m_data->endX%2 == 0) + m_data->endX+=1; + else if(m_data->endX < 0 && m_data->endX%2 ==0) + m_data->endX+=1; + if(m_data->endY > Level::maxBuildHeight) + m_data->endY = Level::maxBuildHeight; + else if(m_data->endY > 0 && m_data->endY%2 == 0) + m_data->endY+=1; + else if(m_data->endY < 0 && m_data->endY%2 ==0) + m_data->endY+=1; + if(m_data->endZ > 0 && m_data->endZ%2 == 0) + m_data->endZ+=1; + else if(m_data->endZ < 0 && m_data->endZ%2 ==0) + m_data->endZ+=1; + + wstring value = m_name.GetText(); + if(!value.empty()) + { + swprintf(m_data->name,64,L"%ls", value.c_str()); + } + else + { + swprintf(m_data->name,64,L"schematic"); + } + + m_data->bSaveMobs = m_saveMobs.IsChecked(); + +#ifdef _XBOX + if (m_useXboxCompr.IsChecked()) + m_data->compressionType = Compression::eCompressionType_LZXRLE; + else +#endif + m_data->compressionType = Compression::eCompressionType_RLE; + + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(), eXuiServerAction_ExportSchematic, (void *)m_data); + + NavigateBack(); + rfHandled = TRUE; + } + return S_OK; +} + +HRESULT CScene_DebugSchematicCreator::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_PAD_BACK: + NavigateBack(); + + rfHandled = TRUE; + + break; + + } + + return S_OK; +} + +HRESULT CScene_DebugSchematicCreator::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled) +{ + if(hObjSource == m_startX) + { + int iVal = 0; + wstring value = m_startX.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->startX = iVal; + } + } + else if(hObjSource == m_startY) + { + int iVal = 0; + wstring value = m_startY.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->startY = iVal; + } + } + else if(hObjSource == m_startZ) + { + int iVal = 0; + wstring value = m_startZ.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->startZ = iVal; + } + } + else if(hObjSource == m_endX) + { + int iVal = 0; + wstring value = m_endX.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->endX = iVal; + } + } + else if(hObjSource == m_endY) + { + int iVal = 0; + wstring value = m_endY.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->endY = iVal; + } + } + else if(hObjSource == m_endZ) + { + int iVal = 0; + wstring value = m_endZ.GetText(); + if(!value.empty()) iVal = _fromString( value ); + + if( iVal >= (LEVEL_MAX_WIDTH * -16) || iVal < (LEVEL_MAX_WIDTH * 16)) + { + m_data->endZ = iVal; + } + } + return S_OK; +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.h b/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.h new file mode 100644 index 0000000..b502d11 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugSchematicCreator.h @@ -0,0 +1,49 @@ +#pragma once +#include "..\Media\xuiscene_debug_schematic_create.h" +#include "XUI_Ctrl_4JEdit.h" +#include "..\..\Common\GameRules\ConsoleSchematicFile.h" + +class CScene_DebugSchematicCreator : public CXuiSceneImpl +{ +#ifndef _CONTENT_PACKAGE +private: + CXuiControl m_createButton; + CXuiCtrl4JEdit m_name, m_startX, m_startY, m_startZ, m_endX, m_endY, m_endZ; + CXuiCheckbox m_saveMobs, m_useXboxCompr; + + ConsoleSchematicFile::XboxSchematicInitParam *m_data; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX( OnNotifyPressEx ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_CreateButton, m_createButton) + MAP_CONTROL(IDC_Name, m_name) + MAP_CONTROL(IDC_StartX, m_startX) + MAP_CONTROL(IDC_StartY, m_startY) + MAP_CONTROL(IDC_StartZ, m_startZ) + MAP_CONTROL(IDC_EndX, m_endX) + MAP_CONTROL(IDC_EndY, m_endY) + MAP_CONTROL(IDC_EndZ, m_endZ) + MAP_CONTROL(IDC_SaveMobs, m_saveMobs) + MAP_CONTROL(IDC_UseXboxCompression, m_useXboxCompr) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DebugSchematicCreator, L"CScene_DebugSchematicCreator", XUI_CLASS_SCENE ) +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.cpp b/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.cpp new file mode 100644 index 0000000..2227e89 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.cpp @@ -0,0 +1,152 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XUI_DebugSetCamera.h" +#include "..\..\..\Minecraft.World\ChunkSource.h" + +// #include "..\..\Xbox\4JLibs\inc\4J_Input.h" + +#include "..\..\Minecraft.h" +#include "..\..\MultiplayerLocalPlayer.h" + +#ifndef _CONTENT_PACKAGE +HRESULT CScene_DebugSetCamera::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + MapChildControls(); + + m_camX .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_camY .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_camZ .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_yRot .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + m_elevation .SetKeyboardType(C_4JInput::EKeyboardMode_Numeric); + + int playerNo = 0; + currentPosition = new DebugSetCameraPosition(); + + currentPosition->player = playerNo; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + if (pMinecraft != NULL) + { + Vec3 *vec = pMinecraft->localplayers[playerNo]->getPos(1.0); + + currentPosition->m_camX = vec->x; + currentPosition->m_camY = vec->y - 1.62;// pMinecraft->localplayers[playerNo]->getHeadHeight(); + currentPosition->m_camZ = vec->z; + + currentPosition->m_yRot = pMinecraft->localplayers[playerNo]->yRot; + currentPosition->m_elev = pMinecraft->localplayers[playerNo]->xRot; + } + + m_camX.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + m_camY.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + m_camZ.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + m_yRot.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + m_elevation.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + + m_camX.SetText((CONST WCHAR *) _toString(currentPosition->m_camX).c_str()); + m_camY.SetText((CONST WCHAR *) _toString(currentPosition->m_camY + 1.62).c_str()); + m_camZ.SetText((CONST WCHAR *) _toString(currentPosition->m_camZ).c_str()); + + m_yRot.SetText((CONST WCHAR *) _toString(currentPosition->m_yRot).c_str()); + m_elevation.SetText((CONST WCHAR *) _toString(currentPosition->m_elev).c_str()); + + //fpp = new FreezePlayerParam(); + //fpp->player = playerNo; + //fpp->freeze = true; + + //m_lockPlayer.SetCheck( !fpp->freeze ); + + m_lockPlayer.SetCheck( app.GetFreezePlayers() ); + + return S_OK; +} + +HRESULT CScene_DebugSetCamera::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if (hObjPressed == m_teleport) + { + app.SetXuiServerAction( ProfileManager.GetPrimaryPad(), + eXuiServerAction_SetCameraLocation, + (void *)currentPosition); + rfHandled = TRUE; + } + else if (hObjPressed == m_lockPlayer) + { + app.SetFreezePlayers( m_lockPlayer.IsChecked() ); + + rfHandled = TRUE; + } + + return S_OK; +} + +HRESULT CScene_DebugSetCamera::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_PAD_START: + case VK_PAD_BACK: + NavigateBack(); + + //delete currentPosition; + //currentPosition = NULL; + + rfHandled = TRUE; + break; + } + return S_OK; +} + +HRESULT CScene_DebugSetCamera::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled) +{ + + // Text Boxes + if (hObjSource == m_camX) + { + double iVal = 0; + wstring value = m_camX.GetText(); + if(!value.empty()) iVal = _fromString( value ); + currentPosition->m_camX = iVal; + bHandled = TRUE; + } + else if (hObjSource == m_camY) + { + double iVal = 0; + wstring value = m_camY.GetText(); + if(!value.empty()) iVal = _fromString( value ); + currentPosition->m_camY = iVal - 1.62; + bHandled = TRUE; + } + else if (hObjSource == m_camZ) + { + double iVal = 0; + wstring value = m_camZ.GetText(); + if(!value.empty()) iVal = _fromString( value ); + currentPosition->m_camZ = iVal; + bHandled = TRUE; + } + else if (hObjSource == m_yRot) + { + double iVal = 0; + wstring value = m_yRot.GetText(); + if(!value.empty()) iVal = _fromString( value ); + currentPosition->m_yRot = iVal; + bHandled = TRUE; + } + else if (hObjSource == m_elevation) + { + double iVal = 0; + wstring value = m_elevation.GetText(); + if(!value.empty()) iVal = _fromString( value ); + currentPosition->m_elev = iVal; + bHandled = TRUE; + } + + return S_OK; +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.h b/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.h new file mode 100644 index 0000000..1bc662e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugSetCamera.h @@ -0,0 +1,54 @@ +#pragma once +#include "..\Media\xuiscene_debug_set_camera.h" +#include "XUI_Ctrl_4JEdit.h" +#include "..\..\Common\GameRules\ConsoleSchematicFile.h" + +class CScene_DebugSetCamera : public CXuiSceneImpl +{ +public: + typedef struct _FreezePlayerParam + { + int player; + bool freeze; + } FreezePlayerParam; + +#ifndef _CONTENT_PACKAGE +private: + CXuiCtrl4JEdit m_camX, m_camY, m_camZ, m_yRot, m_elevation; + CXuiCheckbox m_lockPlayer; + CXuiControl m_teleport; + + DebugSetCameraPosition *currentPosition; + FreezePlayerParam *fpp; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX( OnNotifyPressEx ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_CamX, m_camX) + MAP_CONTROL(IDC_CamY, m_camY) + MAP_CONTROL(IDC_CamZ, m_camZ) + MAP_CONTROL(IDC_YRot, m_yRot) + MAP_CONTROL(IDC_Elevation, m_elevation) + MAP_CONTROL(IDC_LockPlayer, m_lockPlayer) + MAP_CONTROL(IDC_Teleport, m_teleport) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged *pNotifyValueChangedData, BOOL &bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DebugSetCamera, L"CScene_DebugSetCamera", XUI_CLASS_SCENE ) +#endif +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugTips.cpp b/Minecraft.Client/Common/XUI/XUI_DebugTips.cpp new file mode 100644 index 0000000..aaa3b06 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugTips.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" + +#include +#include "XUI_DebugTips.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_DebugTips::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + m_bIgnoreInput = false; + + MapChildControls(); + + // Display the tooltips + //ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT); + // display the next tip + wstring wsText=app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + wchar_t startTags[64]; + swprintf(startTags,64,L"
",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"
"; + XuiControlSetText(m_tip,wsText.c_str()); + + return S_OK; +} + + +HRESULT CScene_DebugTips::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + //ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + + switch(pInputData->dwKeyCode) + { + case VK_PAD_A: + { + + + // next tip + // display the next tip + wstring wsText=app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + wchar_t startTags[64]; + swprintf(startTags,64,L"
",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"
"; + XuiControlSetText(m_tip,wsText.c_str()); + + rfHandled = TRUE; + } + break; + + case VK_PAD_B: + case VK_PAD_START: + case VK_ESCAPE: + + app.NavigateBack(m_iPad); + + rfHandled = TRUE; + + break; +#ifndef _CONTENT_PACKAGE + case VK_PAD_LTHUMB_PRESS: +#ifdef _XBOX + app.OverrideFontRenderer(true); +#endif + break; +#endif + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_DebugTips.h b/Minecraft.Client/Common/XUI/XUI_DebugTips.h new file mode 100644 index 0000000..10627d3 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_DebugTips.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../media/xuiscene_DebugTips.h" + + + + +class CScene_DebugTips : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiControl m_tip; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Tip, m_tip) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_DebugTips, L"CScene_DebugTips", XUI_CLASS_SCENE ) + +private: + bool m_bIgnoreInput; + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.cpp b/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.cpp new file mode 100644 index 0000000..f66c0d7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.cpp @@ -0,0 +1,375 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\..\Minecraft.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Common\Tutorial\TutorialMode.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_FullscreenProgress::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + m_buttonConfirm.SetText( app.GetString( IDS_CONFIRM_OK ) ); + + LoadingInputParams *params = (LoadingInputParams *)pInitData->pvInitData; + + m_CompletionData = params->completionData; + m_iPad=params->completionData->iPad; + m_cancelFunc = params->cancelFunc; + m_cancelFuncParam = params->m_cancelFuncParam; + m_completeFunc = params->completeFunc; + m_completeFuncParam = params->m_completeFuncParam; + m_bWasCancelled=false; + + thread = new C4JThread(params->func, params->lpParam, "FullscreenProgress"); + thread->SetProcessor(3); // TODO 4J Stu - Make sure this is a good thread/core to use + + m_threadCompleted = false; + threadStarted = false; + //ResumeThread( thread ); + if( CXuiSceneBase::GetPlayerBasePosition(m_iPad) != CXuiSceneBase::e_BaseScene_Fullscreen && CXuiSceneBase::GetPlayerBasePosition(m_iPad) != CXuiSceneBase::e_BaseScene_NotSet) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, params->completionData->bShowLogo ); + } + + CXuiSceneBase::ShowBackground( m_iPad, params->completionData->bShowBackground ); + ui.SetTooltips( m_iPad, -1, params->cancelText, -1, -1 ); + + // Clear the progress text + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStart(-1); + pMinecraft->progressRenderer->progressStage(-1); + + // set the tip + wstring wsText=app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + wchar_t startTags[64]; + swprintf(startTags,64,L"
",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"
"; + XuiControlSetText(m_tip, wsText.c_str()); + + m_tip.SetShow( m_CompletionData->bShowTips ); + + return S_OK; +} + +// The framework calls this handler when the object is to be destroyed. +HRESULT CScene_FullscreenProgress::OnDestroy() +{ + if( thread != NULL && thread != INVALID_HANDLE_VALUE ) + delete thread; + + if( m_CompletionData != NULL ) + delete m_CompletionData; + + return S_OK; +} + +HRESULT CScene_FullscreenProgress::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + // 4J-JEV: Fix for Xbox360 #162749 - TU17: Save Upload: Content: UI: Player is presented with non-functional Tooltips after the Upload Save For Xbox One is completed. + if( m_cancelFunc != NULL && !m_threadCompleted ) + { + m_cancelFunc( m_cancelFuncParam ); + m_bWasCancelled=true; + } + break; + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Updates the UI when the list selection changes. +//---------------------------------------------------------------------------------- +HRESULT CScene_FullscreenProgress::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_FullscreenProgress::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(m_threadCompleted == true && hObjPressed == m_buttonConfirm) + { + // if there's a complete function, call it + if(m_completeFunc) + { + m_completeFunc(m_completeFuncParam); + } + switch(m_CompletionData->type) + { + case e_ProgressCompletion_NavigateBack: + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, FALSE ); + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, TRUE ); + app.NavigateBack(m_CompletionData->iPad); + // Show the other players scenes + CXuiSceneBase::ShowOtherPlayersBaseScene(m_CompletionData->iPad, true); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_NavigateBackToScene: + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, FALSE ); + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, TRUE ); + CXuiSceneBase::ShowOtherPlayersBaseScene(m_CompletionData->iPad, true); + // If the pause menu is still active, then navigate back + // Otherwise close everything then navigate forwads to the pause menu + if(app.IsSceneInStack(m_CompletionData->iPad, m_CompletionData->scene)) + { + app.NavigateBack(m_CompletionData->iPad,false, m_CompletionData->scene); + } + else + { + app.CloseXuiScenesAndNavigateToScene(m_CompletionData->iPad,m_CompletionData->scene); + } + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseUIScenes: + app.CloseXuiScenes(m_CompletionData->iPad); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseAllPlayersUIScenes: + app.CloseAllPlayersXuiScenes(); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_NavigateToHomeMenu: + app.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + break; + } + } + + return S_OK; +} + +HRESULT CScene_FullscreenProgress::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + // This gets called every frame, so use it to update our two text boxes + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + int title = pMinecraft->progressRenderer->getCurrentTitle(); + if(title >= 0) + m_title.SetText( app.GetString( title ) ); + else + m_title.SetText( L"" ); + + int status = pMinecraft->progressRenderer->getCurrentStatus(); + if(status >= 0) + m_status.SetText( app.GetString( status ) ); + else + m_status.SetText( L"" ); + + return S_OK; +} + +HRESULT CScene_FullscreenProgress::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + if(!threadStarted) + { + thread->Run(); + threadStarted = true; + XuiSetTimer(m_hObj,TIMER_FULLSCREEN_PROGRESS,TIMER_FULLSCREEN_PROGRESS_TIME); + XuiSetTimer(m_hObj,TIMER_FULLSCREEN_TIPS,TIMER_FULLSCREEN_TIPS_TIME); + } + return S_OK; +} + +HRESULT CScene_FullscreenProgress::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + int code = thread->GetExitCode(); + DWORD exitcode = *((DWORD *)&code); + + //app.DebugPrintf("CScene_FullscreenProgress Timer %d\n",pTimer->nId); + + if( exitcode != STILL_ACTIVE ) + { + // 4J-PB - need to kill the timers whatever happens + XuiKillTimer(m_hObj,TIMER_FULLSCREEN_PROGRESS); + XuiKillTimer(m_hObj,TIMER_FULLSCREEN_TIPS); + XuiControlSetText(m_tip,L""); + + // hide the tips bar in cause we're waiting for the user to press ok + m_tip.SetShow( FALSE ); + + // If we failed (currently used by network connection thread), navigate back + if( exitcode != S_OK ) + { + if( exitcode == ERROR_CANCELLED ) + { + // Current thread cancelled for whatever reason + // Currently used only for the CConsoleMinecraftApp::RemoteSaveThreadProc thread + // Assume to just ignore this thread as something else is now running that will + // cause another action + } + else + { + /*m_threadCompleted = true; + m_buttonConfirm.SetShow( TRUE ); + m_buttonConfirm.SetFocus( m_CompletionData->iPad ); + m_CompletionData->type = e_ProgressCompletion_NavigateToHomeMenu; + + int exitReasonStringId; + switch( app.GetDisconnectReason() ) + { + default: + exitReasonStringId = IDS_CONNECTION_FAILED; + } + Minecraft *pMinecraft=Minecraft::GetInstance(); + pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId );*/ + //app.NavigateBack(m_CompletionData->iPad); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_CONNECTION_FAILED, IDS_CONNECTION_LOST_SERVER, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + + app.NavigateToHomeMenu(); + ui.UpdatePlayerBasePositions(); + } + } + else + { + if(( m_CompletionData->bRequiresUserAction == TRUE ) && (!m_bWasCancelled)) + { + m_threadCompleted = true; + m_buttonConfirm.SetShow( TRUE ); + m_buttonConfirm.SetFocus( ProfileManager.GetPrimaryPad() ); + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT, -1, -1, -1 ); + } + else + { + if(m_bWasCancelled) + { + m_threadCompleted = true; + } + app.DebugPrintf("FullScreenProgress complete with action: "); + switch(m_CompletionData->type) + { + case e_ProgressCompletion_AutosaveNavigateBack: + app.DebugPrintf("e_ProgressCompletion_AutosaveNavigateBack\n"); + { + // store these - they get wiped by the destroy caused by navigateback + int iPad=m_CompletionData->iPad; + //bool bAutosaveWasMenuDisplayed=m_CompletionData->bAutosaveWasMenuDisplayed; + CXuiSceneBase::ShowBackground( iPad, FALSE ); + CXuiSceneBase::ShowLogo(iPad, FALSE ); + app.NavigateBack(iPad); + + // 4J Stu - Fix for #65437 - Customer Encountered: Code: Settings: Autosave option doesn't work when the Host goes into idle state during gameplay. + // Autosave obviously cannot occur if an ignore autosave menu is displayed, so even if we navigate back to a scene and not empty + // then we still want to reset this flag which was set true by the navigate to the fullscreen progress + app.SetIgnoreAutosaveMenuDisplayed(iPad, false); + + // the navigate back leaves SetMenuDisplayed as true, but there may not have been a menu up when autosave was kicked off +// if(bAutosaveWasMenuDisplayed==false) +// { +// app.SetMenuDisplayed(iPad,false); +// } + // Show the other players scenes + CXuiSceneBase::ShowOtherPlayersBaseScene(iPad, true); + // This just allows it to be shown + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(true); + ui.UpdatePlayerBasePositions(); + } + break; + + case e_ProgressCompletion_NavigateBack: + app.DebugPrintf("e_ProgressCompletion_NavigateBack\n"); + { + // store these - they get wiped by the destroy caused by navigateback + int iPad=m_CompletionData->iPad; + + CXuiSceneBase::ShowBackground( iPad, FALSE ); + CXuiSceneBase::ShowBackground( iPad, TRUE ); + app.NavigateBack(iPad); + // Show the other players scenes + CXuiSceneBase::ShowOtherPlayersBaseScene(iPad, true); + ui.UpdatePlayerBasePositions(); + } + break; + case e_ProgressCompletion_NavigateBackToScene: + app.DebugPrintf("e_ProgressCompletion_NavigateBackToScene\n"); + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, FALSE ); + CXuiSceneBase::ShowBackground( m_CompletionData->iPad, TRUE ); + CXuiSceneBase::ShowOtherPlayersBaseScene(m_CompletionData->iPad, true); + // If the pause menu is still active, then navigate back + // Otherwise close everything then navigate forwads to the pause menu + if(app.IsSceneInStack(m_CompletionData->iPad, m_CompletionData->scene)) + { + app.NavigateBack(m_CompletionData->iPad,false, m_CompletionData->scene); + } + else + { + app.CloseXuiScenesAndNavigateToScene(m_CompletionData->iPad,m_CompletionData->scene); + } + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseUIScenes\n"); + app.CloseXuiScenes(m_CompletionData->iPad); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_CloseAllPlayersUIScenes: + app.DebugPrintf("e_ProgressCompletion_CloseAllPlayersUIScenes\n"); + app.CloseAllPlayersXuiScenes(); + ui.UpdatePlayerBasePositions(); + break; + case e_ProgressCompletion_NavigateToHomeMenu: + app.DebugPrintf("e_ProgressCompletion_NavigateToHomeMenu\n"); + app.NavigateToHomeMenu(); + //ui.UpdatePlayerBasePositions(); + break; + default: + app.DebugPrintf("Default\n"); + break; + } + } + } + } + else + { + switch(pTimer->nId) + { + case TIMER_FULLSCREEN_PROGRESS: + break; + case TIMER_FULLSCREEN_TIPS: + { + // display the next tip + wstring wsText=app.FormatHTMLString(m_iPad,app.GetString(app.GetNextTip())); + wchar_t startTags[64]; + swprintf(startTags,64,L"
",app.GetHTMLColour(eHTMLColor_White)); + wsText= startTags + wsText + L"
"; + XuiControlSetText(m_tip,wsText.c_str()); + } + break; + } + } + + bHandled = TRUE; + + return( S_OK ); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.h b/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.h new file mode 100644 index 0000000..271a578 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_FullscreenProgress.h @@ -0,0 +1,68 @@ +#pragma once +#include "../media/xuiscene_fullscreenprogress.h" +#include "..\..\..\Minecraft.World\C4JThread.h" + +#define ERROR_FULLSCREENPROGRESS_ + +class CScene_FullscreenProgress : public CXuiSceneImpl +{ +private: + C4JThread* thread; + bool threadStarted; + UIFullscreenProgressCompletionData *m_CompletionData; + + static const int TIMER_FULLSCREEN_PROGRESS = 0; + static const int TIMER_FULLSCREEN_TIPS = 1; + + static const int TIMER_FULLSCREEN_PROGRESS_TIME = 500; + static const int TIMER_FULLSCREEN_TIPS_TIME = 7000; +protected: + // Control and Element wrapper objects. + CXuiControl m_title, m_status, m_buttonConfirm, m_tip; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_TRANSITION_START( OnTransitionStart ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Title, m_title) + MAP_CONTROL(IDC_Tip, m_tip) + MAP_CONTROL(IDC_Status, m_status) + MAP_CONTROL(IDC_ButtonConfirm, m_buttonConfirm) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_FullscreenProgress, L"CScene_FullscreenProgress", XUI_CLASS_SCENE ) + +private: + bool m_threadCompleted; + int m_iPad; + void (*m_cancelFunc)(LPVOID param); + void (*m_completeFunc)(LPVOID param); + LPVOID m_cancelFuncParam; + LPVOID m_completeFuncParam; + D3DXVECTOR3 m_OriginalPosition; + bool m_bWasCancelled; + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_HUD.cpp b/Minecraft.Client/Common/XUI/XUI_HUD.cpp new file mode 100644 index 0000000..286f06a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HUD.cpp @@ -0,0 +1,464 @@ +#include "stdafx.h" +#include "XUI_HUD.h" +#include "..\..\Minecraft.h" +#include "..\..\Gui.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.material.h" + +HRESULT CXuiSceneHud::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + XuiElementGetPosition(m_hObj,&m_OriginalPosition); + + m_tickCount = 0; + + return S_OK; +} + +HRESULT CXuiSceneHud::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + + app.ReloadHudScene(m_iPad, bJoining); + + return S_OK; +} + +HRESULT CXuiSceneHud::OnCustomMessage_TickScene() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localplayers[m_iPad] == NULL || pMinecraft->localgameModes[m_iPad] == NULL) return S_OK; + + ++m_tickCount; + + int iGuiScale; + + if(pMinecraft->localplayers[m_iPad]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISize); + } + else + { + iGuiScale=app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen); + } + + int startFrame = 0; + switch(iGuiScale) + { + case 0: + XuiElementFindNamedFrame(m_hObj, L"ScaleSmall", &startFrame); + break; + case 1: + XuiElementFindNamedFrame(m_hObj, L"Normal", &startFrame); + break; + case 2: + XuiElementFindNamedFrame(m_hObj, L"ScaleLarge", &startFrame); + break; + } + if(startFrame >= 0) XuiElementPlayTimeline( m_hObj, startFrame, startFrame, startFrame, FALSE, TRUE); + + // Move the whole hud group if we are not in fullscreen + if(pMinecraft->localplayers[m_iPad]->m_iScreenSection != C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + int iTooltipsYOffset = 0; + // if tooltips are off, set the y offset to zero + if(app.GetGameSettings(m_iPad,eGameSetting_Tooltips)==0) + { + switch(iGuiScale) + { + case 0: + iTooltipsYOffset=28;//screenHeight/10; + break; + case 2: + iTooltipsYOffset=28;//screenHeight/10; + break; + case 1: + default: + iTooltipsYOffset=28;//screenHeight/10; + break; + } + } + + float fHeight, fWidth; + GetBounds(&fWidth, &fHeight); + + int iSafezoneYHalf = 0; + switch(pMinecraft->localplayers[m_iPad]->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + iSafezoneYHalf = -fHeight/10;// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + iSafezoneYHalf = (fHeight/2)-(fHeight/10);// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + iSafezoneYHalf = (fHeight/2)-(fHeight/10);// 5% (need to treat the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + iSafezoneYHalf = -fHeight/10; // 5% (the whole screen is 2x this screen) + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + iSafezoneYHalf = -fHeight/10; // 5% (the whole screen is 2x this screen) + break; + }; + + D3DXVECTOR3 pos; + m_hudGroup.GetPosition(&pos); + pos.y = iTooltipsYOffset + iSafezoneYHalf; + m_hudGroup.SetPosition(&pos); + } + + // Update inventory + float opacity = 1.0f; + GetOpacity(&opacity); + int selected = pMinecraft->localplayers[m_iPad]->inventory->selected; + for(unsigned int j = 0; j < 9; ++j) + { + m_hotbarIcon[j]->SetSlot(m_hotbarIcon[j]->m_hObj,pMinecraft->localplayers[m_iPad]->inventoryMenu->getSlot(InventoryMenu::USE_ROW_SLOT_START + j)); + m_hotbarIcon[j]->SetUserIndex(m_hotbarIcon[j]->m_hObj, m_iPad ); + + if(j == selected) + { + m_hotbarIcon[j]->SetEnable(FALSE); //Makes the selected overlay display + } + else + { + m_hotbarIcon[j]->SetEnable(TRUE); //Hides the selected overlay display + } + + m_hotbarIcon[j]->SetAlpha( m_hotbarIcon[j]->m_hObj, opacity ); + } + + // Update xp progress + if (pMinecraft->localgameModes[m_iPad]->canHurtPlayer()) + { + int xpNeededForNextLevel = pMinecraft->localplayers[m_iPad]->getXpNeededForNextLevel(); + int progress = (int)(pMinecraft->localplayers[m_iPad]->experienceProgress *xpNeededForNextLevel); + + m_ExperienceProgress.SetShow(TRUE); + m_ExperienceProgress.SetRange(0,xpNeededForNextLevel); + m_ExperienceProgress.SetValue(progress); + } + else + { + m_ExperienceProgress.SetShow(FALSE); + } + + // Update xp level + if (pMinecraft->localgameModes[m_iPad]->hasExperience() && pMinecraft->localplayers[m_iPad]->experienceLevel > 0) + { + m_xpLevel.SetShow(TRUE); + + wchar_t formatted[10]; + swprintf(formatted, 10, L"%d",pMinecraft->localplayers[m_iPad]->experienceLevel); + + m_xpLevel.SetText(formatted); + } + else + { + m_xpLevel.SetShow(FALSE); + } + + if (pMinecraft->localgameModes[m_iPad]->canHurtPlayer()) + { + m_random.setSeed(m_tickCount * 312871); + + // Update health + int heartOffsetIndex = -1; + if (pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::regeneration)) + { + heartOffsetIndex = m_tickCount % 25; + } + + bool blink = pMinecraft->localplayers[m_iPad]->invulnerableTime / 3 % 2 == 1; + if (pMinecraft->localplayers[m_iPad]->invulnerableTime < 10) blink = false; + int iHealth = pMinecraft->localplayers[m_iPad]->getHealth(); + int iLastHealth = pMinecraft->localplayers[m_iPad]->lastHealth; + bool bHasPoison = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::poison); + for (int icon = 0; icon < Player::MAX_HEALTH / 2; icon++) + { + if(blink) + { + if (icon * 2 + 1 < iLastHealth || icon * 2 + 1 < iHealth) + { + // Full + if(bHasPoison) + { + m_healthIcon[icon].PlayVisualRange(L"FullPoisonFlash",NULL,L"FullPoisonFlash"); + } + else + { + m_healthIcon[icon].PlayVisualRange(L"FullFlash",NULL,L"FullFlash"); + } + } + else if (icon * 2 + 1 == iLastHealth || icon * 2 + 1 == iHealth) + { + // Half + if(bHasPoison) + { + m_healthIcon[icon].PlayVisualRange(L"HalfPoisonFlash",NULL,L"HalfPoisonFlash"); + } + else + { + m_healthIcon[icon].PlayVisualRange(L"HalfFlash",NULL,L"HalfFlash"); + } + } + else + { + // Empty + m_healthIcon[icon].PlayVisualRange(L"NormalFlash",NULL,L"NormalFlash"); + } + } + else + { + if (icon * 2 + 1 < iHealth) + { + // Full + if(bHasPoison) + { + m_healthIcon[icon].PlayVisualRange(L"FullPoison",NULL,L"FullPoison"); + } + else + { + m_healthIcon[icon].PlayVisualRange(L"Full",NULL,L"Full"); + } + } + else if (icon * 2 + 1 == iHealth) + { + // Half + if(bHasPoison) + { + m_healthIcon[icon].PlayVisualRange(L"HalfPoison",NULL,L"HalfPoison"); + } + else + { + m_healthIcon[icon].PlayVisualRange(L"Half",NULL,L"Half"); + } + } + else + { + // Empty + m_healthIcon[icon].PlayVisualRange(L"Normal",NULL,L"Normal"); + } + } + + float yo = 0; + if (iHealth <= 4) + { + yo = (float)m_random.nextInt(2) * (iGuiScale+1); + } + if (icon == heartOffsetIndex) + { + yo = -2.0f * (iGuiScale+1); + } + + D3DXVECTOR3 pos; + m_healthIcon[icon].GetPosition(&pos); + // TODO - 4J Stu - This should be scaled based on gui scale and overall size (ie full, split or 480) + pos.y = yo; + m_healthIcon[icon].SetPosition(&pos); + + } + + // Update food + bool foodBlink = false; + FoodData *foodData = pMinecraft->localplayers[m_iPad]->getFoodData(); + int food = foodData->getFoodLevel(); + int oldFood = foodData->getLastFoodLevel(); + bool hasHungerEffect = pMinecraft->localplayers[m_iPad]->hasEffect(MobEffect::hunger); + int saturationLevel = pMinecraft->localplayers[m_iPad]->getFoodData()->getSaturationLevel(); + for (int icon = 0; icon < 10; icon++) + { + if(foodBlink) + { + if (icon * 2 + 1 < oldFood || icon * 2 + 1 < food) + { + // Full + if(hasHungerEffect) + { + m_foodIcon[icon].PlayVisualRange(L"FullPoisonFlash",NULL,L"FullPoisonFlash"); + } + else + { + m_foodIcon[icon].PlayVisualRange(L"FullFlash",NULL,L"FullFlash"); + } + } + else if (icon * 2 + 1 == oldFood || icon * 2 + 1 == food) + { + // Half + if(hasHungerEffect) + { + m_foodIcon[icon].PlayVisualRange(L"HalfPoisonFlash",NULL,L"HalfPoisonFlash"); + } + else + { + m_foodIcon[icon].PlayVisualRange(L"HalfFlash",NULL,L"HalfFlash"); + } + } + else + { + // Empty + m_foodIcon[icon].PlayVisualRange(L"NormalFlash",NULL,L"NormalFlash"); + } + } + else + { + if (icon * 2 + 1 < food) + { + // Full + if(hasHungerEffect) + { + m_foodIcon[icon].PlayVisualRange(L"FullPoison",NULL,L"FullPoison"); + } + else + { + m_foodIcon[icon].PlayVisualRange(L"Full",NULL,L"Full"); + } + } + else if (icon * 2 + 1 == food) + { + // Half + if(hasHungerEffect) + { + m_foodIcon[icon].PlayVisualRange(L"HalfPoison",NULL,L"HalfPoison"); + } + else + { + m_foodIcon[icon].PlayVisualRange(L"Half",NULL,L"Half"); + } + } + else + { + // Empty + if(hasHungerEffect) + { + m_foodIcon[icon].PlayVisualRange(L"NormalPoison",NULL,L"NormalPoison"); + } + else + { + m_foodIcon[icon].PlayVisualRange(L"Normal",NULL,L"Normal"); + } + } + } + + + float yo = 0; + if (saturationLevel <= 0) + { + if ((m_tickCount % (food * 3 + 1)) == 0) + { + yo = (float)(m_random.nextInt(3) - 1) * (iGuiScale+1); + } + } + + D3DXVECTOR3 pos; + m_foodIcon[icon].GetPosition(&pos); + // TODO - 4J Stu - This should be scaled based on gui scale and overall size (ie full, split or 480) + pos.y = yo; + m_foodIcon[icon].SetPosition(&pos); + } + + // Update armour + int armor = pMinecraft->localplayers[m_iPad]->getArmorValue(); + if(armor > 0) + { + m_armourGroup.SetShow(TRUE); + for (int icon = 0; icon < 10; icon++) + { + if (icon * 2 + 1 < armor) m_armourIcon[icon].PlayVisualRange(L"Full",NULL,L"Full"); + else if (icon * 2 + 1 == armor) m_armourIcon[icon].PlayVisualRange(L"Half",NULL,L"Half"); + else if (icon * 2 + 1 > armor) m_armourIcon[icon].PlayVisualRange(L"Normal",NULL,L"Normal"); + } + } + else + { + m_armourGroup.SetShow(FALSE); + } + + // Update air + if (pMinecraft->localplayers[m_iPad]->isUnderLiquid(Material::water)) + { + m_airGroup.SetShow(TRUE); + int count = (int) ceil((pMinecraft->localplayers[m_iPad]->getAirSupply() - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY); + int extra = (int) ceil((pMinecraft->localplayers[m_iPad]->getAirSupply()) * 10.0f / Player::TOTAL_AIR_SUPPLY) - count; + for (int icon = 0; icon < 10; icon++) + { + // Air bubbles + if (icon < count) + { + m_airIcon[icon].SetShow(TRUE); + m_airIcon[icon].PlayVisualRange(L"Bubble",NULL,L"Bubble"); + } + else if(icon < count + extra) + { + m_airIcon[icon].SetShow(TRUE); + m_airIcon[icon].PlayVisualRange(L"Pop",NULL,L"Pop"); + } + else m_airIcon[icon].SetShow(FALSE); + } + } + else + { + m_airGroup.SetShow(FALSE); + } + } + else + { + m_healthGroup.SetShow(FALSE); + m_foodGroup.SetShow(FALSE); + m_armourGroup.SetShow(FALSE); + m_airGroup.SetShow(FALSE); + } + return S_OK; +} + +HRESULT CXuiSceneHud::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + bool bPauseMenuDisplayed=false; + bool bInGame=(Minecraft::GetInstance()->level!=NULL); + // ignore this if we have menus up - they'll deal with it + for(int i=0;iskins->getSelected(); + // if(tPack->getDLCParentPackId()!=app.GetRequiredTexturePackID()) + // { + // app.DebugPrintf("DLC install finished, and the game is using a texture pack that isn't the required one\n"); + // } + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_HUD.h b/Minecraft.Client/Common/XUI/XUI_HUD.h new file mode 100644 index 0000000..1bf6bcb --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HUD.h @@ -0,0 +1,129 @@ +#pragma once +#include "../media/xuiscene_hud.h" +#include "XUI_CustomMessages.h" + +#define CHAT_LINES_COUNT 10 + +class CXuiSceneHud : public CXuiSceneImpl +{ +private: + Random m_random; + int m_tickCount; + +protected: + CXuiControl m_hudHolder; // Contains the HUD group to enable moving all elements together + CXuiControl m_hudGroup; // Contains all the HUD elements except crosshair, in a group that scales + CXuiControl m_hudScaleGroup; // Contains all the HUD elements except crosshair + CXuiControl m_hotbarGroup; + CXuiCtrlSlotItem *m_hotbarIcon[9]; + CXuiProgressBar m_ExperienceProgress; + CXuiControl m_healthGroup; + CXuiControl m_healthIcon[10]; + CXuiControl m_armourGroup; + CXuiControl m_armourIcon[10]; + CXuiControl m_foodGroup; + CXuiControl m_foodIcon[10]; + CXuiControl m_airGroup; + CXuiControl m_airIcon[10]; + CXuiControl m_xpLevel; + + D3DXVECTOR3 m_OriginalPosition; + int m_iPad; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_CUSTOMTICKSCENE_MESSAGE(OnCustomMessage_TickScene) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_HudHolder, m_hudHolder) + BEGIN_MAP_CHILD_CONTROLS(m_hudHolder) + MAP_CONTROL(IDC_HudGroup, m_hudGroup) + BEGIN_MAP_CHILD_CONTROLS(m_hudGroup) + MAP_CONTROL(IDC_HudScaleGroup, m_hudScaleGroup) + BEGIN_MAP_CHILD_CONTROLS(m_hudScaleGroup) + MAP_CONTROL(IDC_Hotbar, m_hotbarGroup) + BEGIN_MAP_CHILD_CONTROLS(m_hotbarGroup) + MAP_OVERRIDE(IDC_Inventory1, m_hotbarIcon[0]) + MAP_OVERRIDE(IDC_Inventory2, m_hotbarIcon[1]) + MAP_OVERRIDE(IDC_Inventory3, m_hotbarIcon[2]) + MAP_OVERRIDE(IDC_Inventory4, m_hotbarIcon[3]) + MAP_OVERRIDE(IDC_Inventory5, m_hotbarIcon[4]) + MAP_OVERRIDE(IDC_Inventory6, m_hotbarIcon[5]) + MAP_OVERRIDE(IDC_Inventory7, m_hotbarIcon[6]) + MAP_OVERRIDE(IDC_Inventory8, m_hotbarIcon[7]) + MAP_OVERRIDE(IDC_Inventory9, m_hotbarIcon[8]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_ExperienceProgress, m_ExperienceProgress) + MAP_CONTROL(IDC_Health, m_healthGroup) + BEGIN_MAP_CHILD_CONTROLS(m_healthGroup) + MAP_CONTROL(IDC_Health0, m_healthIcon[0]) + MAP_CONTROL(IDC_Health1, m_healthIcon[1]) + MAP_CONTROL(IDC_Health2, m_healthIcon[2]) + MAP_CONTROL(IDC_Health3, m_healthIcon[3]) + MAP_CONTROL(IDC_Health4, m_healthIcon[4]) + MAP_CONTROL(IDC_Health5, m_healthIcon[5]) + MAP_CONTROL(IDC_Health6, m_healthIcon[6]) + MAP_CONTROL(IDC_Health7, m_healthIcon[7]) + MAP_CONTROL(IDC_Health8, m_healthIcon[8]) + MAP_CONTROL(IDC_Health9, m_healthIcon[9]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Armour, m_armourGroup) + BEGIN_MAP_CHILD_CONTROLS(m_armourGroup) + MAP_CONTROL(IDC_Armour0, m_armourIcon[0]) + MAP_CONTROL(IDC_Armour1, m_armourIcon[1]) + MAP_CONTROL(IDC_Armour2, m_armourIcon[2]) + MAP_CONTROL(IDC_Armour3, m_armourIcon[3]) + MAP_CONTROL(IDC_Armour4, m_armourIcon[4]) + MAP_CONTROL(IDC_Armour5, m_armourIcon[5]) + MAP_CONTROL(IDC_Armour6, m_armourIcon[6]) + MAP_CONTROL(IDC_Armour7, m_armourIcon[7]) + MAP_CONTROL(IDC_Armour8, m_armourIcon[8]) + MAP_CONTROL(IDC_Armour9, m_armourIcon[9]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Food, m_foodGroup) + BEGIN_MAP_CHILD_CONTROLS(m_foodGroup) + MAP_CONTROL(IDC_Food0, m_foodIcon[0]) + MAP_CONTROL(IDC_Food1, m_foodIcon[1]) + MAP_CONTROL(IDC_Food2, m_foodIcon[2]) + MAP_CONTROL(IDC_Food3, m_foodIcon[3]) + MAP_CONTROL(IDC_Food4, m_foodIcon[4]) + MAP_CONTROL(IDC_Food5, m_foodIcon[5]) + MAP_CONTROL(IDC_Food6, m_foodIcon[6]) + MAP_CONTROL(IDC_Food7, m_foodIcon[7]) + MAP_CONTROL(IDC_Food8, m_foodIcon[8]) + MAP_CONTROL(IDC_Food9, m_foodIcon[9]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Air, m_airGroup) + BEGIN_MAP_CHILD_CONTROLS(m_airGroup) + MAP_CONTROL(IDC_Air0, m_airIcon[0]) + MAP_CONTROL(IDC_Air1, m_airIcon[1]) + MAP_CONTROL(IDC_Air2, m_airIcon[2]) + MAP_CONTROL(IDC_Air3, m_airIcon[3]) + MAP_CONTROL(IDC_Air4, m_airIcon[4]) + MAP_CONTROL(IDC_Air5, m_airIcon[5]) + MAP_CONTROL(IDC_Air6, m_airIcon[6]) + MAP_CONTROL(IDC_Air7, m_airIcon[7]) + MAP_CONTROL(IDC_Air8, m_airIcon[8]) + MAP_CONTROL(IDC_Air9, m_airIcon[9]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_XPLevel, m_xpLevel) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnCustomMessage_TickScene(); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneHud, L"CXuiSceneHud", XUI_CLASS_SCENE ) +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_HelpAndOptions.cpp b/Minecraft.Client/Common/XUI/XUI_HelpAndOptions.cpp new file mode 100644 index 0000000..9ba49e9 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpAndOptions.cpp @@ -0,0 +1,462 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\XUI\XUI_HelpAndOptions.h" + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_HelpAndOptions::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + MapChildControls(); + XuiControlSetText(m_Buttons[BUTTON_HAO_CHANGESKIN],app.GetString(IDS_CHANGE_SKIN)); + XuiControlSetText(m_Buttons[BUTTON_HAO_HOWTOPLAY],app.GetString(IDS_HOW_TO_PLAY)); + XuiControlSetText(m_Buttons[BUTTON_HAO_CONTROLS],app.GetString(IDS_CONTROLS)); + XuiControlSetText(m_Buttons[BUTTON_HAO_SETTINGS],app.GetString(IDS_SETTINGS)); + XuiControlSetText(m_Buttons[BUTTON_HAO_CREDITS],app.GetString(IDS_CREDITS)); + XuiControlSetText(m_Buttons[BUTTON_HAO_REINSTALL],app.GetString(IDS_REINSTALL_CONTENT)); + XuiControlSetText(m_Buttons[BUTTON_HAO_DEBUG],app.GetString(IDS_DEBUG_SETTINGS)); + + //if(app.GetTMSDLCInfoRead()) + { + m_Timer.SetShow(FALSE); + m_bIgnoreInput=false; + + #ifndef _FINAL_BUILD + if(!app.DebugSettingsOn()) + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(FALSE); + } + else + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(TRUE); + + } + #endif + + // 4J-PB - do not need a storage device to see this menu - just need one when you choose to re-install them + + // any content to be re-installed? + if(m_iPad==ProfileManager.GetPrimaryPad() && bNotInGame) + { + // We should show the reinstall menu + app.DebugPrintf("Reinstall Menu required...\n"); + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(TRUE); + } + else + { + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(FALSE); + } + + if(app.GetLocalPlayerCount()>1) + { + // no credits in splitscreen + D3DXVECTOR3 vec; + + m_Buttons[BUTTON_HAO_CREDITS].GetPosition(&vec); + + m_Buttons[BUTTON_HAO_CREDITS].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_CREDITS].SetShow(FALSE); + + m_Buttons[BUTTON_HAO_REINSTALL].SetPosition(&vec); + + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(FALSE); + } + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + // Display the tooltips + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + + if(!ProfileManager.IsFullVersion() )//|| ProfileManager.IsGuest(m_iPad)) + { + // disable save button + m_Buttons[BUTTON_HAO_CHANGESKIN].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_CHANGESKIN].EnableInput(FALSE); + // set the focus to the second button + + XuiElementSetUserFocus(m_Buttons[BUTTON_HAO_HOWTOPLAY].m_hObj, m_iPad); + } + } + /*else + { + m_bIgnoreInput=true; + m_Timer.SetShow(TRUE); + + // hide all the buttons + for(int i=0;ilevel==NULL); + m_Timer.SetShow(FALSE); + m_bIgnoreInput=false; + + // show all the buttons - except the debug settings and reinstall content + m_Buttons[BUTTON_HAO_CHANGESKIN].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_CHANGESKIN].SetShow(TRUE); + m_Buttons[BUTTON_HAO_HOWTOPLAY].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_HOWTOPLAY].SetShow(TRUE); + m_Buttons[BUTTON_HAO_CONTROLS].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_CONTROLS].SetShow(TRUE); + m_Buttons[BUTTON_HAO_SETTINGS].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_SETTINGS].SetShow(TRUE); + m_Buttons[BUTTON_HAO_CREDITS].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_CREDITS].SetShow(TRUE); + + + +#ifndef _FINAL_BUILD + if(!app.DebugSettingsOn()) + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(FALSE); + } + else + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(TRUE); + + } +#endif + + // 4J-PB - do not need a storage device to see this menu - just need one when you choose to re-install them + + // any content to be re-installed? + if(m_iPad==ProfileManager.GetPrimaryPad() && bNotInGame) + { + // We should show the reinstall menu + app.DebugPrintf("Reinstall Menu required...\n"); + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(TRUE); + } + else + { + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(FALSE); + } + + if(app.GetLocalPlayerCount()>1) + { + // no credits in splitscreen + D3DXVECTOR3 vec; + + m_Buttons[BUTTON_HAO_CREDITS].GetPosition(&vec); + + m_Buttons[BUTTON_HAO_CREDITS].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_CREDITS].SetShow(FALSE); + + m_Buttons[BUTTON_HAO_REINSTALL].SetPosition(&vec); + + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + m_Buttons[BUTTON_HAO_DEBUG].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_DEBUG].SetShow(FALSE); + } + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + // Display the tooltips + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + + if(!ProfileManager.IsFullVersion() )//|| ProfileManager.IsGuest(m_iPad)) + { + // disable save button + m_Buttons[BUTTON_HAO_CHANGESKIN].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_CHANGESKIN].EnableInput(FALSE); + // set the focus to the second button + + XuiElementSetUserFocus(m_Buttons[BUTTON_HAO_HOWTOPLAY].m_hObj, m_iPad); + } + else + { + XuiElementSetUserFocus(m_Buttons[BUTTON_HAO_CHANGESKIN].m_hObj, m_iPad); + } + return S_OK; +}*/ + +HRESULT CScene_HelpAndOptions::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_HelpAndOptions::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + + while((uiButtonCounterUserIndex,eUIScene_HowToPlayMenu); + break; + case BUTTON_HAO_CONTROLS: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_ControlsMenu); + break; + case BUTTON_HAO_SETTINGS: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SettingsMenu); + break; + case BUTTON_HAO_CHANGESKIN: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SkinSelectMenu); + break; + case BUTTON_HAO_CREDITS: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_Credits); + break; + + case BUTTON_HAO_REINSTALL: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_ReinstallMenu); + break; + + case BUTTON_HAO_DEBUG: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_DebugOptions); + break; + + default: + break; + } + + return S_OK; +} + +HRESULT CScene_HelpAndOptions::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // ignore a held down key to avoid skipping this scene if the user changes a setting and presses B for slightly too long + if((pInputData->dwFlags&XUI_INPUT_FLAG_REPEAT)&&(pInputData->dwKeyCode==VK_PAD_B)) + { + return S_OK; + } + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + BYTE userIndex = pInputData->UserIndex; + if( !app.IsPauseMenuDisplayed(userIndex) ) + { + // If we are not from a pause menu, then we are from the main menu + userIndex = XUSER_INDEX_ANY; + } + + app.NavigateBack(userIndex); + rfHandled = TRUE; + + break; + } + + return S_OK; +} + +HRESULT CScene_HelpAndOptions::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + + if(m_iPad==ProfileManager.GetPrimaryPad() )//&& + // StorageManager.GetSaveDeviceSelected(m_iPad) && + // !StorageManager.GetSaveDisabled() && +// ( ProfileManager.IsAwardsFlagSet(m_iPad,eAward_mine100Blocks) || +// ProfileManager.IsAwardsFlagSet(m_iPad,eAward_kill10Creepers) || +// ProfileManager.IsAwardsFlagSet(m_iPad,eAward_eatPorkChop) || +// ProfileManager.IsAwardsFlagSet(m_iPad,eAward_play100Days) || +// ProfileManager.IsAwardsFlagSet(m_iPad,eAward_arrowKillCreeper) || +// ProfileManager.IsAwardsFlagSet(m_iPad,eAward_socialPost)) ) + { + // We should show the reinstall menu + app.DebugPrintf("Reinstall Menu required...\n"); + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(TRUE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(TRUE); + } + else + { + m_Buttons[BUTTON_HAO_REINSTALL].SetEnable(FALSE); + m_Buttons[BUTTON_HAO_REINSTALL].SetShow(FALSE); + } + + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + if(app.GetLocalPlayerCount()>1) + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + + +// int CScene_HelpAndOptions::ResetDefaultsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +// { +// CScene_HelpAndOptions* pClass = (CScene_HelpAndOptions*)pParam; +// +// // results switched for this dialog +// if(result==C4JStorage::EMessage_ResultDecline) +// { +// app.SetDefaultOptions(ProfileManager.GetDashboardProfileSettings(pClass->m_iPad),pClass->m_iPad); +// // if the profile data has been changed, then force a profile write +// // It seems we're allowed to break the 5 minute rule if it's the result of a user action +// app.CheckGameSettingsChanged(true,iPad); +// } +// return 0; +// } + +HRESULT CScene_HelpAndOptions::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-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + XUIRect xuiRect; + HXUIOBJ visual=NULL; + HXUIOBJ text; + float fMaxTextLen=0.0f; + float fTextVisualLen; + float fMaxButton; + float fWidth,fHeight; + + HRESULT hr=XuiControlGetVisual(m_Buttons[0].m_hObj,&visual); + hr=XuiElementGetChildById(visual,L"text_Label",&text); + hr=XuiElementGetBounds(text,&fTextVisualLen,&fHeight); + m_Buttons[0].GetBounds(&fMaxButton,&fHeight); + + + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + } + + if(fTextVisualLen +#include "..\XUI\XUI_HelpControls.h" +#include "XUI_Ctrl_4JList.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\LevelData.h" +#include "..\..\MultiplayerLocalPlayer.h" + +#define ALIGN_START 0 +#define ALIGN_END 1 +typedef struct +{ + WCHAR wchName[20]; + int iAlign; + D3DXVECTOR3 vPos; +} +CONTROLDETAILS; + +LPCWSTR CScene_Controls::m_LayoutNameA[3]= +{ + L"1", + L"2", + L"3", +}; +CONTROLDETAILS controlDetailsA[MAX_CONTROLS]= +{ + { + L"FigA",ALIGN_END // _360_JOY_BUTTON_A + }, + { + L"FigB",ALIGN_END // _360_JOY_BUTTON_B + }, + { + L"FigX",ALIGN_END // _360_JOY_BUTTON_X + }, + { + L"FigY",ALIGN_END // _360_JOY_BUTTON_Y + }, + { + L"FigStart",ALIGN_END // _360_JOY_BUTTON_START + }, + { + L"FigBack",ALIGN_START // _360_JOY_BUTTON_BACK + }, + { + L"FigRB",ALIGN_END // _360_JOY_BUTTON_RB + }, + { + L"FigLB",ALIGN_START // _360_JOY_BUTTON_LB + }, + { + L"FigRStickButton",ALIGN_END // _360_JOY_BUTTON_RTHUMB + }, + { + L"FigLStickButton",ALIGN_START // _360_JOY_BUTTON_LTHUMB + }, + // Move + { + L"FigRStick",ALIGN_END // _360_JOY_BUTTON_RSTICK_RIGHT + }, + // Look + { + L"FigLStick",ALIGN_START // _360_JOY_BUTTON_LSTICK_RIGHT + }, + { + L"FigRT",ALIGN_END // RT + }, + { + L"FigLT",ALIGN_START // LT + }, + { + L"FigDpadR",ALIGN_START // DpadR + }, + { + L"FigDpadL",ALIGN_START // DpadL + }, + { + L"FigDpad",ALIGN_START // DpadL + }, + +}; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Controls::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + XuiControlSetText(m_SouthPaw,app.GetString(IDS_SOUTHPAW)); + XuiControlSetText(m_InvertLook,app.GetString(IDS_INVERT_LOOK)); + + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bSplitscreen=(app.GetLocalPlayerCount()>1); + m_iCurrentTextIndex=0; + m_bCreativeMode = !bNotInGame && Minecraft::GetInstance()->localplayers[m_iPad] && Minecraft::GetInstance()->localplayers[m_iPad]->abilities.mayfly; + + if(!bNotInGame) + { + // disable the build ver display + m_BuildVer.SetShow(FALSE); + } + + if(bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,32.0f); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + m_iSchemeTextA[0]=IDS_CONTROLS_SCHEME0; + m_iSchemeTextA[1]=IDS_CONTROLS_SCHEME1; + m_iSchemeTextA[2]=IDS_CONTROLS_SCHEME2; + + // get the figures + HXUIOBJ hObj; + HRESULT hr; + + // turn off all the figures to start with + for(int i=0;iAddData(ListInfo[i]); + } + + m_bIgnoreNotifies=true; + m_bIgnoreNotifies=false; + int iSelected=app.GetGameSettings(m_iPad,eGameSetting_ControlScheme); + XuiControlSetText(m_SchemeList,app.GetString(IDS_CONTROLS_LAYOUT)); + hr=m_pLayoutList->SetCurSelVisible(iSelected); + m_iCurrentNavigatedControlsLayout=iSelected; + + LPWSTR layoutString = new wchar_t[ 128 ]; + swprintf( layoutString, 128, L"%ls : %ls", app.GetString( IDS_CURRENT_LAYOUT ),app.GetString(m_iSchemeTextA[iSelected])); + XuiControlSetText(m_CurrentLayout,layoutString); + + //PositionAllText(m_iPad); + + swprintf( layoutString, 128, L"%ls%ls", VER_PRODUCTVERSION_STR_W,app.DLCInstallProcessCompleted()?L"_DLC":L" "); + + m_BuildVer.SetText(layoutString); + delete [] layoutString; + + // Set check box initial states. + BOOL bInvertLook = app.GetGameSettings(m_iPad,eGameSetting_ControlInvertLook); + m_InvertLook.SetCheck( bInvertLook ); + + BOOL bSouthPaw = app.GetGameSettings(m_iPad,eGameSetting_ControlSouthPaw); + m_SouthPaw.SetCheck( bSouthPaw ); + + return S_OK; +} + + + +HRESULT CScene_Controls::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) + { + int iSelected=app.GetGameSettings(m_iPad,eGameSetting_ControlScheme); + // and update the highlight on the current selection + m_pLayoutList->SetBorder(iSelected,TRUE); + + PositionAllText(m_iPad); + } + + return S_OK; +} + +void CScene_Controls::PositionAllText(int iPad) +{ + // turn off all the figures to start with + for(int i=0;iUserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + + app.CheckGameSettingsChanged(true,pInputData->UserIndex); + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +void CScene_Controls::PositionText(int iPad,int iTextID, unsigned char ucAction) +{ + // position the text depending on the control id + //int iTextWidth; + XUIRect xuiRect; + LPCWSTR pwchText; + D3DXVECTOR3 vpos,vScale; + HXUIOBJ hFigGroup; + unsigned int uiVal=InputManager.GetGameJoypadMaps(m_iCurrentNavigatedControlsLayout,ucAction); + + // get the visual of the fig group and use any scaling in it to adjust the text positions + XuiElementGetChildById(m_hObj,L"FigGroup",&hFigGroup); + XuiElementGetScale(hFigGroup,&vScale); + + // check the width of the control with the text set in it + if(m_TextPresenterA[m_iCurrentTextIndex]==NULL) + { + HXUIOBJ hObj=NULL; + HRESULT hr=XuiControlGetVisual(m_TextA[m_iCurrentTextIndex].m_hObj,&hObj); + hr=XuiElementGetChildById(hObj,L"Text",&m_TextPresenterA[m_iCurrentTextIndex]); + } + + pwchText=app.GetString(iTextID); + HRESULT hr=XuiTextPresenterMeasureText(m_TextPresenterA[m_iCurrentTextIndex], pwchText, &xuiRect); + m_TextA[m_iCurrentTextIndex].SetBounds(xuiRect.right,xuiRect.bottom); + + int iControlDetailsIndex=0; + switch(uiVal) + { + case _360_JOY_BUTTON_A: + iControlDetailsIndex=0; + break; + case _360_JOY_BUTTON_B: + iControlDetailsIndex=1; + break; + case _360_JOY_BUTTON_X: + iControlDetailsIndex=2; + break; + case _360_JOY_BUTTON_Y: + iControlDetailsIndex=3; + break; + case _360_JOY_BUTTON_START: + iControlDetailsIndex=4; + break; + case _360_JOY_BUTTON_BACK: + iControlDetailsIndex=5; + break; + case _360_JOY_BUTTON_RB: + iControlDetailsIndex=6; + break; + case _360_JOY_BUTTON_LB: + iControlDetailsIndex=7; + break; + case _360_JOY_BUTTON_RTHUMB: + iControlDetailsIndex=8; + break; + case _360_JOY_BUTTON_LTHUMB: + iControlDetailsIndex=9; + break; + // Look + case _360_JOY_BUTTON_RSTICK_RIGHT: + iControlDetailsIndex=10; + break; + // Move + case _360_JOY_BUTTON_LSTICK_RIGHT: + iControlDetailsIndex=11; + break; + case _360_JOY_BUTTON_RT: + iControlDetailsIndex=12; + break; + // Move + case _360_JOY_BUTTON_LT: + iControlDetailsIndex=13; + break; + case _360_JOY_BUTTON_DPAD_RIGHT: + iControlDetailsIndex=14; + break; + case _360_JOY_BUTTON_DPAD_LEFT: + iControlDetailsIndex=15; + break; + } + + hr=m_FigA[iControlDetailsIndex].SetShow(TRUE); + // position the control depending on the text width + + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + // 480 mode - we need to scale the text positions + if(controlDetailsA[iControlDetailsIndex].iAlign==ALIGN_END) + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x + 5.0f)* vScale.x; + } + else + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x - 5.0f)* vScale.x - xuiRect.right; + } + + vpos.y=(controlDetailsA[iControlDetailsIndex].vPos.y * vScale.y) - (xuiRect.bottom/2.0f); + } + else + { + if(controlDetailsA[iControlDetailsIndex].iAlign==ALIGN_END) + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x + 5.0f); + } + else + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x - 5.0f) - xuiRect.right; + } + + vpos.y=(controlDetailsA[iControlDetailsIndex].vPos.y ) - (xuiRect.bottom/2.0f); + } + + vpos.x=floor(vpos.x); + vpos.y=floor(vpos.y); + vpos.z=0.0f; + + m_TextA[m_iCurrentTextIndex].SetPosition(&vpos); + m_TextA[m_iCurrentTextIndex].SetText(pwchText); + m_TextA[m_iCurrentTextIndex].SetShow(TRUE); + m_iCurrentTextIndex++; +} + +void CScene_Controls::PositionTextDirect(int iPad,int iTextID, int iControlDetailsIndex, bool bShow) +{ + // position the text depending on the control id + //int iTextWidth; + XUIRect xuiRect; + LPCWSTR pwchText; + D3DXVECTOR3 vpos,vScale; + HXUIOBJ hFigGroup; + + if(bShow==false) + { + m_TextA[m_iCurrentTextIndex++].SetShow(FALSE); + return; + } + // get the visual of the fig group and use any scaling in it to adjust the text positions + XuiElementGetChildById(m_hObj,L"FigGroup",&hFigGroup); + XuiElementGetScale(hFigGroup,&vScale); + + // check the width of the control with the text set in it + if(m_TextPresenterA[m_iCurrentTextIndex]==NULL) + { + HXUIOBJ hObj=NULL; + HRESULT hr=XuiControlGetVisual(m_TextA[m_iCurrentTextIndex].m_hObj,&hObj); + hr=XuiElementGetChildById(hObj,L"Text",&m_TextPresenterA[m_iCurrentTextIndex]); + } + + pwchText=app.GetString(iTextID); + HRESULT hr=XuiTextPresenterMeasureText(m_TextPresenterA[m_iCurrentTextIndex], pwchText, &xuiRect); + m_TextA[m_iCurrentTextIndex].SetBounds(xuiRect.right,xuiRect.bottom); + + + + hr=m_FigA[iControlDetailsIndex].SetShow(TRUE); + // position the control depending on the text width + + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + // 480 mode - we need to scale the text positions + if(controlDetailsA[iControlDetailsIndex].iAlign==ALIGN_END) + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x + 5.0f)* vScale.x; + } + else + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x - 5.0f)* vScale.x - xuiRect.right; + } + + vpos.y=(controlDetailsA[iControlDetailsIndex].vPos.y * vScale.y) - (xuiRect.bottom/2.0f); + } + else + { + if(controlDetailsA[iControlDetailsIndex].iAlign==ALIGN_END) + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x + 5.0f); + } + else + { + vpos.x=(controlDetailsA[iControlDetailsIndex].vPos.x - 5.0f) - xuiRect.right; + } + + vpos.y=(controlDetailsA[iControlDetailsIndex].vPos.y ) - (xuiRect.bottom/2.0f); + } + + vpos.x=floor(vpos.x); + vpos.y=floor(vpos.y); + vpos.z=0.0f; + + m_TextA[m_iCurrentTextIndex].SetPosition(&vpos); + m_TextA[m_iCurrentTextIndex].SetText(pwchText); + m_TextA[m_iCurrentTextIndex].SetShow(TRUE); + m_iCurrentTextIndex++; +} + +HRESULT CScene_Controls::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if( ( !m_bIgnoreNotifies )&&( hObjSource==m_pLayoutList->m_hObj ) ) + { + m_iCurrentNavigatedControlsLayout=pNotifySelChangedData->iItem; + PositionAllText(m_iPad); + } + if(pNotifySelChangedData->iOldItem!=-1 && hObjSource==m_SchemeList ) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + return S_OK; +} + + + + +HRESULT CScene_Controls::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + // Handle check box changes. + if ( hObjPressed == m_InvertLook.m_hObj ) + { + BOOL bIsChecked = m_InvertLook.IsChecked(); + app.SetGameSettings(m_iPad,eGameSetting_ControlInvertLook,(unsigned char)( bIsChecked ) ); + } + else if ( hObjPressed == m_SouthPaw.m_hObj ) + { + BOOL bIsChecked = m_SouthPaw.IsChecked(); + app.SetGameSettings(m_iPad,eGameSetting_ControlSouthPaw,(unsigned char)( bIsChecked ) ); + PositionAllText(m_iPad); + } + else if( hObjPressed == m_SchemeList) + { + // check what's been selected + app.SetGameSettings(m_iPad,eGameSetting_ControlScheme,(unsigned char)m_SchemeList.GetCurSel()); + LPWSTR layoutString = new wchar_t[ 128 ]; + swprintf( layoutString, 128, L"%ls : %ls", app.GetString( IDS_CURRENT_LAYOUT ),app.GetString(m_iSchemeTextA[m_SchemeList.GetCurSel()]) ); + + XuiControlSetText(m_CurrentLayout,layoutString); + delete [] layoutString; + // and update the highlight on the current selection + for(int i=0;iGetItemCount();i++) + { + m_pLayoutList->SetBorder(i,FALSE); + } + m_pLayoutList->SetBorder(m_SchemeList.GetCurSel(),TRUE); + } + return S_OK; +} + +HRESULT CScene_Controls::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining,32.0f); +} diff --git a/Minecraft.Client/Common/XUI/XUI_HelpControls.h b/Minecraft.Client/Common/XUI/XUI_HelpControls.h new file mode 100644 index 0000000..4d14953 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpControls.h @@ -0,0 +1,115 @@ +#pragma once + +#include "../media/xuiscene_controls.h" +#include "XUI_CustomMessages.h" + +class CXuiCtrl4JList; + + + +#define MAX_CONTROLS 17 +class CScene_Controls : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiControl m_TextA[MAX_CONTROLS]; + HXUIOBJ m_TextPresenterA[MAX_CONTROLS]; + CXuiElement m_FigA[MAX_CONTROLS]; + CXuiList m_SchemeList; + CXuiElement m_Group; + CXuiControl m_BuildVer; + CXuiControl m_CurrentLayout; + CXuiCheckbox m_InvertLook; + CXuiCheckbox m_SouthPaw; + static LPCWSTR m_LayoutNameA[3]; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_SchemeList, m_SchemeList) + MAP_CONTROL(IDC_CurrentLayout, m_CurrentLayout) + + MAP_CONTROL(IDC_XuiBuildVer, m_BuildVer) + MAP_CONTROL(IDC_XuiLabel1, m_TextA[0]) + MAP_CONTROL(IDC_XuiLabel2, m_TextA[1]) + MAP_CONTROL(IDC_XuiLabel3, m_TextA[2]) + MAP_CONTROL(IDC_XuiLabel4, m_TextA[3]) + MAP_CONTROL(IDC_XuiLabel5, m_TextA[4]) + MAP_CONTROL(IDC_XuiLabel6, m_TextA[5]) + MAP_CONTROL(IDC_XuiLabel7, m_TextA[6]) + MAP_CONTROL(IDC_XuiLabel8, m_TextA[7]) + MAP_CONTROL(IDC_XuiLabel9, m_TextA[8]) + MAP_CONTROL(IDC_XuiLabel10, m_TextA[9]) + MAP_CONTROL(IDC_XuiLabel11, m_TextA[10]) + MAP_CONTROL(IDC_XuiLabel12, m_TextA[11]) + MAP_CONTROL(IDC_XuiLabel13, m_TextA[12]) + MAP_CONTROL(IDC_XuiLabel14, m_TextA[13]) + MAP_CONTROL(IDC_XuiLabel15, m_TextA[14]) + MAP_CONTROL(IDC_XuiLabel16, m_TextA[15]) + MAP_CONTROL(IDC_XuiLabel17, m_TextA[16]) + MAP_CONTROL(IDC_FigGroup, m_Group) + MAP_CONTROL(IDC_InvertLook, m_InvertLook) + MAP_CONTROL(IDC_SouthPaw, m_SouthPaw) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_A, m_FigA[0]) + MAP_CONTROL(IDC_B, m_FigA[1]) + MAP_CONTROL(IDC_X, m_FigA[2]) + MAP_CONTROL(IDC_Y, m_FigA[3]) + MAP_CONTROL(IDC_Start, m_FigA[4]) + MAP_CONTROL(IDC_Back, m_FigA[5]) + MAP_CONTROL(IDC_RB, m_FigA[6]) + MAP_CONTROL(IDC_LB, m_FigA[7]) + MAP_CONTROL(IDC_RStickButton, m_FigA[8]) + MAP_CONTROL(IDC_LStickButton, m_FigA[9]) + MAP_CONTROL(IDC_RStick, m_FigA[10]) + MAP_CONTROL(IDC_LStick, m_FigA[11]) + MAP_CONTROL(IDC_RT, m_FigA[12]) + MAP_CONTROL(IDC_LT, m_FigA[13]) + MAP_CONTROL(IDC_DpadR, m_FigA[14]) + MAP_CONTROL(IDC_DpadL, m_FigA[15]) + MAP_CONTROL(IDC_Dpad, m_FigA[16]) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + + + void PositionText(int iPad,int iTextID, unsigned char ucAction); + void PositionTextDirect(int iPad,int iTextID, int iControlDetailsIndex, bool bShow); + void PositionAllText(int iPad); + + + int m_iCurrentTextIndex; + int m_iCurrentNavigatedControlsLayout; + int m_iSchemeTextA[3]; + int m_nItems; + int m_iPad; + CXuiCtrl4JList *m_pLayoutList; + bool m_bIgnoreNotifies; + D3DXVECTOR3 m_OriginalPosition; + bool m_bCreativeMode; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Controls, L"CScene_Controls", XUI_CLASS_SCENE ) + + + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_HelpCredits.cpp b/Minecraft.Client/Common/XUI/XUI_HelpCredits.cpp new file mode 100644 index 0000000..4a6ce55 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpCredits.cpp @@ -0,0 +1,688 @@ +#include "stdafx.h" +#include +#include "XUI_HelpCredits.h" + +SCreditTextItemDef CScene_Credits::gs_aCreditDefs[MAX_CREDIT_STRINGS] = +{ + { L"MOJANG", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_ORIGINALDESIGN, NO_TRANSLATED_STRING,eLargeText }, + { L"Markus Persson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_PMPROD, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Kaplan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_RESTOFMOJANG, NO_TRANSLATED_STRING,eMediumText }, + { L"%s", IDS_CREDITS_LEADPC, NO_TRANSLATED_STRING,eLargeText }, + { L"Jens Bergensten", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_JON_KAGSTROM, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_CEO, NO_TRANSLATED_STRING,eLargeText }, + { L"Carl Manneh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DOF, NO_TRANSLATED_STRING,eLargeText }, + { L"Lydia Winters", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_WCW, NO_TRANSLATED_STRING,eLargeText }, + { L"Karin Severinsson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_CUSTOMERSUPPORT, NO_TRANSLATED_STRING,eLargeText }, + { L"Marc Watson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_DESPROG, NO_TRANSLATED_STRING,eLargeText }, + { L"Aron Nieminen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_CHIEFARCHITECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Frisk", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_CODENINJA, NO_TRANSLATED_STRING,eLargeText }, + { L"%s", IDS_CREDITS_TOBIAS_MOLLSTAM, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_OFFICEDJ, NO_TRANSLATED_STRING,eLargeText }, + { L"Kristoffer Jelbring", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DEVELOPER, NO_TRANSLATED_STRING,eLargeText }, + { L"Leonard Axelsson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_BULLYCOORD, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakob Porser", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ARTDEVELOPER, NO_TRANSLATED_STRING,eLargeText }, + { L"Junkboy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_EXPLODANIM, NO_TRANSLATED_STRING,eLargeText }, + { L"Mattis Grahm", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_CONCEPTART, NO_TRANSLATED_STRING,eLargeText }, + { L"Henrik Petterson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_CRUNCHER, NO_TRANSLATED_STRING,eLargeText }, + { L"Patrick Geuder", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_MUSICANDSOUNDS, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel Rosenfeld (C418)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"4J Studios", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"%s", IDS_CREDITS_PROGRAMMING, NO_TRANSLATED_STRING,eLargeText }, + { L"Paddy Burns", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Richard Reavy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Stuart Ross", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"James Vaughan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mark Hughes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Thomas Kronberg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ian le Bruce", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Andy West", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gordon McLean", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ART, NO_TRANSLATED_STRING,eLargeText }, + { L"David Keningale", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Pat McGovern", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alan Redmond", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Julian Laing", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Caitlin Goodale", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Sutherland", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Reeves", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kate Wright", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Hansen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kate Flavell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Donald Robertson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jamie Keddie", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Thomas Naylor", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brian Lindsay", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hannah Watts", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rebecca O'Neil", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_QA, NO_TRANSLATED_STRING,eLargeText }, + { L"Steven Gary Woodward", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Richard Black", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"George Vaughan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris van der Kuyl", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Roni Percy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Anne Clarke", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Anthony Kent", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Microsoft Studios", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + + { L"Xbox LIVE Arcade Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_LEADPRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Roger Carpenter", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Stuart Platt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Riccardo Lenzi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_LEADTESTER, NO_TRANSLATED_STRING,eLargeText }, + { L"Bill Brown (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brandon McCurry (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hakim Ronaque, Joe Dunavant", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paul Loynd, Jeffery Stephens", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rial Lerum (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DESIGNTEAM, NO_TRANSLATED_STRING,eLargeText }, + { L"Craig Leigh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_DEVELOPMENTTEAM, NO_TRANSLATED_STRING,eLargeText }, + { L"Scott Guest", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jeff \"Dextor\" Blazier", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yukie Yamaguchi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Hewitt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_RELEASEMANAGEMENT, NO_TRANSLATED_STRING,eLargeText }, + { L"Josh Mulanax", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shogo Ishii (TekSystems)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Keenan (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Joshua Bullard (TekSystems)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_EXECPRODUCER, NO_TRANSLATED_STRING,eLargeText }, + { L"Mark Coates", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Avi Ben-Menahem", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Earnest Yuen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_XBLADIRECTOR, NO_TRANSLATED_STRING,eLargeText }, + { L"Ted Woolsey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_BIZDEV, NO_TRANSLATED_STRING,eLargeText }, + { L"Cherie Lutz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Peter Zetterberg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PORTFOLIODIRECTOR, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris Charla", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PRODUCTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Daniel McConnell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_MARKETING, NO_TRANSLATED_STRING,eLargeText }, + { L"Brandon Wells", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Wolf", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Dongelmans", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_COMMUNITYMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Alex Hebert", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_REDMONDLOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Zeb Wedell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gabriella Mittiga (Pactera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Fielding (Global Studio Consulting)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yong Zhao (Hisoft Envisage Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shogo Ishii (Insight Global)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_EUROPELOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Gerard Dunne", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ricardo Cordoba", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Magali Lucchini", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Malika Kherfi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lizzy Untermann", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ian Walsh", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alfonsina Mossello (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marika Mauri (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nobuhiro Izumisawa (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Sebastien Faucon (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jose Manuel Martinez (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Montse Garcia (Keywords International Ltd)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ASIALOC, NO_TRANSLATED_STRING,eLargeText }, + { L"Takashi Sasaki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Changseon Ha", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shinya Muto (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Hiroshi Hosoda (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Natsuko Kudo (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yong-Hong Park (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Yuko Yoshida (Zip Global Corporation)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_USERRESEARCH, NO_TRANSLATED_STRING,eLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"User Research Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Tim Nichols", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"User Research Engineer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Michael Medlock", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kristie Fisher", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_MGSCENTRAL, NO_TRANSLATED_STRING,eLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"Test Team Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Dan Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_MILESTONEACCEPT, NO_TRANSLATED_STRING,eLargeText }, + { L"Justin Davis (VMC)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Microsoft Studios Sentient Development Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Ellery Charlson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Frank Klier", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Ronald", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Cullen Waters", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Steve Jackson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Barath Vasudevan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Derek Mantey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Henry Sterchi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Scott Fintel", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Soren Hannibal Nielsen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Meetali Goel (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Uladzimir Sadouski (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + { L"Brianna Witherspoon (Nytec Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jim Pekola (Xtreme Consulting Group Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Henry", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Matt Golz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Gaffney (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jared Barnhill (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Laura Hawkins", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"2nd Cavalry", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"GTO Bug Bash Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Oliver Miyashita", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin Salcedo", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nick Bodenham", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Giggins", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ben Board", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Peter Choi", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Andy Su (CompuCom Systems Inc.)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Boker ", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Josh Bliggenstorfer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paul Amer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Louise Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karin Behland (Aquent LLC)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Bruno", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Phil Spencer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Smith", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Christi Davisson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacob Farley (Aditi)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chad Stringer (Collabera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rick Rispoli (Collabera)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Test by Experis", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"%s", IDS_CREDITS_TESTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Matt Brown", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gavin Kennedy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SRTESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Lloyd Bell", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tim Attuquayefio", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Byron R. Monzon", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marta Alombro", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SDET, NO_TRANSLATED_STRING,eLargeText }, + { L"Valeriy Novytskyy", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PROJECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Allyson Burk", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Scott", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"John Shearer", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_ADDITIONALSTE, NO_TRANSLATED_STRING,eLargeText }, + { L"Chris Merritt", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kimberlee Lyles", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Eric Ranz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Russ Allen", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTASSOCIATES, NO_TRANSLATED_STRING,eLargeText }, + { L"Michael Arvat", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Josh Breese", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"April Culberson", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jason Fox", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Clayton K. Hopper", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Matthew Howells", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alan Hume", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacob Martin", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kevin Lourigan", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Lovemark", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_RISE_LUGO, NO_TRANSLATED_STRING,eSmallText }, + { L"Ryan Naegeli", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Isaac Price", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Masha Reutovski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brad Shockey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jonathan Tote", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marc Williams", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Gillian Williams", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jeffrey Woito", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tyler Young", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jae Yslas", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Amanda Swalling", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ben Dienes", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Kent", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dustin Lukas", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Emily Lovering", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nick Fowler", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + // EVEN MORE CREDITS + { L"Test by Lionbridge", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"%s", IDS_CREDITS_TESTMANAGER, NO_TRANSLATED_STRING,eLargeText }, + { L"Blazej Zawadzki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Hickey", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_TESTLEAD, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakub Garwacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Lahti", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mariusz Gelnicki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karol Falak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Watroba", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_PROJECT, NO_TRANSLATED_STRING,eLargeText }, + { L"Artur Grochowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Grzegorz Kohorewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Derewonko", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Celej", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Senior Test Engineers", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Jakub Rybacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mateusz Szymanski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Szczytowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Rafal Rawski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"%s", IDS_CREDITS_TESTASSOCIATES, NO_TRANSLATED_STRING,eLargeText }, + { L"Adrian Klepacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Kala", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Sykula", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Bartlomiej Kmita", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Malinowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jan Prejs", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Maciej Urlo", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Maciej Wygoda", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Piasecki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Piotrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marek Latacz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Biernat", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Krupinski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Warchal", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Wascinski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Zbrzezniak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Milosz Maciejewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Przemyslaw Malinowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Dabrowicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Trzebiatowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Adam Bogucki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Aleksander Pietraszak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Arkadiusz Szczytowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Blazej Kohorewicz", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Damian Mielnik", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dariusz Nowakowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Dominik Rzeznicki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jacek Piotrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Rybacki", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jakub Wozniakowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jaroslaw Radzio", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Dabrowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Kamil Kaczor", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Karolina Szymanska", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Konrad Mady", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Krzysztof Galazka", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ludwik Miszta", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Lukasz Kwiatkowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Marcin Krzysiak", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Mateusz Szymanski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Maslany", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michal Nyszka", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Norbert Jankowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Piotr Daszewski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Radoslaw Kozlowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Tomasz Kalowski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"%s", IDS_CREDITS_SPECIALTHANKS, NO_TRANSLATED_STRING,eLargeText }, + { L"Adam Keating", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jerzy Tyminski", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Paulina Sliwinska", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Sean Kellogg", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + + { L"Test by Shield", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eExtraLargeText }, + { L"", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, // extra blank line + { L"GTO Shared Service Test Manager", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Natahri Felton", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shield Test Lead", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Matt Giddings", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Shield IT Support", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"David Grant (Compucom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Primary Team", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eLargeText }, + { L"Alex Chen (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Alex Hunte (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Brian Boye (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Bridgette Cummins (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Chris Carleson (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Christopher Hermey (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"David Hendrickson (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Ioana Preda (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Jessica Jenkins (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Johnathan Ochs (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Michael Upham (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nicholas Johansson (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Nicholas Starner (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Torr Vickers (Volt)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + { L"Victoria Bruder (CompuCom Systems Inc)", NO_TRANSLATED_STRING, NO_TRANSLATED_STRING,eSmallText }, + +}; + +// Table tells us how many text elements of each type are available in the scene. +static const int gs_aNumTextElements[ eNumTextTypes ] = +{ + 3, // CScene_Credits::eExtraLargeText + 5, // CScene_Credits::eLargeText + 5, // CScene_Credits::eMediumText + 15 // CScene_Credits::eSmallText +}; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Credits::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + int iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + // if we're not in the game, we need to use basescene 0 + if(Minecraft::GetInstance()->level==NULL) + { + iPad=0; + } + + ui.SetTooltips( iPad, -1, IDS_TOOLTIPS_BACK); + + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + CREDITS_SCREEN_MIN_Y = 200.0f/1.5f; // Y pos at which credits are removed from top of screen. + CREDITS_SCREEN_MAX_Y = 630.0f/1.5f; // Y pos at which credits appear at bottom of screen. + CREDITS_FADE_HEIGHT = 100.0f/1.5f; // Height over which credits fade in or fade out. + gs_aLineSpace[eExtraLargeText]=53.0f; + gs_aLineSpace[eLargeText]=46.0f; + gs_aLineSpace[eMediumText]=40.0f; + gs_aLineSpace[eSmallText]=21.0f; + } + else + { + CREDITS_SCREEN_MIN_Y = 200.0f; // Y pos at which credits are removed from top of screen. + CREDITS_SCREEN_MAX_Y = 630.0f; // Y pos at which credits appear at bottom of screen. + CREDITS_FADE_HEIGHT = 100.0f; // Height over which credits fade in or fade out. + gs_aLineSpace[eExtraLargeText]=80.0f; + gs_aLineSpace[eLargeText]=70.0f; + gs_aLineSpace[eMediumText]=60.0f; + gs_aLineSpace[eSmallText]=32.0f; + } + + + // Init text elements. + int iIDTag = 1; + LPWSTR idString = new wchar_t[ 32 ]; + + for ( int i = 0; i < eNumTextTypes; ++i ) + { + m_aTextTypes[ i ].m_iNextFreeElement = 0; + m_aTextTypes[ i ].m_iNumUsedElements = 0; + m_aTextTypes[ i ].m_iMaxElements = gs_aNumTextElements[ i ]; + m_aTextTypes[ i ].m_appTextElements = new CXuiControl* [ m_aTextTypes[ i ].m_iMaxElements ]; + + for ( int j = 0; j < m_aTextTypes[ i ].m_iMaxElements; ++j ) + { + swprintf( idString, 32, L"XuiText%d", iIDTag ); + ++iIDTag; + + HXUIOBJ text; + GetChildById( idString, &text ); + + VOID* pTextObj; + XuiObjectFromHandle( text, &pTextObj ); + m_aTextTypes[ i ].m_appTextElements[ j ] = (CXuiControl *)pTextObj; + m_aTextTypes[ i ].m_appTextElements[ j ]->SetShow( false ); + } + } + delete [] idString; + + // How many lines of text are in the credits? + + m_iNumTextDefs = MAX_CREDIT_STRINGS;//sizeof( gs_aCreditDefs ) / sizeof( SCreditTextItemDef ); + + // Are there any additional lines needed for the DLC credits? + m_iNumTextDefs+=app.GetDLCCreditsCount(); + + m_iCurrDefIndex = -1; + m_fMoveSinceLastDef = 0.0f; + m_fMoveToNextDef = 0.0f; + + // Add timer to tick credits update at 60Hz + HRESULT timerResult = SetTimer( CREDITS_TICK_TIMER_ID, ( 1000 / 60 ) ); + assert( timerResult == S_OK ); + + return S_OK; +} + +HRESULT CScene_Credits::OnDestroy() +{ + // Free up memory that we allocated. + for ( int i = 0; i < eNumTextTypes; ++i ) + { + delete [] m_aTextTypes[ i ].m_appTextElements; + } + + return S_OK; +} + +HRESULT CScene_Credits::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + + return hr; +} + +HRESULT CScene_Credits::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // ignore any joypads other than the main + BYTE bFocusUser=XuiElementGetFocusUser(pControlNavigateData->hObjSource); + // get the user from the control + /*if(!=ProfileManager.GetLockedProfile()) + { + bHandled=true; + return S_OK; + }*/ + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Credits::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + + return S_OK; +} + + + +HRESULT CScene_Credits::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // Update pointer from stick input on timer. + if ( pTimer->nId == CREDITS_TICK_TIMER_ID ) + { + D3DXVECTOR3 vTextPos; + + // Do we need to spawn a new item? + if ( ( m_iCurrDefIndex == -1 ) || ( m_fMoveSinceLastDef >= m_fMoveToNextDef ) ) + { + const SCreditTextItemDef* pDef; + + // Time to create next text item. + ++m_iCurrDefIndex; + + // Wrap back to start. + if ( m_iCurrDefIndex >= m_iNumTextDefs ) + { + m_iCurrDefIndex = 0; + } + + if(m_iCurrDefIndex >= MAX_CREDIT_STRINGS) + { + // DLC credit + pDef = app.GetDLCCredits(m_iCurrDefIndex-MAX_CREDIT_STRINGS); + } + else + { + // Get text def for this item. + pDef = &( gs_aCreditDefs[ m_iCurrDefIndex ] ); + } + + int iNewTextType = ( int )( pDef->m_eType ); + STextType* pTextType = &( m_aTextTypes[ iNewTextType ] ); + + // Are there any text elements available for this item? + if ( pTextType->m_iNumUsedElements < pTextType->m_iMaxElements ) + { + // Get the correct text element to use. + CXuiControl* pElement = pTextType->m_appTextElements[ pTextType->m_iNextFreeElement ]; + + // Set up the new text element. + if ( pDef->m_iStringID[0] == NO_TRANSLATED_STRING ) + { + pElement->SetText( pDef->m_Text ); + } + else // using additional translated string. + { + LPWSTR creditsString = new wchar_t[ 128 ]; + if(pDef->m_iStringID[1]!=NO_TRANSLATED_STRING) + { + swprintf( creditsString, 128, pDef->m_Text, app.GetString( pDef->m_iStringID[0] ), app.GetString( pDef->m_iStringID[1] ) ); + } + else + { + swprintf( creditsString, 128, pDef->m_Text, app.GetString( pDef->m_iStringID[0] ) ); + } + pElement->SetText( creditsString ); + delete [] creditsString; + } + + pElement->SetShow( true ); + pElement->SetOpacity( 0.0f ); + + pElement->GetPosition( &vTextPos ); + vTextPos.y = CREDITS_SCREEN_MAX_Y; + pElement->SetPosition( &vTextPos ); + + // Set next free element of this type. + ++( pTextType->m_iNextFreeElement ); + + if ( pTextType->m_iNextFreeElement >= pTextType->m_iMaxElements ) + { + pTextType->m_iNextFreeElement = 0; + } + + ++pTextType->m_iNumUsedElements; + + int iNextDef = m_iCurrDefIndex + 1; + if ( iNextDef == m_iNumTextDefs ) + { + // Large space before looping back to start of credits. + m_fMoveToNextDef = 400.0f; + } + else if ( iNextDef < MAX_CREDIT_STRINGS ) + { + // Determine space to next item. + m_fMoveToNextDef = gs_aLineSpace[ gs_aCreditDefs[ iNextDef ].m_eType ]; + } + else + { + // Determine space to next item. + m_fMoveToNextDef = gs_aLineSpace[app.GetDLCCredits(iNextDef-MAX_CREDIT_STRINGS)->m_eType]; + } + + m_fMoveSinceLastDef = 0.0f; + } + } + + // Scroll up every active text element. + for ( int i = 0; i < eNumTextTypes; ++i ) + { + STextType* pTextType = &( m_aTextTypes[ i ] ); + + // Process in reverse order from latest one back (so that we can easily remove oldest if it has scrolled off the top). + int iElementIndex = pTextType->m_iNextFreeElement; + + --iElementIndex; + if ( iElementIndex < 0 ) + { + iElementIndex = pTextType->m_iMaxElements - 1; + } + + // For each element that it is use + for ( int j = 0; j < pTextType->m_iNumUsedElements; ++j ) + { + // Get the actual element. + CXuiControl* pElement = pTextType->m_appTextElements[ iElementIndex ]; + + // Scroll element up. + pElement->GetPosition( &vTextPos ); + vTextPos.y -= 1.0f; + pElement->SetPosition( &vTextPos ); + + // Is it off the top? + if ( vTextPos.y < CREDITS_SCREEN_MIN_Y ) + { + // Remove it. + pElement->SetShow( false ); + --( pTextType->m_iNumUsedElements ); + } + else + { + // Set transparency to fade in at bottom or out at top. + float fOpacity = 1.0f; + if ( vTextPos.y < ( CREDITS_SCREEN_MIN_Y + CREDITS_FADE_HEIGHT ) ) + { + fOpacity = ( vTextPos.y - CREDITS_SCREEN_MIN_Y ) / CREDITS_FADE_HEIGHT; + } + else if ( vTextPos.y > ( CREDITS_SCREEN_MAX_Y - CREDITS_FADE_HEIGHT ) ) + { + fOpacity = ( CREDITS_SCREEN_MAX_Y - vTextPos.y ) / CREDITS_FADE_HEIGHT; + } + pElement->SetOpacity( fOpacity ); + } + + // Determine next element index. + --iElementIndex; + if ( iElementIndex < 0 ) + { + iElementIndex = pTextType->m_iMaxElements - 1; + } + } + } + + m_fMoveSinceLastDef += 1.0f; + + // This message has been dealt with, don't pass it on further. + bHandled = TRUE; + } + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_HelpCredits.h b/Minecraft.Client/Common/XUI/XUI_HelpCredits.h new file mode 100644 index 0000000..e973286 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpCredits.h @@ -0,0 +1,73 @@ +#pragma once + +#define CREDITS_TICK_TIMER_ID (6) // Arbitrary timer ID used to tick credits for scrolling. + +#define MAX_CREDIT_STRINGS 360 +// 213 + +#include "..\UI\UIStructs.h" + +class CScene_Credits : public CXuiSceneImpl +{ +protected: + // Control and Element wrapper objects. + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_CONTROL_NAVIGATE(OnControlNavigate) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnDestroy(); + +private: + + + struct STextType + { + // Array of pointers to text elements. + CXuiControl** m_appTextElements; + + int m_iNextFreeElement; + int m_iNumUsedElements; + int m_iMaxElements; + }; + + STextType m_aTextTypes[ eNumTextTypes ]; + + int m_iCurrDefIndex; // Index of last created text def. + float m_fMoveSinceLastDef; // How far have credits scrolled since we last created a new text item. + float m_fMoveToNextDef; // How far we need to move before starting next text item. + int m_iNumTextDefs; // Total number of text defs in the credits. + + float CREDITS_SCREEN_MIN_Y;// ( 200.0f ) // Y pos at which credits are removed from top of screen. + float CREDITS_SCREEN_MAX_Y;// ( 630.0f ) // Y pos at which credits appear at bottom of screen. + float CREDITS_FADE_HEIGHT;// ( 100.0f ) // Height over which credits fade in or fade out. + + float gs_aLineSpace[ eNumTextTypes ]; + +public: + static SCreditTextItemDef gs_aCreditDefs[MAX_CREDIT_STRINGS]; + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Credits, L"CScene_Credits", XUI_CLASS_SCENE ) + + + +}; + diff --git a/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.cpp b/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.cpp new file mode 100644 index 0000000..5c750d0 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" + +#include +#include "..\XUI\XUI_HelpHowToPlay.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +static SHowToPlayPageDef gs_aPageDefs[ eHowToPlay_NumPages ] = +{ + { eHowToPlay_WhatsNew, IDS_HOW_TO_PLAY_WHATSNEW, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_WhatsNew + { eHowToPlay_TextBasics, IDS_HOW_TO_PLAY_BASICS, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_Basics + { eHowToPlay_TextMultiplayer, IDS_HOW_TO_PLAY_MULTIPLAYER, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_Multiplayer + { eHowToPlay_TextHUD, IDS_HOW_TO_PLAY_HUD, eHowToPlay_ImageHUD, 0, 0}, // eHowToPlay_HUD + { eHowToPlay_TextCreative, IDS_HOW_TO_PLAY_CREATIVE, eHowToPlay_ImageCreative, eHowToPlay_LabelCreativeInventory, 1}, // eHowToPlay_Creative + { eHowToPlay_TextInventory, IDS_HOW_TO_PLAY_INVENTORY, eHowToPlay_ImageInventory, eHowToPlay_LabelIInventory, 1}, // eHowToPlay_Inventory + { eHowToPlay_TextSmallChest, IDS_HOW_TO_PLAY_CHEST, eHowToPlay_ImageChest, eHowToPlay_LabelSCInventory, 2}, // eHowToPlay_Chest + { eHowToPlay_TextLargeChest, IDS_HOW_TO_PLAY_LARGECHEST, eHowToPlay_ImageLargeChest, eHowToPlay_LabelLCInventory, 2}, // eHowToPlay_LargeChest + { eHowToPlay_TextEnderchest, IDS_HOW_TO_PLAY_ENDERCHEST, eHowToPlay_ImageEnderChest, 0, 0}, // eHowToPlay_EnderChest + { eHowToPlay_TextCrafting, IDS_HOW_TO_PLAY_CRAFTING, eHowToPlay_ImageInventoryCrafting, eHowToPlay_LabelCItem, 3}, // eHowToPlay_InventoryCrafting + { eHowToPlay_TextCraftTable, IDS_HOW_TO_PLAY_CRAFT_TABLE, eHowToPlay_ImageCraftingTable, eHowToPlay_LabelCTItem, 3}, // eHowToPlay_CraftTable + { eHowToPlay_TextFurnace, IDS_HOW_TO_PLAY_FURNACE, eHowToPlay_ImageFurnace, eHowToPlay_LabelFFuel, 4}, // eHowToPlay_Furnace + { eHowToPlay_TextDispenser, IDS_HOW_TO_PLAY_DISPENSER, eHowToPlay_ImageDispenser, eHowToPlay_LabelDText, 2}, // eHowToPlay_Dispenser + { eHowToPlay_TextBrewing, IDS_HOW_TO_PLAY_BREWING, eHowToPlay_ImageBrewing, eHowToPlay_LabelBBrew, 2}, // eHowToPlay_Brewing + { eHowToPlay_TextEnchantment, IDS_HOW_TO_PLAY_ENCHANTMENT, eHowToPlay_ImageEnchantment, eHowToPlay_LabelEEnchant, 2}, // eHowToPlay_Enchantment + { eHowToPlay_TextAnvil, IDS_HOW_TO_PLAY_ANVIL, eHowToPlay_ImageAnvil, eHowToPlay_LabelAnvil_Inventory, 3}, // eHowToPlay_Anvil + { eHowToPlay_TextFarmingAnimals,IDS_HOW_TO_PLAY_FARMANIMALS, eHowToPlay_ImageFarmingAnimals, 0, 0}, // eHowToPlay_Farming + { eHowToPlay_TextBreeding, IDS_HOW_TO_PLAY_BREEDANIMALS, eHowToPlay_ImageBreeding, 0, 0}, // eHowToPlay_Breeding + { eHowToPlay_TextTrading, IDS_HOW_TO_PLAY_TRADING, eHowToPlay_ImageTrading, eHowToPlay_LabelTrading_Inventory, 5}, // eHowToPlay_Trading + { eHowToPlay_TextNetherPortal, IDS_HOW_TO_PLAY_NETHERPORTAL, eHowToPlay_ImageNetherPortal, 0, 0}, // eHowToPlay_NetherPortal + { eHowToPlay_TextTheEnd, IDS_HOW_TO_PLAY_THEEND, eHowToPlay_ImageTheEnd, 0, 0}, // eHowToPlay_TheEnd + { eHowToPlay_TextSocialMedia, IDS_HOW_TO_PLAY_SOCIALMEDIA, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_SocialMedia + { eHowToPlay_TextBanList, IDS_HOW_TO_PLAY_BANLIST, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_BanList + { eHowToPlay_TextHostOptions, IDS_HOW_TO_PLAY_HOSTOPTIONS, eHowToPlay_ImageNone, 0, 0}, // eHowToPlay_HostOptions +}; + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_HowToPlay::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + // Extract pad and required page from init data. We just put the data into the pointer rather than using it as an address. + size_t uiInitData = ( size_t )( pInitData->pvInitData ); + + m_iPad = ( int )( ( short )( uiInitData & 0xFFFF ) ); + EHowToPlayPage eStartPage = ( EHowToPlayPage )( ( uiInitData >> 16 ) & 0xFFF ); // Ignores MSB which is set to 1! + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_HowToPlay, (ETelemetry_HowToPlay_SubMenuId)eStartPage); + + MapChildControls(); + + wstring wsTemp, inventoryString = app.GetString(IDS_INVENTORY); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCTItem],app.GetString(IDS_ITEM_HATCHET_WOOD)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCTGroup],app.GetString(IDS_GROUPNAME_TOOLS)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCTInventory3x3],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCItem],app.GetString(IDS_TILE_WORKBENCH)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCGroup],app.GetString(IDS_GROUPNAME_STRUCTURES)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCInventory2x2],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelFFuel],app.GetString(IDS_FUEL)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelFInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelFIngredient],app.GetString(IDS_INGREDIENT)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelFChest],app.GetString(IDS_FURNACE)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelLCInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelCreativeInventory],app.GetString(IDS_GROUPNAME_BUILDING_BLOCKS)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelLCChest],app.GetString(IDS_CHEST)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelSCInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelSCChest],app.GetString(IDS_CHEST)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelIInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelDInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelDText],app.GetString(IDS_DISPENSER)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelEEnchant],app.GetString(IDS_ENCHANT)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelEInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelBBrew],app.GetString(IDS_BREWING_STAND)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelBInventory],inventoryString.c_str()); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelAnvil_Inventory], inventoryString.c_str()); + + wsTemp = app.GetString(IDS_REPAIR_COST); + wsTemp.replace( wsTemp.find(L"%d"), 2, wstring(L"8") ); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelAnvil_Cost], wsTemp.c_str()); + + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelAnvil_ARepairAndName], app.GetString(IDS_REPAIR_AND_NAME)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelTrading_Inventory], inventoryString.c_str()); + //XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelTrading_Offer2], app.GetString(IDS_ITEM_HATCHET_DIAMOND)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelTrading_Offer1], app.GetString(IDS_ITEM_EMERALD)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelTrading_NeededForTrade], app.GetString(IDS_REQUIRED_ITEMS_FOR_TRADE)); + + wsTemp = app.GetString(IDS_VILLAGER_OFFERS_ITEM); + wsTemp = replaceAll(wsTemp,L"{*VILLAGER_TYPE*}",app.GetString(IDS_VILLAGER_PRIEST)); + wsTemp.replace(wsTemp.find(L"%s"),2, app.GetString(IDS_TILE_LIGHT_GEM)); + XuiControlSetText(m_aLabelControls[ eHowToPlay_LabelTrading_VillagerOffers], wsTemp.c_str()); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + + StartPage( eStartPage ); + + return S_OK; +} + +HRESULT CScene_HowToPlay::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + { + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + } + break; + case VK_PAD_A: + { + // Next page + int iNextPage = ( int )( m_eCurrPage ) + 1; + if ( iNextPage != eHowToPlay_NumPages ) + { + StartPage( ( EHowToPlayPage )( iNextPage ) ); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + rfHandled = TRUE; + } + break; + case VK_PAD_X: + { + // Next page + int iPrevPage = ( int )( m_eCurrPage ) - 1; + if ( iPrevPage >= 0 ) + { + StartPage( ( EHowToPlayPage )( iPrevPage ) ); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + rfHandled = TRUE; + } + break; + } + return S_OK; +} + + +void CScene_HowToPlay::StartPage( EHowToPlayPage ePage ) +{ + int iBaseSceneUser; + // if we're not in the game, we need to use basescene 0 + if(Minecraft::GetInstance()->level==NULL) + { + iBaseSceneUser=DEFAULT_XUI_MENU_USER; + } + else + { + iBaseSceneUser=m_iPad; + } + m_eCurrPage = ePage; + + // Turn off everything. + for ( int i = 0; i < eHowToPlay_NumTexts; ++i ) + { + m_aTextControls[ i ].SetShow( FALSE ); + } + for ( int i = 0; i < eHowToPlay_NumImages; ++i ) + { + m_aImageControls[ i ].SetShow( FALSE ); + } + for ( int i = 0; i < eHowToPlay_NumLabels; ++i ) + { + m_aLabelControls[ i ].SetShow( FALSE ); + } + + // Turn on just what we need for this screen. + SHowToPlayPageDef* pDef = &( gs_aPageDefs[ m_eCurrPage ] ); + + if ( pDef->m_iTextControlIndex != eHowToPlay_TextNone ) + { + // Replace button identifiers in the text with actual button images. + wstring replacedText = app.FormatHTMLString(m_iPad, app.GetString( pDef->m_iTextStringID )); + + // 4J-PB - replace the title with the platform specific title, and the platform name + replacedText = replaceAll(replacedText,L"{*PLATFORM_NAME*}",app.GetString(IDS_PLATFORM_NAME)); + replacedText = replaceAll(replacedText,L"{*BACK_BUTTON*}",app.GetString(IDS_BACK_BUTTON)); + replacedText = replaceAll(replacedText,L"{*DISABLES_ACHIEVEMENTS*}",app.GetString(IDS_HOST_OPTION_DISABLES_ACHIEVEMENTS)); + + // Set the text colour + wstring finalText(replacedText.c_str() ); + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White)); + finalText = startTags + finalText; + + // Set the text in the xui scene. + m_aTextControls[ pDef->m_iTextControlIndex ].SetText( finalText.c_str() ); + + // Make it visible. + m_aTextControls[ pDef->m_iTextControlIndex ].SetShow( TRUE ); + + XuiElementSetUserFocus(m_aTextControls[ pDef->m_iTextControlIndex ].m_hObj, m_iPad); + } + + if(pDef->m_iLabelCount!=0) + { + for(int i=pDef->m_iLabelStartIndex;i<(pDef->m_iLabelStartIndex+pDef->m_iLabelCount);i++) + { + m_aLabelControls[i].SetShow( TRUE ); + } + } + + if ( pDef->m_iImageControlIndex != eHowToPlay_ImageNone ) + { + m_aImageControls[ pDef->m_iImageControlIndex ].SetShow( TRUE ); + } + + // Tool tips. + int iPage = ( int )( m_eCurrPage ); + if ( iPage == 0 ) + { + // No previous page. + ui.SetTooltips( iBaseSceneUser, IDS_HOW_TO_PLAY_NEXT, IDS_TOOLTIPS_BACK, -1 ); + } + else if ( ( iPage + 1 ) == eHowToPlay_NumPages ) + { + // No next page. + ui.SetTooltips( iBaseSceneUser, -1, IDS_TOOLTIPS_BACK, IDS_HOW_TO_PLAY_PREV ); + } + else + { + ui.SetTooltips( iBaseSceneUser, IDS_HOW_TO_PLAY_NEXT, IDS_TOOLTIPS_BACK, IDS_HOW_TO_PLAY_PREV ); + } + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_HowToPlay, (ETelemetry_HowToPlay_SubMenuId)ePage); +} + +HRESULT CScene_HowToPlay::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} diff --git a/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.h b/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.h new file mode 100644 index 0000000..8a37e92 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HelpHowToPlay.h @@ -0,0 +1,217 @@ +#pragma once + +#include "../media/xuiscene_howtoplay.h" +#include "XUI_CustomMessages.h" + +enum EHowToPlayTextControls +{ + eHowToPlay_TextNone = -1, + eHowToPlay_TextWhatsNew = 0, + eHowToPlay_TextBasics, + eHowToPlay_TextMultiplayer, + eHowToPlay_TextHUD, + eHowToPlay_TextCreative, + eHowToPlay_TextInventory, + eHowToPlay_TextSmallChest, + eHowToPlay_TextLargeChest, + eHowToPlay_TextEnderchest, + eHowToPlay_TextCrafting, + eHowToPlay_TextCraftTable, + eHowToPlay_TextFurnace, + eHowToPlay_TextDispenser, + eHowToPlay_TextBrewing, + eHowToPlay_TextEnchantment, + eHowToPlay_TextAnvil, + eHowToPlay_TextFarmingAnimals, + eHowToPlay_TextBreeding, + eHowToPlay_TextTrading, + eHowToPlay_TextNetherPortal, + eHowToPlay_TextTheEnd, + eHowToPlay_TextSocialMedia, + eHowToPlay_TextBanList, + eHowToPlay_TextHostOptions, + eHowToPlay_NumTexts +}; + +enum EHowToPlayImageControls +{ + eHowToPlay_ImageNone = -1, + eHowToPlay_ImageHUD = 0, + eHowToPlay_ImageCreative, + eHowToPlay_ImageInventory, + eHowToPlay_ImageChest, + eHowToPlay_ImageLargeChest, + eHowToPlay_ImageEnderChest, + eHowToPlay_ImageInventoryCrafting, + eHowToPlay_ImageCraftingTable, + eHowToPlay_ImageFurnace, + eHowToPlay_ImageDispenser, + eHowToPlay_ImageBrewing, + eHowToPlay_ImageEnchantment, + eHowToPlay_ImageAnvil, + eHowToPlay_ImageFarmingAnimals, + eHowToPlay_ImageBreeding, + eHowToPlay_ImageTrading, + eHowToPlay_ImageNetherPortal, + eHowToPlay_ImageTheEnd, + eHowToPlay_NumImages +}; + +enum EHowToPlayLabelControls +{ + eHowToPlay_LabelNone = -1, + eHowToPlay_LabelIInventory =0, + eHowToPlay_LabelSCInventory , + eHowToPlay_LabelSCChest , + eHowToPlay_LabelLCInventory , + eHowToPlay_LabelLCChest , + eHowToPlay_LabelCItem , + eHowToPlay_LabelCGroup , + eHowToPlay_LabelCInventory2x2 , + eHowToPlay_LabelCTItem , + eHowToPlay_LabelCTGroup , + eHowToPlay_LabelCTInventory3x3 , + eHowToPlay_LabelFFuel , + eHowToPlay_LabelFInventory , + eHowToPlay_LabelFIngredient , + eHowToPlay_LabelFChest , + eHowToPlay_LabelDText , + eHowToPlay_LabelDInventory , + eHowToPlay_LabelCreativeInventory, + eHowToPlay_LabelEEnchant, + eHowToPlay_LabelEInventory, + eHowToPlay_LabelBBrew, + eHowToPlay_LabelBInventory, + eHowToPlay_LabelAnvil_Inventory, + eHowToPlay_LabelAnvil_Cost, + eHowToPlay_LabelAnvil_ARepairAndName, + eHowToPlay_LabelTrading_Inventory, + eHowToPlay_LabelTrading_Offer2, + eHowToPlay_LabelTrading_Offer1, + eHowToPlay_LabelTrading_NeededForTrade, + eHowToPlay_LabelTrading_VillagerOffers, + eHowToPlay_NumLabels +}; + +struct SHowToPlayPageDef +{ + int m_iTextControlIndex; // eHowToPlay_TextNone if not used. + int m_iTextStringID; // -1 if not used. + int m_iImageControlIndex; // eHowToPlay_ImageNone if not used. + int m_iLabelStartIndex; // index of the labels if there are any for the page + int m_iLabelCount; +}; + +class CScene_HowToPlay : public CXuiSceneImpl +{ +protected: + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + EHowToPlayPage m_eCurrPage; + + // Control and Element wrapper objects. + CXuiHtmlElement m_aTextControls[ eHowToPlay_NumTexts ]; + CXuiControl m_aImageControls[ eHowToPlay_NumImages ]; + CXuiControl m_aLabelControls[ eHowToPlay_NumLabels ]; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiHtmlControlMultiplayer, m_aTextControls[ eHowToPlay_TextMultiplayer ] ) + MAP_CONTROL(IDC_XuiHtmlControlBasics, m_aTextControls[ eHowToPlay_TextBasics ] ) + MAP_CONTROL(IDC_XuiHtmlControlHUD, m_aTextControls[ eHowToPlay_TextHUD ] ) + MAP_CONTROL(IDC_XuiHtmlControlCreative, m_aTextControls[ eHowToPlay_TextCreative ] ) + MAP_CONTROL(IDC_XuiHtmlControlInventory, m_aTextControls[ eHowToPlay_TextInventory ] ) + MAP_CONTROL(IDC_XuiHtmlControlChest, m_aTextControls[ eHowToPlay_TextSmallChest ] ) + MAP_CONTROL(IDC_XuiHtmlControlLargeChest, m_aTextControls[ eHowToPlay_TextLargeChest ] ) + MAP_CONTROL(IDC_XuiHtmlControlEnderchest, m_aTextControls[ eHowToPlay_TextEnderchest ] ) + MAP_CONTROL(IDC_XuiHtmlControlCrafting, m_aTextControls[ eHowToPlay_TextCrafting ] ) + MAP_CONTROL(IDC_XuiHtmlControlCraftingTable, m_aTextControls[ eHowToPlay_TextCraftTable ] ) + MAP_CONTROL(IDC_XuiHtmlControlFurnace, m_aTextControls[ eHowToPlay_TextFurnace ] ) + MAP_CONTROL(IDC_XuiHtmlControlDispenser, m_aTextControls[ eHowToPlay_TextDispenser ] ) + MAP_CONTROL(IDC_XuiHtmlControlBrewing, m_aTextControls[ eHowToPlay_TextBrewing ] ) + MAP_CONTROL(IDC_XuiHtmlControlEnchantment, m_aTextControls[ eHowToPlay_TextEnchantment ] ) + MAP_CONTROL(IDC_XuiHtmlControlAnvil, m_aTextControls[ eHowToPlay_TextAnvil ] ) + MAP_CONTROL(IDC_XuiHtmlControlFarmingAnimals, m_aTextControls[ eHowToPlay_TextFarmingAnimals ] ) + MAP_CONTROL(IDC_XuiHtmlControlBreeding, m_aTextControls[ eHowToPlay_TextBreeding ] ) + MAP_CONTROL(IDC_XuiHtmlControlTrading, m_aTextControls[ eHowToPlay_TextTrading ] ) + MAP_CONTROL(IDC_XuiHtmlControlNetherPortal, m_aTextControls[ eHowToPlay_TextNetherPortal ] ) + MAP_CONTROL(IDC_XuiHtmlControlTheEnd, m_aTextControls[ eHowToPlay_TextTheEnd ] ) + MAP_CONTROL(IDC_XuiHtmlControlSocialMedia, m_aTextControls[ eHowToPlay_TextSocialMedia ] ) + MAP_CONTROL(IDC_XuiHtmlControlBanList, m_aTextControls[ eHowToPlay_TextBanList ] ) + MAP_CONTROL(IDC_XuiHtmlControlWhatsNew, m_aTextControls[ eHowToPlay_TextWhatsNew ] ) + MAP_CONTROL(IDC_XuiHtmlControlHostOptions, m_aTextControls[ eHowToPlay_TextHostOptions] ) + + MAP_CONTROL(IDC_XuiImageHUD, m_aImageControls[ eHowToPlay_ImageHUD ] ) + MAP_CONTROL(IDC_XuiImageCreative, m_aImageControls[ eHowToPlay_ImageCreative ] ) + MAP_CONTROL(IDC_XuiImageInventory, m_aImageControls[ eHowToPlay_ImageInventory ] ) + MAP_CONTROL(IDC_XuiImageChest, m_aImageControls[ eHowToPlay_ImageChest ] ) + MAP_CONTROL(IDC_XuiImageLargeChest, m_aImageControls[ eHowToPlay_ImageLargeChest ] ) + MAP_CONTROL(IDC_XuiImageEnderchest, m_aImageControls[ eHowToPlay_ImageEnderChest ] ) + MAP_CONTROL(IDC_XuiImageCrafting, m_aImageControls[ eHowToPlay_ImageInventoryCrafting ] ) + MAP_CONTROL(IDC_XuiImageCraftingTable, m_aImageControls[ eHowToPlay_ImageCraftingTable ] ) + MAP_CONTROL(IDC_XuiImageFurnace, m_aImageControls[ eHowToPlay_ImageFurnace ] ) + MAP_CONTROL(IDC_XuiImageDispenser, m_aImageControls[ eHowToPlay_ImageDispenser ] ) + MAP_CONTROL(IDC_XuiImageBrewing, m_aImageControls[ eHowToPlay_ImageBrewing ] ) + MAP_CONTROL(IDC_XuiImageEnchantment, m_aImageControls[ eHowToPlay_ImageEnchantment ] ) + MAP_CONTROL(IDC_XuiImageAnvil, m_aImageControls[ eHowToPlay_ImageAnvil ] ) + MAP_CONTROL(IDC_XuiImageBreeding, m_aImageControls[ eHowToPlay_ImageBreeding ] ) + MAP_CONTROL(IDC_XuiImageFarmingAnimals, m_aImageControls[ eHowToPlay_ImageFarmingAnimals ] ) + MAP_CONTROL(IDC_XuiImageTrading, m_aImageControls[ eHowToPlay_ImageTrading ] ) + MAP_CONTROL(IDC_XuiImageNetherPortal, m_aImageControls[ eHowToPlay_ImageNetherPortal ] ) + MAP_CONTROL(IDC_XuiImageTheEnd, m_aImageControls[ eHowToPlay_ImageTheEnd ] ) + + MAP_CONTROL(IDC_CTItem, m_aLabelControls[ eHowToPlay_LabelCTItem ] ) + MAP_CONTROL(IDC_CTGroup, m_aLabelControls[ eHowToPlay_LabelCTGroup ] ) + MAP_CONTROL(IDC_CTInventory3x3, m_aLabelControls[ eHowToPlay_LabelCTInventory3x3 ] ) + MAP_CONTROL(IDC_CItem, m_aLabelControls[ eHowToPlay_LabelCItem ] ) + MAP_CONTROL(IDC_CGroup, m_aLabelControls[ eHowToPlay_LabelCGroup ] ) + MAP_CONTROL(IDC_CInventory, m_aLabelControls[ eHowToPlay_LabelCInventory2x2 ] ) + MAP_CONTROL(IDC_FFuel, m_aLabelControls[ eHowToPlay_LabelFFuel ] ) + MAP_CONTROL(IDC_FInventory, m_aLabelControls[ eHowToPlay_LabelFInventory ] ) + MAP_CONTROL(IDC_FIngredient, m_aLabelControls[ eHowToPlay_LabelFIngredient ] ) + MAP_CONTROL(IDC_FChest, m_aLabelControls[ eHowToPlay_LabelFChest ] ) + MAP_CONTROL(IDC_LCInventory, m_aLabelControls[ eHowToPlay_LabelLCInventory ] ) + MAP_CONTROL(IDC_CIGroup, m_aLabelControls[ eHowToPlay_LabelCreativeInventory ] ) + MAP_CONTROL(IDC_LCChest, m_aLabelControls[ eHowToPlay_LabelLCChest ] ) + MAP_CONTROL(IDC_SCInventory, m_aLabelControls[ eHowToPlay_LabelSCInventory ] ) + MAP_CONTROL(IDC_SCChest, m_aLabelControls[ eHowToPlay_LabelSCChest ] ) + MAP_CONTROL(IDC_IInventory, m_aLabelControls[ eHowToPlay_LabelIInventory ] ) + MAP_CONTROL(IDC_DInventory, m_aLabelControls[ eHowToPlay_LabelDInventory ] ) + MAP_CONTROL(IDC_DText, m_aLabelControls[ eHowToPlay_LabelDText ] ) + MAP_CONTROL(IDC_EEnchant, m_aLabelControls[ eHowToPlay_LabelEEnchant ] ) + MAP_CONTROL(IDC_EInventory, m_aLabelControls[ eHowToPlay_LabelEInventory ] ) + MAP_CONTROL(IDC_BBrew, m_aLabelControls[ eHowToPlay_LabelBBrew ] ) + MAP_CONTROL(IDC_BInventory, m_aLabelControls[ eHowToPlay_LabelBInventory ] ) + MAP_CONTROL(IDC_AInventory, m_aLabelControls[ eHowToPlay_LabelAnvil_Inventory ] ) + MAP_CONTROL(IDC_ACost, m_aLabelControls[ eHowToPlay_LabelAnvil_Cost ] ) + MAP_CONTROL(IDC_ARepairAndName, m_aLabelControls[ eHowToPlay_LabelAnvil_ARepairAndName ] ) + MAP_CONTROL(IDC_TInventory, m_aLabelControls[ eHowToPlay_LabelTrading_Inventory ] ) + //MAP_CONTROL(IDC_TOffer2Label, m_aLabelControls[ eHowToPlay_LabelTrading_Offer2 ] ) + MAP_CONTROL(IDC_TOffer1Label, m_aLabelControls[ eHowToPlay_LabelTrading_Offer1 ] ) + MAP_CONTROL(IDC_TNeededForTrade, m_aLabelControls[ eHowToPlay_LabelTrading_NeededForTrade ] ) + MAP_CONTROL(IDC_TVillagerOffers, m_aLabelControls[ eHowToPlay_LabelTrading_VillagerOffers ] ) + + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + void StartPage( EHowToPlayPage ePage ); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_HowToPlay, L"CScene_HowToPlay", XUI_CLASS_SCENE ) + + + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Helper.h b/Minecraft.Client/Common/XUI/XUI_Helper.h new file mode 100644 index 0000000..cb47618 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Helper.h @@ -0,0 +1,38 @@ +#pragma once + +#define BEGIN_CONTROL_MAP() \ + HRESULT MapChildControls() \ + { \ + HRESULT hr = S_OK; \ + CXuiElement e = m_hObj; \ + + + +#define MAP_CONTROL(name, member) \ + hr = e.GetChildById(name, &member); \ + assert(hr==0); \ + +#define BEGIN_MAP_CHILD_CONTROLS( member ) \ + { \ + CXuiElement tempE = e; \ + e = member; \ + +#define END_MAP_CHILD_CONTROLS() \ + e = tempE; \ + } \ + + +#define MAP_OVERRIDE(name, member) \ + { \ + HXUIOBJ h; \ + hr = e.GetChildById(name, &h); \ + assert(hr==0); \ + hr = XuiObjectFromHandle(h, reinterpret_cast(&member)); \ + assert(hr==0); \ + } \ + + +#define END_CONTROL_MAP() \ + return hr; \ + } \ + diff --git a/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.cpp b/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.cpp new file mode 100644 index 0000000..14046f2 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.cpp @@ -0,0 +1,232 @@ +#include "stdafx.h" + +#include +#include "..\XUI\XUI_HowToPlayMenu.h" +#include "..\XUI\XUI_HelpHowToPlay.h" + +// strings for buttons in the list +unsigned int CScene_HowToPlayMenu::m_uiHTPButtonNameA[]= +{ + IDS_HOW_TO_PLAY_MENU_WHATSNEW, // eHTPButton_WhatsNew + IDS_HOW_TO_PLAY_MENU_BASICS, // eHTPButton_Basics, + IDS_HOW_TO_PLAY_MENU_MULTIPLAYER, // eHTPButton_Multiplayer + IDS_HOW_TO_PLAY_MENU_HUD, // eHTPButton_Hud, + IDS_HOW_TO_PLAY_MENU_CREATIVE, // eHTPButton_Creative, + IDS_HOW_TO_PLAY_MENU_INVENTORY, // eHTPButton_Inventory, + IDS_HOW_TO_PLAY_MENU_CHESTS, // eHTPButton_Chest, + IDS_HOW_TO_PLAY_MENU_CRAFTING, // eHTPButton_Crafting, + IDS_HOW_TO_PLAY_MENU_FURNACE, // eHTPButton_Furnace, + IDS_HOW_TO_PLAY_MENU_DISPENSER, // eHTPButton_Dispenser, + + IDS_HOW_TO_PLAY_MENU_BREWING, // eHTPButton_Brewing, + IDS_HOW_TO_PLAY_MENU_ENCHANTMENT, // eHTPButton_Enchantment, + IDS_HOW_TO_PLAY_MENU_ANVIL, + IDS_HOW_TO_PLAY_MENU_FARMANIMALS, // eHTPButton_Breeding, + IDS_HOW_TO_PLAY_MENU_BREEDANIMALS, // eHTPButton_Breeding, + IDS_HOW_TO_PLAY_MENU_TRADING, + + IDS_HOW_TO_PLAY_MENU_NETHERPORTAL, // eHTPButton_NetherPortal, + IDS_HOW_TO_PLAY_MENU_THEEND, // eHTPButton_TheEnd, + IDS_HOW_TO_PLAY_MENU_SOCIALMEDIA, // eHTPButton_SocialMedia, + IDS_HOW_TO_PLAY_MENU_BANLIST, // eHTPButton_BanningLevels, + IDS_HOW_TO_PLAY_MENU_HOSTOPTIONS, // eHTPButton_HostOptions, +}; + +// mapping the buttons to a scene value +unsigned int CScene_HowToPlayMenu::m_uiHTPSceneA[]= +{ + eHowToPlay_WhatsNew, + eHowToPlay_Basics, + eHowToPlay_Multiplayer, + eHowToPlay_HUD, + eHowToPlay_Creative, + eHowToPlay_Inventory, + eHowToPlay_Chest, + eHowToPlay_InventoryCrafting, + eHowToPlay_Furnace, + eHowToPlay_Dispenser, + + eHowToPlay_Brewing, + eHowToPlay_Enchantment, + eHowToPlay_Anvil, + eHowToPlay_FarmingAnimals, + eHowToPlay_Breeding, + eHowToPlay_Trading, + + eHowToPlay_NetherPortal, + eHowToPlay_TheEnd, + eHowToPlay_SocialMedia, + eHowToPlay_BanList, + eHowToPlay_HostOptions, +}; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_HowToPlayMenu::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bSplitscreen= app.GetLocalPlayerCount()>1; + m_ButtonList=NULL; + + //MapChildControls(); + + //m_iButtons=0; + if(bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad, false); + } + + // 4J-PB - changing all versions to use a list of buttons, since we're adding some + // We're going to use a list of buttons here + CXuiElement e = m_hObj; + HRESULT hr = e.GetChildById(L"HowToListButtons", &m_ButtonList); + m_iButtons=eHTPButton_Max; + for(int i=0;ibItemData ) + { + if( pGetSourceTextData->iItem < (int)eHTPButton_Max ) + { + pGetSourceTextData->szText = app.GetString(m_uiHTPButtonNameA[pGetSourceTextData->iItem]);//m_Buttons[pGetSourceTextData->iItem].GetText(); + pGetSourceTextData->bDisplay = TRUE; + + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_HowToPlayMenu::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +{ + pGetItemCountData->cItems = m_iButtons; + bHandled = TRUE; + return S_OK; +} + + +HRESULT CScene_HowToPlayMenu::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + // In a list, we need to play the 'focus' sound ourselves + if((pNotifySelChangedData->iOldItem!=-1) && m_ButtonList && (hObjSource==m_ButtonList.m_hObj)) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + + return S_OK; +} +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_HowToPlayMenu::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + unsigned int uiInitData; + unsigned int uiButtonCounter=0; + + // 4J-PB - now using a list for all resolutions + //if((!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) || app.GetLocalPlayerCount()>1) + { + if(hObjPressed==m_ButtonList && m_ButtonList.TreeHasFocus() && (m_ButtonList.GetItemCount() > 0) && (m_ButtonList.GetCurSel() < (int)eHTPButton_Max) ) + { + uiButtonCounter=m_ButtonList.GetCurSel(); + } + } + /*else + { + while((uiButtonCounter1) + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_HowToPlay, ( void* )( uiInitData ) ); + } + else + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_HowToPlay, ( void* )( uiInitData ) ); + } + + rfHandled=TRUE; + return S_OK; +} + +HRESULT CScene_HowToPlayMenu::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + + break; + } + + return S_OK; +} + +HRESULT CScene_HowToPlayMenu::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + return S_OK; +} + +HRESULT CScene_HowToPlayMenu::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining,false); +} diff --git a/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.h b/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.h new file mode 100644 index 0000000..ba334c8 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_HowToPlayMenu.h @@ -0,0 +1,76 @@ +#pragma once + +#include "../media/xuiscene_howtoplay_menu.h" +#include "XUI_CustomMessages.h" + +class CScene_HowToPlayMenu : public CXuiSceneImpl +{ +protected: + + enum eHTPButton + { + eHTPButton_WhatsNew = 0, + eHTPButton_Basics, + eHTPButton_Multiplayer, + eHTPButton_Hud, + eHTPButton_Creative, + eHTPButton_Inventory, + eHTPButton_Chest, + eHTPButton_Crafting, + eHTPButton_Furnace, + eHTPButton_Dispenser, + eHTPButton_Brewing, + eHTPButton_Enchantment, + eHTPButton_Anvil, + eHTPButton_FarmingAnimals, + eHTPButton_Breeding, + eHTPButton_Trading, + eHTPButton_NetherPortal, + eHTPButton_TheEnd, + eHTPButton_SocialMedia, + eHTPButton_BanningLevels, + eHTPButton_HostOptions, + eHTPButton_Max, + }; + + // Control and Element wrapper objects. + CXuiScene m_Scene; + CXuiElement m_Background; + CXuiList m_ButtonList; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_NOTIFY_SELCHANGED(OnNotifySelChanged) + + XUI_END_MSG_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_HowToPlayMenu, L"CScene_HowToPlayMenu", XUI_CLASS_SCENE ) + +private: + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + static unsigned int m_uiHTPButtonNameA[eHTPButton_Max]; + static unsigned int m_uiHTPSceneA[eHTPButton_Max]; + int m_iButtons; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.cpp b/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.cpp new file mode 100644 index 0000000..f056174 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.cpp @@ -0,0 +1,150 @@ +#include "stdafx.h" +#include "XUI_MultiGameCreate.h" +#include "XUI_InGameHostOptions.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGameHostOptions::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + m_focusElement = m_CheckboxFireSpreads.m_hObj; + + XuiControlSetText(m_CheckboxFireSpreads,app.GetString(IDS_FIRE_SPREADS)); + XuiControlSetText(m_CheckboxTNTExplodes,app.GetString(IDS_TNT_EXPLODES)); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + m_CheckboxFireSpreads.SetEnable(TRUE); + m_CheckboxTNTExplodes.SetEnable(TRUE); + m_CheckboxFireSpreads.SetCheck((app.GetGameHostOption(eGameHostOption_FireSpreads)!=0)?TRUE:FALSE); + m_CheckboxTNTExplodes.SetCheck((app.GetGameHostOption(eGameHostOption_TNT)!=0)?TRUE:FALSE); + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + unsigned int privs = app.GetPlayerPrivileges(localPlayer->GetSmallId()); + if ( app.GetGameHostOption(eGameHostOption_CheatsEnabled) + && Player::getPlayerGamePrivilege(privs,Player::ePlayerGamePrivilege_CanTeleport) + && (g_NetworkManager.GetPlayerCount() > 1) ) + { + m_buttonTeleportToPlayer.SetText(app.GetString(IDS_TELEPORT_TO_PLAYER)); + m_buttonTeleportToMe.SetText(app.GetString(IDS_TELEPORT_TO_ME)); + } + else + { + removeControl(m_buttonTeleportToPlayer, true); + removeControl(m_buttonTeleportToMe, true); + } + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + + + //SentientManager.RecordMenuShown(m_iPad, eUIScene_CreateWorldMenu, 0); + + return S_OK; +} + + +HRESULT CScene_InGameHostOptions::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + { + unsigned int hostOptions = app.GetGameHostOption(eGameHostOption_All); + app.SetGameHostOption(hostOptions,eGameHostOption_FireSpreads,m_CheckboxFireSpreads.IsChecked()); + app.SetGameHostOption(hostOptions,eGameHostOption_TNT,m_CheckboxTNTExplodes.IsChecked()); + + // Send update settings packet to server + if(hostOptions != app.GetGameHostOption(eGameHostOption_All) ) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player != NULL && player->connection) + { + player->connection->send( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, hostOptions) ) ); + } + } + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + } + break; + } + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGameHostOptions::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed == m_buttonTeleportToPlayer || hObjPressed == m_buttonTeleportToMe) + { + TeleportMenuInitData *initData = new TeleportMenuInitData(); + initData->iPad = m_iPad; + initData->teleportToPlayer = false; + if( hObjPressed == m_buttonTeleportToPlayer ) + { + initData->teleportToPlayer = true; + } + ui.NavigateToScene(m_iPad,eUIScene_TeleportMenu,(void*)initData); + } + return S_OK; +} + +void CScene_InGameHostOptions::removeControl(HXUIOBJ hObjToRemove, bool center) +{ + D3DXVECTOR3 pos; + float fControlHeight, fTempHeight, fWidth; + + bool changeFocus = m_focusElement == hObjToRemove; + + XuiElementGetBounds(hObjToRemove,&fWidth,&fControlHeight); + + // Hide this control + XuiControlSetEnable(hObjToRemove, FALSE); + XuiElementSetShow(hObjToRemove, FALSE); + + // Move future downwards nav up + HXUIOBJ controlToMove = hObjToRemove; + while(controlToMove = XuiControlGetNavigation(controlToMove, XUI_CONTROL_NAVIGATE_DOWN, FALSE, TRUE) ) + { + if(changeFocus && XuiElementIsShown(controlToMove)) + { + m_focusElement = controlToMove; + XuiElementSetUserFocus( controlToMove, m_iPad ); + changeFocus = FALSE; + } + XuiElementGetPosition(controlToMove, &pos); + pos.y -= fControlHeight; + XuiElementSetPosition(controlToMove, &pos); + } + + // Resize and move scene + GetBounds(&fWidth, &fTempHeight); + SetBounds(fWidth, fTempHeight - fControlHeight); + + GetPosition(&pos); + pos.y += fControlHeight/2; + SetPosition(&pos); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.h b/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.h new file mode 100644 index 0000000..cdc99f7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGameHostOptions.h @@ -0,0 +1,45 @@ +#pragma once +#include "..\Media\xuiscene_ingame_host_options.h" + +class CScene_InGameHostOptions : public CXuiSceneImpl +{ +protected: + CXuiScene m_GameOptionsGroup; + CXuiCheckbox m_CheckboxFireSpreads; + CXuiCheckbox m_CheckboxTNTExplodes; + CXuiControl m_buttonTeleportToPlayer, m_buttonTeleportToMe; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_GameOptions, m_GameOptionsGroup) + BEGIN_MAP_CHILD_CONTROLS(m_GameOptionsGroup) + MAP_CONTROL(IDC_CheckboxFireSpreads, m_CheckboxFireSpreads) + MAP_CONTROL(IDC_CheckboxTNT, m_CheckboxTNTExplodes) + MAP_CONTROL(IDC_ButtonTeleportToPlayer, m_buttonTeleportToPlayer) + MAP_CONTROL(IDC_ButtonTeleportPlayerToMe, m_buttonTeleportToMe) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_InGameHostOptions, L"CScene_InGameHostOptions", XUI_CLASS_SCENE ) + +private: + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + HXUIOBJ m_focusElement; // Only used for the remove control process + + void removeControl(HXUIOBJ hObjToRemove, bool center); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_InGameInfo.cpp b/Minecraft.Client/Common/XUI/XUI_InGameInfo.cpp new file mode 100644 index 0000000..4839013 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGameInfo.cpp @@ -0,0 +1,537 @@ +#include "stdafx.h" + +#include +#include "XUI_InGameInfo.h" +#include "..\..\ServerPlayer.h" +#include "..\..\PlayerConnection.h" +#include "..\..\PlayerList.h" +#include "..\..\MinecraftServer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\PlayerRenderer.h" +#include "XUI_InGamePlayerOptions.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\Xbox\Network\NetworkPlayerXbox.h" + +#define IGNORE_KEYPRESS_TIMERID 0 +#define TOOLTIP_TIMERID 1 +#define IGNORE_KEYPRESS_TIME 100 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGameInfo::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_bIgnoreKeyPresses=true; + m_iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + XuiControlSetText(m_gameOptionsButton,app.GetString(IDS_HOST_OPTIONS)); + XuiControlSetText(m_title,app.GetString(IDS_PLAYERS_INVITE)); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL ) + { + m_players[i] = player->GetSmallId(); + ++m_playersCount; + } + } + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &CScene_InGameInfo::OnPlayerChanged, this); + + INetworkPlayer *thisPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + m_isHostPlayer = false; + if(thisPlayer != NULL) m_isHostPlayer = thisPlayer->IsHost() == TRUE; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + if(!m_isHostPlayer && !localPlayer->isModerator() ) + { + m_gameOptionsButton.SetEnable(FALSE); + m_gameOptionsButton.SetShow(FALSE); + playersList.SetFocus(m_iPad); + } + + int keyX = IDS_TOOLTIPS_INVITE_FRIENDS; + XPARTY_USER_LIST partyList; + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + keyX = IDS_TOOLTIPS_INVITE_PARTY; + } + if(g_NetworkManager.IsLocalGame()) keyX = -1; + + int keyA = -1; + ui.SetTooltips( m_iPad, keyA,IDS_TOOLTIPS_BACK,keyX,-1); + + + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + + SetTimer( TOOLTIP_TIMERID , INGAME_INFO_TOOLTIP_TIMER ); + + // get rid of the quadrant display if it's on + CXuiSceneBase::HidePressStart(); + + SetTimer(IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); + + return S_OK; +} + +HRESULT CScene_InGameInfo::OnDestroy() +{ + XuiKillTimer(m_hObj,TOOLTIP_TIMERID); + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &CScene_InGameInfo::OnPlayerChanged, this); + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Updates the UI when the list selection changes. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGameInfo::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if( hObjSource == playersList) + { + updateTooltips(); + + bHandled = TRUE; + } + + return S_OK; +} + +HRESULT CScene_InGameInfo::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreKeyPresses) return S_OK; + + // 4J-PB - ignore repeats to stop the scene displaying and quitting right away if you hold the back button down + if((pInputData->dwKeyCode==VK_PAD_BACK) &&(pInputData->dwFlags&XUI_INPUT_FLAG_REPEAT )) + { + rfHandled = TRUE; + return S_OK; + } + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr = S_OK; + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_BACK: + case VK_ESCAPE: + CXuiSceneBase::PlayUISFX(eSFX_Back); + app.CloseXuiScenes(pInputData->UserIndex); + rfHandled = TRUE; + + break; + case VK_PAD_Y: + if(playersList.TreeHasFocus() && (playersList.GetItemCount() > 0) && (playersList.GetCurSel() < m_playersCount) ) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId(m_players[playersList.GetCurSel()]); + if( player != NULL ) + { + PlayerUID xuid = ((NetworkPlayerXbox *)player)->GetUID(); + if( xuid != INVALID_XUID ) + hr = XShowGamerCardUI(pInputData->UserIndex, xuid); + } + } + break; + case VK_PAD_X: + { + if(!g_NetworkManager.IsLocalGame()) + { + XPARTY_USER_LIST partyList; + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY) && (partyList.dwUserCount>1)) + { + hr = XShowPartyUI( pInputData->UserIndex ); + } + else + { + hr = XShowFriendsUI( pInputData->UserIndex ); + } + } + } + break; + } + + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGameInfo::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if( hObjPressed == playersList ) + { + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ playersList.GetCurSel() ] ); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + + bool isOp = m_isHostPlayer || localPlayer->isModerator(); + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + bool trust = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + + if( isOp && selectedPlayer != NULL && playersList.TreeHasFocus() && (playersList.GetItemCount() > 0) && (playersList.GetCurSel() < m_playersCount) ) + { + bool editingHost = selectedPlayer->IsHost(); + if( (cheats && (m_isHostPlayer || !editingHost ) ) + || (!trust && (m_isHostPlayer || !editingHost)) + || (m_isHostPlayer && !editingHost) +#if (!defined(_CONTENT_PACKAGE) && !defined(_FINAL_BUILD) && defined(_DEBUG_MENUS_ENABLED)) + || (m_isHostPlayer && editingHost) +#endif + ) + { + InGamePlayerOptionsInitData *pInitData = new InGamePlayerOptionsInitData(); + pInitData->iPad = m_iPad; + pInitData->networkSmallId = m_players[ playersList.GetCurSel() ]; + pInitData->playerPrivileges = app.GetPlayerPrivileges(m_players[ playersList.GetCurSel() ] ); + app.NavigateToScene(m_iPad,eUIScene_InGamePlayerOptionsMenu,pInitData); + } + else if(selectedPlayer->IsLocal() != TRUE && selectedPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + // Only ops will hit this, can kick anyone not local and not local to the host + BYTE *smallId = new BYTE(); + *smallId = m_players[playersList.GetCurSel()]; + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + StorageManager.RequestMessageBox(IDS_UNLOCK_KICK_PLAYER_TITLE, IDS_UNLOCK_KICK_PLAYER, uiIDA, 2, m_iPad,&CScene_InGameInfo::KickPlayerReturned,smallId,app.GetStringTable()); + } + } + } + else if( hObjPressed == m_gameOptionsButton ) + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_InGameHostOptionsMenu); + } + return S_OK; +} + +HRESULT CScene_InGameInfo::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==IGNORE_KEYPRESS_TIMERID) + { + XuiKillTimer(m_hObj,IGNORE_KEYPRESS_TIMERID); + m_bIgnoreKeyPresses=false; + } + else + { + updateTooltips(); + } + + return S_OK; +} + +HRESULT CScene_InGameInfo::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + if(pTransition->dwTransType == XUI_TRANSITION_FROM) + { + KillTimer( TOOLTIP_TIMERID ); + } + return S_OK; +} + +HRESULT CScene_InGameInfo::OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + if(pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + SetTimer( TOOLTIP_TIMERID , INGAME_INFO_TOOLTIP_TIMER ); + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &CScene_InGameInfo::OnPlayerChanged, this); + } + return S_OK; +} + +HRESULT CScene_InGameInfo::OnNotifySetFocus( HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled ) +{ + updateTooltips(); + return S_OK; +} + +void CScene_InGameInfo::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + CScene_InGameInfo *scene = (CScene_InGameInfo *)callbackParam; + bool playerFound = false; + + for(int i = 0; i < scene->m_playersCount; ++i) + { + if(playerFound) + { + scene->m_players[i-1] = scene->m_players[i]; + } + else if( scene->m_players[i] == pPlayer->GetSmallId() ) + { + if( scene->playersList.GetCurSel() == scene->playersList.GetItemCount() - 1 ) + { + scene->playersList.SetCurSel( scene->playersList.GetItemCount() - 2 ); + } + // Player removed + playerFound = true; + } + } + + if( playerFound ) + { + --scene->m_playersCount; + scene->playersList.DeleteItems( scene->playersList.GetItemCount() - 1, 1 ); + } + + if( !playerFound ) + { + // Player added + scene->m_players[scene->m_playersCount] = pPlayer->GetSmallId(); + ++scene->m_playersCount; + scene->playersList.InsertItems( scene->playersList.GetItemCount(), 1 ); + } +} + + +HRESULT CScene_InGameInfo::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + if( pGetSourceTextData->bItemData ) + { + if( pGetSourceTextData->iItem < m_playersCount ) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId( m_players[pGetSourceTextData->iItem] ); + if( player != NULL ) + { +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<szText = L"WWWWWWWWWWWWWWWW"; + } + else +#endif + { + pGetSourceTextData->szText = player->GetOnlineName(); + } + } + else + { + pGetSourceTextData->szText = L""; + } + + HRESULT hr; + HXUIOBJ hButton, hVisual, hPlayerIcon, hVoiceIcon; + hButton = playersList.GetItemControl(pGetSourceTextData->iItem); + hr=XuiControlGetVisual(hButton,&hVisual); + + // Set the players icon + hr=XuiElementGetChildById(hVisual,L"IconGroup",&hPlayerIcon); + short colourIndex = app.GetPlayerColour( m_players[pGetSourceTextData->iItem] ); + int playFrame = 0; + switch(colourIndex) + { + case 1: + XuiElementFindNamedFrame(hPlayerIcon, L"P1", &playFrame); + break; + case 2: + XuiElementFindNamedFrame(hPlayerIcon, L"P2", &playFrame); + break; + case 3: + XuiElementFindNamedFrame(hPlayerIcon, L"P3", &playFrame); + break; + case 4: + XuiElementFindNamedFrame(hPlayerIcon, L"P4", &playFrame); + break; + case 5: + XuiElementFindNamedFrame(hPlayerIcon, L"P5", &playFrame); + break; + case 6: + XuiElementFindNamedFrame(hPlayerIcon, L"P6", &playFrame); + break; + case 7: + XuiElementFindNamedFrame(hPlayerIcon, L"P7", &playFrame); + break; + case 8: + XuiElementFindNamedFrame(hPlayerIcon, L"P8", &playFrame); + break; + case 9: + XuiElementFindNamedFrame(hPlayerIcon, L"P9", &playFrame); + break; + case 10: + XuiElementFindNamedFrame(hPlayerIcon, L"P10", &playFrame); + break; + case 11: + XuiElementFindNamedFrame(hPlayerIcon, L"P11", &playFrame); + break; + case 12: + XuiElementFindNamedFrame(hPlayerIcon, L"P12", &playFrame); + break; + case 13: + XuiElementFindNamedFrame(hPlayerIcon, L"P13", &playFrame); + break; + case 14: + XuiElementFindNamedFrame(hPlayerIcon, L"P14", &playFrame); + break; + case 15: + XuiElementFindNamedFrame(hPlayerIcon, L"P15", &playFrame); + break; + case 0: + default: + XuiElementFindNamedFrame(hPlayerIcon, L"P0", &playFrame); + break; + }; + if(playFrame < 0) playFrame = 0; + XuiElementPlayTimeline(hPlayerIcon,playFrame,playFrame,playFrame,FALSE,FALSE); + + // Set the voice icon + hr=XuiElementGetChildById(hVisual,L"VoiceGroup",&hVoiceIcon); + + playFrame = -1; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + XuiElementFindNamedFrame(hVoiceIcon, L"Muted", &playFrame); + } + else if( player->IsTalking() ) + { + // Talking image + XuiElementFindNamedFrame(hVoiceIcon, L"Speaking", &playFrame); + } + else + { + // Not talking image + XuiElementFindNamedFrame(hVoiceIcon, L"NotSpeaking", &playFrame); + } + } + + if(playFrame < 0) + { + XuiElementFindNamedFrame(hVoiceIcon, L"Normal", &playFrame); + } + XuiElementPlayTimeline(hVoiceIcon,playFrame,playFrame,playFrame,FALSE,FALSE); + + pGetSourceTextData->bDisplay = TRUE; + + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_InGameInfo::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + if( pGetSourceImageData->bItemData ) + { + if( pGetSourceImageData->iItem < m_playersCount ) + { + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_InGameInfo::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +{ + pGetItemCountData->cItems = m_playersCount; + bHandled = TRUE; + return S_OK; +} + +void CScene_InGameInfo::updateTooltips() +{ + int keyX = IDS_TOOLTIPS_INVITE_FRIENDS; + int ikeyY = -1; + + XPARTY_USER_LIST partyList; + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + keyX = IDS_TOOLTIPS_INVITE_PARTY; + } + if(g_NetworkManager.IsLocalGame()) keyX = -1; + + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ playersList.GetCurSel() ] ); + + int keyA = -1; + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[m_iPad]; + + bool isOp = m_isHostPlayer || localPlayer->isModerator(); + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + bool trust = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + + if( isOp ) + { + if(m_gameOptionsButton.HasFocus()) + { + keyA = IDS_TOOLTIPS_SELECT; + } + else if( selectedPlayer != NULL) + { + bool editingHost = selectedPlayer->IsHost(); + if( (cheats && (m_isHostPlayer || !editingHost ) ) || (!trust && (m_isHostPlayer || !editingHost)) +#if (!defined(_CONTENT_PACKAGE) && !defined(_FINAL_BUILD) && defined(_DEBUG_MENUS_ENABLED)) + || (m_isHostPlayer && editingHost) +#endif + ) + { + keyA = IDS_TOOLTIPS_PRIVILEGES; + } + else if(selectedPlayer->IsLocal() != TRUE && selectedPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + // Only ops will hit this, can kick anyone not local and not local to the host + keyA = IDS_TOOLTIPS_KICK; + } + } + } + + if(!m_gameOptionsButton.HasFocus()) + { + // if the player is me, then view gamer profile + if(selectedPlayer != NULL && selectedPlayer->IsLocal() && selectedPlayer->GetUserIndex()==m_iPad) + { + ikeyY = IDS_TOOLTIPS_VIEW_GAMERPROFILE; + } + else + { + ikeyY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + } + ui.SetTooltips( m_iPad, keyA,IDS_TOOLTIPS_BACK,keyX,ikeyY); +} + + +HRESULT CScene_InGameInfo::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +int CScene_InGameInfo::KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + BYTE smallId = *(BYTE *)pParam; + delete pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[iPad]; + if(localPlayer != NULL && localPlayer->connection) + { + localPlayer->connection->send( shared_ptr( new KickPlayerPacket(smallId) ) ); + } + } + + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_InGameInfo.h b/Minecraft.Client/Common/XUI/XUI_InGameInfo.h new file mode 100644 index 0000000..8b685aa --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGameInfo.h @@ -0,0 +1,84 @@ +#pragma once +using namespace std; +#include "../media/xuiscene_ingameinfo.h" +#include "XUI_CustomMessages.h" + +class INetworkPlayer; + +#define INGAME_INFO_TOOLTIP_TIMER 1000 + +#define VOICE_ICON_DATA_ID 0 +#define MAP_ICON_DATA_ID 1 +#define OPS_ICON_DATA_ID 2 + +class CScene_InGameInfo : public CXuiSceneImpl +{ +protected: + // Control and Element wrapper objects. + CXuiList playersList; + CXuiControl m_gameOptionsButton; + CXuiControl m_title; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_TRANSITION_START( OnTransitionStart ) + XUI_ON_XM_TRANSITION_END( OnTransitionEnd ) + XUI_ON_XM_NOTIFY_SET_FOCUS( OnNotifySetFocus ) + + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_GamePlayers, playersList) + MAP_CONTROL(IDC_GameOptionsButton, m_gameOptionsButton) + MAP_CONTROL(IDC_Title, m_title) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnNotifySetFocus( HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled ); + + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_InGameInfo, L"CScene_InGameInfo", XUI_CLASS_SCENE ) + + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + +private: + bool m_isHostPlayer; + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + + int m_playersCount; + BYTE m_players[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's + bool m_bIgnoreKeyPresses; + + void updateTooltips(); + +public: + static int KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +}; diff --git a/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.cpp b/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.cpp new file mode 100644 index 0000000..156cd09 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.cpp @@ -0,0 +1,499 @@ +#include "stdafx.h" +#include "XUI_MultiGameCreate.h" +#include "XUI_InGamePlayerOptions.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "XUI_InGameInfo.h" + + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_InGamePlayerOptions::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + InGamePlayerOptionsInitData *initData = (InGamePlayerOptionsInitData *)pInitData->pvInitData; + m_iPad = initData->iPad; + m_networkSmallId = initData->networkSmallId; + m_playerPrivileges = initData->playerPrivileges; + + MapChildControls(); + + m_focusElement = m_checkboxes[eControl_BuildAndMine].m_hObj; + + m_TeleportGroup.SetShow(false); + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + INetworkPlayer *editingPlayer = g_NetworkManager.GetPlayerBySmallId(m_networkSmallId); + + if(editingPlayer != NULL) + { + m_Gamertag.SetText(editingPlayer->GetOnlineName()); + } + + bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + m_editingSelf = (localPlayer != NULL && localPlayer == editingPlayer); + + if( m_editingSelf || trustPlayers || editingPlayer->IsHost()) + { + removeControl( m_checkboxes[eControl_BuildAndMine], true ); + removeControl( m_checkboxes[eControl_UseDoorsAndSwitches], true ); + removeControl( m_checkboxes[eControl_UseContainers], true ); + removeControl( m_checkboxes[eControl_AttackPlayers], true ); + removeControl( m_checkboxes[eControl_AttackAnimals], true ); + } + else + { + bool checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotMine)==0 && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotBuild)==0); + m_checkboxes[eControl_BuildAndMine].SetText( app.GetString(IDS_CAN_BUILD_AND_MINE) ); + m_checkboxes[eControl_BuildAndMine].SetCheck(checked); + + checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches)!=0); + m_checkboxes[eControl_UseDoorsAndSwitches].SetText( app.GetString(IDS_CAN_USE_DOORS_AND_SWITCHES) ); + m_checkboxes[eControl_UseDoorsAndSwitches].SetCheck(checked); + + checked = (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanUseContainers)!=0); + m_checkboxes[eControl_UseContainers].SetText( app.GetString(IDS_CAN_OPEN_CONTAINERS) ); + m_checkboxes[eControl_UseContainers].SetCheck(checked); + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotAttackPlayers)==0; + m_checkboxes[eControl_AttackPlayers].SetText( app.GetString(IDS_CAN_ATTACK_PLAYERS) ); + m_checkboxes[eControl_AttackPlayers].SetCheck(checked); + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CannotAttackAnimals)==0; + m_checkboxes[eControl_AttackAnimals].SetText( app.GetString(IDS_CAN_ATTACK_ANIMALS) ); + m_checkboxes[eControl_AttackAnimals].SetCheck(checked); + } + + if(m_editingSelf) + { +#if (defined(_CONTENT_PACKAGE) || defined(_FINAL_BUILD) && !defined(_DEBUG_MENUS_ENABLED)) + removeControl( m_checkboxes[eControl_Op], true ); +#else + m_checkboxes[eControl_Op].SetText(L"DEBUG: Creative"); + m_checkboxes[eControl_Op].SetCheck(Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode)); +#endif + + removeControl( m_buttonKick, true ); + removeControl( m_checkboxes[eControl_CheatTeleport], true ); + + if(cheats) + { + bool canBeInvisible = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; + m_checkboxes[eControl_HostInvisible].SetEnable(canBeInvisible); + bool checked = canBeInvisible && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Invisible)!=0 && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Invulnerable)!=0); + m_checkboxes[eControl_HostInvisible].SetText( app.GetString(IDS_INVISIBLE) ); + m_checkboxes[eControl_HostInvisible].SetCheck(checked); + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(inCreativeMode) + { + removeControl( m_checkboxes[eControl_HostFly], true ); + removeControl( m_checkboxes[eControl_HostHunger], true ); + } + else + { + bool canFly = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly); + bool canChangeHunger = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger); + + m_checkboxes[eControl_HostFly].SetEnable(canFly); + checked = canFly && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanFly)!=0; + m_checkboxes[eControl_HostFly].SetText( app.GetString(IDS_CAN_FLY) ); + m_checkboxes[eControl_HostFly].SetCheck(checked); + + m_checkboxes[eControl_HostHunger].SetEnable(canChangeHunger); + checked = canChangeHunger && Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_ClassicHunger)!=0; + m_checkboxes[eControl_HostHunger].SetText( app.GetString(IDS_DISABLE_EXHAUSTION) ); + m_checkboxes[eControl_HostHunger].SetCheck(checked); + } + } + else + { + removeControl( m_checkboxes[eControl_HostInvisible], true ); + removeControl( m_checkboxes[eControl_HostFly], true ); + removeControl( m_checkboxes[eControl_HostHunger], true ); + } + } + else + { + if(localPlayer->IsHost()) + { + // Only host can make people moderators, or enable teleporting for them + m_checkboxes[eControl_Op].SetText( app.GetString(IDS_MODERATOR) ); + m_checkboxes[eControl_Op].SetCheck(Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_Op)!=0); + } + else + { + removeControl( m_checkboxes[eControl_Op], true ); + } + + if(localPlayer->IsHost() && cheats) + { + m_checkboxes[eControl_HostInvisible].SetEnable(true); + bool checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible)!=0; + m_checkboxes[eControl_HostInvisible].SetText( app.GetString(IDS_CAN_INVISIBLE) ); + m_checkboxes[eControl_HostInvisible].SetCheck(checked); + + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(inCreativeMode) + { + removeControl( m_checkboxes[eControl_HostFly], true ); + removeControl( m_checkboxes[eControl_HostHunger], true ); + } + else + { + m_checkboxes[eControl_HostFly].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleFly)!=0; + m_checkboxes[eControl_HostFly].SetText( app.GetString(IDS_CAN_FLY) ); + m_checkboxes[eControl_HostFly].SetCheck(checked); + + m_checkboxes[eControl_HostHunger].SetEnable(true); + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleClassicHunger)!=0; + m_checkboxes[eControl_HostHunger].SetText( app.GetString(IDS_CAN_DISABLE_EXHAUSTION) ); + m_checkboxes[eControl_HostHunger].SetCheck(checked); + } + + checked = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanTeleport)!=0; + m_checkboxes[eControl_CheatTeleport].SetText(app.GetString(IDS_ENABLE_TELEPORT)); + m_checkboxes[eControl_CheatTeleport].SetCheck(checked); + } + else + { + removeControl( m_checkboxes[eControl_HostInvisible], true ); + removeControl( m_checkboxes[eControl_HostFly], true ); + removeControl( m_checkboxes[eControl_HostHunger], true ); + removeControl( m_checkboxes[eControl_CheatTeleport], true ); + } + + // Can only kick people if they are not local, and not local to the host + if(editingPlayer->IsLocal() != TRUE && editingPlayer->IsSameSystem(g_NetworkManager.GetHostPlayer()) != TRUE) + { + m_buttonKick.SetText( app.GetString(IDS_KICK_PLAYER)); + } + else + { + removeControl( m_buttonKick, true ); + } + } + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &CScene_InGamePlayerOptions::OnPlayerChanged, this); + + //SentientManager.RecordMenuShown(m_iPad, eUIScene_CreateWorldMenu, 0); + + resetCheatCheckboxes(); + + return S_OK; +} + +HRESULT CScene_InGamePlayerOptions::OnDestroy() +{ + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &CScene_InGameInfo::OnPlayerChanged, this); + return S_OK; +} + + +HRESULT CScene_InGamePlayerOptions::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + { + bool trustPlayers = app.GetGameHostOption(eGameHostOption_TrustPlayers) != 0; + bool cheats = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + if(m_editingSelf) + { +#if (defined(_CONTENT_PACKAGE) || defined(_FINAL_BUILD) && !defined(_DEBUG_MENUS_ENABLED)) +#else + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode,m_checkboxes[eControl_Op].IsChecked()); +#endif + if(cheats) + { + bool canBeInvisible = Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0; + if(canBeInvisible) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Invisible,m_checkboxes[eControl_HostInvisible].IsChecked()); + if(canBeInvisible) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Invulnerable,m_checkboxes[eControl_HostInvisible].IsChecked()); + + bool inCreativeMode = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CreativeMode) != 0; + if(!inCreativeMode) + { + bool canFly = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly); + bool canChangeHunger = Player::getPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger); + + if(canFly) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanFly,m_checkboxes[eControl_HostFly].IsChecked()); + if(canChangeHunger) Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_ClassicHunger,m_checkboxes[eControl_HostHunger].IsChecked()); + } + } + } + else + { + INetworkPlayer *editingPlayer = g_NetworkManager.GetPlayerBySmallId(m_networkSmallId); + if(!trustPlayers && (editingPlayer != NULL && !editingPlayer->IsHost() ) ) + { + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotMine,!m_checkboxes[eControl_BuildAndMine].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotBuild,!m_checkboxes[eControl_BuildAndMine].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackPlayers,!m_checkboxes[eControl_AttackPlayers].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CannotAttackAnimals, !m_checkboxes[eControl_AttackAnimals].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches, m_checkboxes[eControl_UseDoorsAndSwitches].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanUseContainers, m_checkboxes[eControl_UseContainers].IsChecked()); + } + + INetworkPlayer *localPlayer = g_NetworkManager.GetLocalPlayerByUserIndex( m_iPad ); + + if(localPlayer->IsHost()) + { + if(cheats) + { + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleInvisible,m_checkboxes[eControl_HostInvisible].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleFly,m_checkboxes[eControl_HostFly].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanToggleClassicHunger,m_checkboxes[eControl_HostHunger].IsChecked()); + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_CanTeleport,m_checkboxes[eControl_CheatTeleport].IsChecked()); + } + + Player::setPlayerGamePrivilege(m_playerPrivileges,Player::ePlayerGamePrivilege_Op,m_checkboxes[eControl_Op].IsChecked()); + } + } + unsigned int originalPrivileges = app.GetPlayerPrivileges(m_networkSmallId); + if(originalPrivileges != m_playerPrivileges) + { + // Send update settings packet to server + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + if(player != NULL && player->connection) + { + player->connection->send( shared_ptr( new PlayerInfoPacket( m_networkSmallId, -1, m_playerPrivileges) ) ); + } + } + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + } + break; + } + return S_OK; +} + +HRESULT CScene_InGamePlayerOptions::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + //HRESULT hr = S_OK; + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if( hObjPressed == m_buttonKick ) + { + BYTE *smallId = new BYTE(); + *smallId = m_networkSmallId; + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + StorageManager.RequestMessageBox(IDS_UNLOCK_KICK_PLAYER_TITLE, IDS_UNLOCK_KICK_PLAYER, uiIDA, 2, m_iPad,&CScene_InGamePlayerOptions::KickPlayerReturned,smallId,app.GetStringTable()); + } + else if (hObjPressed == m_checkboxes[eControl_Op] ) + { + resetCheatCheckboxes(); + } + + return S_OK; +} + +HRESULT CScene_InGamePlayerOptions::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +int CScene_InGamePlayerOptions::KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + BYTE smallId = *(BYTE *)pParam; + delete pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr localPlayer = pMinecraft->localplayers[iPad]; + if(localPlayer != NULL && localPlayer->connection) + { + localPlayer->connection->send( shared_ptr( new KickPlayerPacket(smallId) ) ); + } + + // Fix for #61494 - [CRASH]: TU7: Code: Multiplayer: Title may crash while kicking a player from an online game. + // We cannot do a navigate back here is this actually occurs on a thread other than the main thread. On rare occasions this can clash + // with the XUI render and causes a crash. The OnPlayerChanged event should perform the navigate back on the main thread + //app.NavigateBack(iPad); + } + + return 0; +} + +void CScene_InGamePlayerOptions::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + CScene_InGamePlayerOptions *scene = (CScene_InGamePlayerOptions *)callbackParam; + + HXUIOBJ hBackScene = scene->GetBackScene(); + CScene_InGameInfo* infoScene; + VOID *pObj; + XuiObjectFromHandle( hBackScene, &pObj ); + infoScene = (CScene_InGameInfo *)pObj; + if(infoScene != NULL) CScene_InGameInfo::OnPlayerChanged(infoScene,pPlayer,leaving); + + if(leaving && pPlayer != NULL && pPlayer->GetSmallId() == scene->m_networkSmallId) + { + app.NavigateBack(scene->m_iPad); + } +} + +HRESULT CScene_InGamePlayerOptions::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + INetworkPlayer *editingPlayer = g_NetworkManager.GetPlayerBySmallId(m_networkSmallId); + if(editingPlayer != NULL) + { + short colourIndex = app.GetPlayerColour( m_networkSmallId ); + switch(colourIndex) + { + case 1: + m_Icon.PlayVisualRange(L"P1",NULL,L"P1"); + break; + case 2: + m_Icon.PlayVisualRange(L"P2",NULL,L"P2"); + break; + case 3: + m_Icon.PlayVisualRange(L"P3",NULL,L"P3"); + break; + case 4: + m_Icon.PlayVisualRange(L"P4",NULL,L"P4"); + break; + case 5: + m_Icon.PlayVisualRange(L"P5",NULL,L"P5"); + break; + case 6: + m_Icon.PlayVisualRange(L"P6",NULL,L"P6"); + break; + case 7: + m_Icon.PlayVisualRange(L"P7",NULL,L"P7"); + break; + case 8: + m_Icon.PlayVisualRange(L"P8",NULL,L"P8"); + break; + case 9: + m_Icon.PlayVisualRange(L"P9",NULL,L"P9"); + break; + case 10: + m_Icon.PlayVisualRange(L"P10",NULL,L"P10"); + break; + case 11: + m_Icon.PlayVisualRange(L"P11",NULL,L"P11"); + break; + case 12: + m_Icon.PlayVisualRange(L"P12",NULL,L"P12"); + break; + case 13: + m_Icon.PlayVisualRange(L"P13",NULL,L"P13"); + break; + case 14: + m_Icon.PlayVisualRange(L"P14",NULL,L"P14"); + break; + case 15: + m_Icon.PlayVisualRange(L"P15",NULL,L"P15"); + break; + case 0: + default: + m_Icon.PlayVisualRange(L"P0",NULL,L"P0"); + break; + }; + } + } + return S_OK; +} + +void CScene_InGamePlayerOptions::removeControl(HXUIOBJ hObjToRemove, bool center) +{ + D3DXVECTOR3 pos; + float fControlHeight, fTempHeight, fWidth; + + bool changeFocus = m_focusElement == hObjToRemove; + + XuiElementGetBounds(hObjToRemove,&fWidth,&fControlHeight); + + // Hide this control + XuiControlSetEnable(hObjToRemove, FALSE); + XuiElementSetShow(hObjToRemove, FALSE); + + // Move future downwards nav up + HXUIOBJ controlToMove = hObjToRemove; + while(controlToMove = XuiControlGetNavigation(controlToMove, XUI_CONTROL_NAVIGATE_DOWN, FALSE, TRUE) ) + { + if(changeFocus && XuiElementIsShown(controlToMove)) + { + m_focusElement = controlToMove; + XuiElementSetUserFocus( controlToMove, m_iPad ); + changeFocus = FALSE; + } + XuiElementGetPosition(controlToMove, &pos); + pos.y -= fControlHeight; + XuiElementSetPosition(controlToMove, &pos); + } + + // Resize and move scene + GetBounds(&fWidth, &fTempHeight); + SetBounds(fWidth, fTempHeight - fControlHeight); + + GetPosition(&pos); + pos.y += fControlHeight/2; + SetPosition(&pos); +} + +void CScene_InGamePlayerOptions::resetCheatCheckboxes() +{ + bool isModerator = m_checkboxes[eControl_Op].IsChecked(); + //bool cheatsEnabled = app.GetGameHostOption(eGameHostOption_CheatsEnabled) != 0; + + if (!m_editingSelf) + { + m_checkboxes[eControl_HostInvisible].SetEnable(isModerator); + m_checkboxes[eControl_HostInvisible].SetCheck( isModerator + && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleInvisible) != 0) ); + + // NOT CREATIVE MODE. + { + m_checkboxes[eControl_HostFly].SetEnable(isModerator); + m_checkboxes[eControl_HostFly].SetCheck( isModerator + && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleFly) != 0) ); + + m_checkboxes[eControl_HostHunger].SetEnable(isModerator); + m_checkboxes[eControl_HostHunger].SetCheck( isModerator + && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanToggleClassicHunger) != 0) ); + } + + m_checkboxes[eControl_CheatTeleport].SetEnable(isModerator); + m_checkboxes[eControl_CheatTeleport].SetCheck( isModerator + && (Player::getPlayerGamePrivilege(m_playerPrivileges, Player::ePlayerGamePrivilege_CanTeleport) != 0) ); + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.h b/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.h new file mode 100644 index 0000000..87b9de1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_InGamePlayerOptions.h @@ -0,0 +1,96 @@ +#pragma once +#include "..\Media\xuiscene_ingame_player_options.h" + +class CScene_InGamePlayerOptions : public CXuiSceneImpl +{ +private: + enum EControls + { + // Checkboxes + eControl_BuildAndMine, + eControl_UseDoorsAndSwitches, + eControl_UseContainers, + eControl_AttackPlayers, + eControl_AttackAnimals, + eControl_Op, + eControl_CheatTeleport, + eControl_HostFly, + eControl_HostHunger, + eControl_HostInvisible, + + eControl_CHECKBOXES_COUNT, + + // Others + eControl_Kick = eControl_CHECKBOXES_COUNT, + }; + +protected: + HXUIOBJ m_focusElement; // Only used for the remove control process + + CXuiControl m_Icon; + CXuiControl m_Gamertag; + CXuiScene m_TeleportGroup; + CXuiControl m_buttonKick; + CXuiCheckbox m_checkboxes[eControl_CHECKBOXES_COUNT]; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START( OnTransitionStart ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Icon, m_Icon) + MAP_CONTROL(IDC_Gamertag, m_Gamertag) + + MAP_CONTROL(IDC_CheckboxBuildAndMine, m_checkboxes[eControl_BuildAndMine]) + MAP_CONTROL(IDC_CheckboxAttackPlayers, m_checkboxes[eControl_AttackPlayers]) + MAP_CONTROL(IDC_CheckboxAttackAnimals, m_checkboxes[eControl_AttackAnimals]) + MAP_CONTROL(IDC_CheckboxUseContainers, m_checkboxes[eControl_UseContainers]) + MAP_CONTROL(IDC_CheckboxUseDoorsAndSwitches, m_checkboxes[eControl_UseDoorsAndSwitches]) + MAP_CONTROL(IDC_CheckboxOp, m_checkboxes[eControl_Op]) + MAP_CONTROL(IDC_CheckboxTeleport, m_checkboxes[eControl_CheatTeleport]) + MAP_CONTROL(IDC_CheckboxHostInvisible, m_checkboxes[eControl_HostInvisible]) + MAP_CONTROL(IDC_CheckboxHostFly, m_checkboxes[eControl_HostFly]) + MAP_CONTROL(IDC_CheckboxHostHunger, m_checkboxes[eControl_HostHunger]) + + MAP_CONTROL(IDC_ButtonKick, m_buttonKick) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_InGamePlayerOptions, L"CScene_InGamePlayerOptions", XUI_CLASS_SCENE ) + + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + +private: + bool m_editingSelf; + int m_iPad; + BYTE m_networkSmallId; + unsigned int m_playerPrivileges; + D3DXVECTOR3 m_OriginalPosition; + + void removeControl(HXUIOBJ hObjToRemove, bool center); + + /** 4J-JEV: + For enabling/disabling 'Can Fly', 'Can Teleport', 'Can Disable Hunger' etc + used after changing the moderator checkbox. + */ + void resetCheatCheckboxes(); + +public: + static int KickPlayerReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Intro.cpp b/Minecraft.Client/Common/XUI/XUI_Intro.cpp new file mode 100644 index 0000000..997abe5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Intro.cpp @@ -0,0 +1,153 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\XUI\XUI_Intro.h" + +#define TIMELINE_NORMAL 0 +#define TIMELINE_ESRBFADE 1 +#define TIMELINE_LOGOSFADE 2 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Intro::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + // We may need to display a ratings image for a while at the start... + m_bWantsToSkip=false; + m_iTimeline=TIMELINE_NORMAL; + + // 4J-PB - We can't check to see if the version is a trial or full game until after 5 seconds... + // The reason that this is a requirement is that there is a problem that occasionally happens *only* in the production + // environment (not partnernet or cert), where if you dont wait 5 seconds, you can run into an issue where the timing + // of the call fails and the game is always identified as being the trial version even if you have upgraded to the full version. + // -Joe Dunavant + + // start a timer for the required 5 seconds, plus an extra bit to allow the lib timer to enable the xcontent license check call +#ifdef _CONTENT_PACKAGE + m_bSkippable=false; + XuiSetTimer( m_hObj,0,5200); +#else + m_bSkippable=true; +#endif + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Intro::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + + return S_OK; +} + +HRESULT CScene_Intro::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + static bool bPressed=false; + + if(bPressed==false) + { + if(m_bSkippable) + { + // stop the animation + XuiElementStopTimeline(m_hObj,TRUE); + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_SaveMessage); + app.SetIntroRunning(false); + } + else + { + m_bWantsToSkip=true; + } + + bPressed=true; + } + + return S_OK; +} + +HRESULT CScene_Intro::OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled) +{ + int nStart, nEnd; + + if(m_bSkippable && m_bWantsToSkip) + { + // straight to the game + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_SaveMessage); + app.SetIntroRunning(false); + } + else + { + switch(m_iTimeline) + { + case TIMELINE_NORMAL: + { + // 4J-PB - lots of discussions over this because Brazil is in the NA region. This is what I have been advised to do... + //if(ProfileManager.RegionIsNorthAmerica()) + if(ProfileManager.LocaleIsUSorCanada()) + { + m_iTimeline=TIMELINE_ESRBFADE; + XuiElementFindNamedFrame( m_hObj, L"ESRBFade", &nStart ); + XuiElementFindNamedFrame( m_hObj, L"ESRBFadeEnd", &nEnd ); + XuiElementPlayTimeline( m_hObj, nStart, nStart, nEnd, FALSE, TRUE ); + } + else + { + m_iTimeline=TIMELINE_LOGOSFADE; + XuiElementFindNamedFrame( m_hObj, L"StartFade", &nStart ); + XuiElementFindNamedFrame( m_hObj, L"EndFade", &nEnd ); + XuiElementPlayTimeline( m_hObj, nStart, nStart, nEnd, FALSE, TRUE ); + } + } + break; + + case TIMELINE_ESRBFADE: + if(m_bWantsToSkip && m_bSkippable) + { + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_SaveMessage); + app.SetIntroRunning(false); + } + else + { + m_iTimeline=TIMELINE_LOGOSFADE; + XuiElementFindNamedFrame( m_hObj, L"StartFade", &nStart ); + XuiElementFindNamedFrame( m_hObj, L"EndFade", &nEnd ); + XuiElementPlayTimeline( m_hObj, nStart, nStart, nEnd, FALSE, TRUE ); + } + break; + case TIMELINE_LOGOSFADE: + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_SaveMessage); + app.SetIntroRunning(false); + break; + } + } + + return S_OK; +} + + +HRESULT CScene_Intro::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + HRESULT hr=XuiKillTimer(m_hObj,0); + m_bSkippable=true; + + if(m_bWantsToSkip) + { + // stop the animation + XuiElementStopTimeline(m_hObj,TRUE); + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_SaveMessage); + app.SetIntroRunning(false); + } + + return hr; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Intro.h b/Minecraft.Client/Common/XUI/XUI_Intro.h new file mode 100644 index 0000000..f896fae --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Intro.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../media/xuiscene_intro.h" + +class CScene_Intro : public CXuiSceneImpl +{ + protected: + CXuiScene m_Scene; + CXuiControl m_4jlogo; + CXuiElement m_grpXbox; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_TIMELINE_END(OnTimelineEnd) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + //MAP_CONTROL(IDC_LogoGroup, m_grpXbox) + //BEGIN_MAP_CHILD_CONTROLS(m_grpXbox) + MAP_CONTROL(IDC_Logo4J, m_4jlogo) + //END_MAP_CHILD_CONTROLS() + + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + + bool m_bSkippable; + bool m_bWantsToSkip; + int m_iTimeline; +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Intro, L"CScene_Intro", XUI_CLASS_SCENE ) + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Leaderboards.cpp b/Minecraft.Client/Common/XUI/XUI_Leaderboards.cpp new file mode 100644 index 0000000..428b3e8 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Leaderboards.cpp @@ -0,0 +1,1202 @@ +#include "stdafx.h" +#include +#include +#include "XUI_Leaderboards.h" +#include "..\..\..\Minecraft.World\Tile.h" +#include "..\..\..\Minecraft.World\Item.h" +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "XUI_XZP_Icons.h" + +LPCWSTR CScene_Leaderboards::m_TitleIconNameA[7]= +{ + L"XuiHSlot1", + L"XuiHSlot2", + L"XuiHSlot3", + L"XuiHSlot4", + L"XuiHSlot5", + L"XuiHSlot6", + L"XuiHSlot7", +}; + +LPCWSTR CScene_Leaderboards::m_TextColumnNameA[7]= +{ + L"text_Column1", + L"text_Column2", + L"text_Column3", + L"text_Column4", + L"text_Column5", + L"text_Column6", + L"text_Column7", +}; + + +// if the value is greater than 511, it's an xzp icon that needs displayed, rather than the game icon +const int CScene_Leaderboards::TitleIcons[CScene_Leaderboards::NUM_LEADERBOARDS][7] = +{ + { XZP_ICON_WALKED, XZP_ICON_FALLEN, Item::minecart_Id, Item::boat_Id, NULL }, + { Tile::dirt_Id, Tile::stoneBrick_Id, Tile::sand_Id, Tile::rock_Id, Tile::gravel_Id, Tile::clay_Id, Tile::obsidian_Id }, + { Item::egg_Id, Item::wheat_Id, Tile::mushroom1_Id, Tile::reeds_Id, Item::milk_Id, Tile::pumpkin_Id, NULL }, + { XZP_ICON_ZOMBIE, XZP_ICON_SKELETON, XZP_ICON_CREEPER, XZP_ICON_SPIDER, XZP_ICON_SPIDERJOCKEY, XZP_ICON_ZOMBIEPIGMAN, XZP_ICON_SLIME }, +}; + +const int CScene_Leaderboards::LEADERBOARD_HEADERS[CScene_Leaderboards::NUM_LEADERBOARDS][4] = { + { SPASTRING_LB_TRAVELLING_PEACEFUL_NAME, SPASTRING_LB_TRAVELLING_EASY_NAME, SPASTRING_LB_TRAVELLING_NORMAL_NAME, SPASTRING_LB_TRAVELLING_HARD_NAME }, + { SPASTRING_LB_MINING_BLOCKS_PEACEFUL_NAME, SPASTRING_LB_MINING_BLOCKS_EASY_NAME, SPASTRING_LB_MINING_BLOCKS_NORMAL_NAME, SPASTRING_LB_MINING_BLOCKS_HARD_NAME }, + { SPASTRING_LB_FARMING_PEACEFUL_NAME, SPASTRING_LB_FARMING_EASY_NAME, SPASTRING_LB_FARMING_NORMAL_NAME, SPASTRING_LB_FARMING_HARD_NAME }, + { NULL, SPASTRING_LB_KILLS_EASY_NAME, SPASTRING_LB_KILLS_NORMAL_NAME, SPASTRING_LB_KILLS_HARD_NAME }, +}; + +const CScene_Leaderboards::LeaderboardDescriptor CScene_Leaderboards::LEADERBOARD_DESCRIPTORS[CScene_Leaderboards::NUM_LEADERBOARDS][4] = { + { + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_TRAVELLING_PEACEFUL, 4, STATS_COLUMN_TRAVELLING_PEACEFUL_WALKED, STATS_COLUMN_TRAVELLING_PEACEFUL_FALLEN, STATS_COLUMN_TRAVELLING_PEACEFUL_MINECART, STATS_COLUMN_TRAVELLING_PEACEFUL_BOAT, NULL, NULL, NULL,NULL), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_TRAVELLING_EASY, 4, STATS_COLUMN_TRAVELLING_EASY_WALKED, STATS_COLUMN_TRAVELLING_EASY_FALLEN, STATS_COLUMN_TRAVELLING_EASY_MINECART, STATS_COLUMN_TRAVELLING_EASY_BOAT, NULL, NULL, NULL,NULL), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_TRAVELLING_NORMAL, 4, STATS_COLUMN_TRAVELLING_NORMAL_WALKED, STATS_COLUMN_TRAVELLING_NORMAL_FALLEN, STATS_COLUMN_TRAVELLING_NORMAL_MINECART, STATS_COLUMN_TRAVELLING_NORMAL_BOAT, NULL, NULL, NULL,NULL), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_TRAVELLING_HARD, 4, STATS_COLUMN_TRAVELLING_HARD_WALKED, STATS_COLUMN_TRAVELLING_HARD_FALLEN, STATS_COLUMN_TRAVELLING_HARD_MINECART, STATS_COLUMN_TRAVELLING_HARD_BOAT, NULL, NULL, NULL,NULL), + }, + { + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_MINING_BLOCKS_PEACEFUL, 7, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_DIRT, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_STONE, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_SAND, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_COBBLESTONE, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_GRAVEL, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_CLAY, STATS_COLUMN_MINING_BLOCKS_PEACEFUL_OBSIDIAN,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_MINING_BLOCKS_EASY, 7, STATS_COLUMN_MINING_BLOCKS_EASY_DIRT, STATS_COLUMN_MINING_BLOCKS_EASY_STONE, STATS_COLUMN_MINING_BLOCKS_EASY_SAND, STATS_COLUMN_MINING_BLOCKS_EASY_COBBLESTONE, STATS_COLUMN_MINING_BLOCKS_EASY_GRAVEL, STATS_COLUMN_MINING_BLOCKS_EASY_CLAY, STATS_COLUMN_MINING_BLOCKS_EASY_OBSIDIAN,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_MINING_BLOCKS_NORMAL, 7, STATS_COLUMN_MINING_BLOCKS_NORMAL_DIRT, STATS_COLUMN_MINING_BLOCKS_NORMAL_STONE, STATS_COLUMN_MINING_BLOCKS_NORMAL_SAND, STATS_COLUMN_MINING_BLOCKS_NORMAL_COBBLESTONE, STATS_COLUMN_MINING_BLOCKS_NORMAL_GRAVEL, STATS_COLUMN_MINING_BLOCKS_NORMAL_CLAY, STATS_COLUMN_MINING_BLOCKS_NORMAL_OBSIDIAN,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_MINING_BLOCKS_HARD, 7, STATS_COLUMN_MINING_BLOCKS_HARD_DIRT, STATS_COLUMN_MINING_BLOCKS_HARD_STONE, STATS_COLUMN_MINING_BLOCKS_HARD_SAND, STATS_COLUMN_MINING_BLOCKS_HARD_COBBLESTONE, STATS_COLUMN_MINING_BLOCKS_HARD_GRAVEL, STATS_COLUMN_MINING_BLOCKS_HARD_CLAY, STATS_COLUMN_MINING_BLOCKS_HARD_OBSIDIAN,NULL ), + }, + { + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_FARMING_PEACEFUL, 6, STATS_COLUMN_FARMING_PEACEFUL_EGGS, STATS_COLUMN_FARMING_PEACEFUL_WHEAT, STATS_COLUMN_FARMING_PEACEFUL_MUSHROOMS, STATS_COLUMN_FARMING_PEACEFUL_SUGARCANE, STATS_COLUMN_FARMING_PEACEFUL_MILK, STATS_COLUMN_FARMING_PEACEFUL_PUMPKINS, NULL,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_FARMING_EASY, 6, STATS_COLUMN_FARMING_EASY_EGGS, STATS_COLUMN_FARMING_PEACEFUL_WHEAT, STATS_COLUMN_FARMING_EASY_MUSHROOMS, STATS_COLUMN_FARMING_EASY_SUGARCANE, STATS_COLUMN_FARMING_EASY_MILK, STATS_COLUMN_FARMING_EASY_PUMPKINS, NULL,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_FARMING_NORMAL, 6, STATS_COLUMN_FARMING_NORMAL_EGGS, STATS_COLUMN_FARMING_NORMAL_WHEAT, STATS_COLUMN_FARMING_NORMAL_MUSHROOMS, STATS_COLUMN_FARMING_NORMAL_SUGARCANE, STATS_COLUMN_FARMING_NORMAL_MILK, STATS_COLUMN_FARMING_NORMAL_PUMPKINS, NULL,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_FARMING_HARD, 6, STATS_COLUMN_FARMING_HARD_EGGS, STATS_COLUMN_FARMING_HARD_WHEAT, STATS_COLUMN_FARMING_HARD_MUSHROOMS, STATS_COLUMN_FARMING_HARD_SUGARCANE, STATS_COLUMN_FARMING_HARD_MILK, STATS_COLUMN_FARMING_HARD_PUMPKINS, NULL,NULL ), + }, + { + CScene_Leaderboards::LeaderboardDescriptor( NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_KILLS_EASY, 7, STATS_COLUMN_KILLS_EASY_ZOMBIES, STATS_COLUMN_KILLS_EASY_SKELETONS, STATS_COLUMN_KILLS_EASY_CREEPERS, STATS_COLUMN_KILLS_EASY_SPIDERS, STATS_COLUMN_KILLS_EASY_SPIDERJOCKEYS, STATS_COLUMN_KILLS_EASY_ZOMBIEPIGMEN, STATS_COLUMN_KILLS_EASY_SLIME,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_KILLS_NORMAL, 7, STATS_COLUMN_KILLS_NORMAL_ZOMBIES, STATS_COLUMN_KILLS_NORMAL_SKELETONS, STATS_COLUMN_KILLS_NORMAL_CREEPERS, STATS_COLUMN_KILLS_NORMAL_SPIDERS, STATS_COLUMN_KILLS_NORMAL_SPIDERJOCKEYS, STATS_COLUMN_KILLS_NORMAL_ZOMBIEPIGMEN, STATS_COLUMN_KILLS_NORMAL_SLIME,NULL ), + CScene_Leaderboards::LeaderboardDescriptor( STATS_VIEW_KILLS_HARD, 7, STATS_COLUMN_KILLS_HARD_ZOMBIES, STATS_COLUMN_KILLS_HARD_SKELETONS, STATS_COLUMN_KILLS_HARD_CREEPERS, STATS_COLUMN_KILLS_HARD_SPIDERS, STATS_COLUMN_KILLS_HARD_SPIDERJOCKEYS, STATS_COLUMN_KILLS_HARD_ZOMBIEPIGMEN, STATS_COLUMN_KILLS_HARD_SLIME,NULL ), + }, +}; + +HRESULT CScene_Leaderboards::OnInit(XUIMessageInit *pInitData, BOOL &bHandled) +{ + m_iPad = *(int *)pInitData->pvInitData; + MapChildControls(); + m_bReady=false; + + // if we're not in the game, we need to use basescene 0 + if(Minecraft::GetInstance()->level==NULL) + { + m_iPad=DEFAULT_XUI_MENU_USER; + } + + m_bPopulatedOnce = false; + + ui.SetTooltips(m_iPad,-1, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGE_FILTER, -1); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + + m_friends = NULL; + m_numFriends = 0; + m_filteredFriends = NULL; + m_numFilteredFriends = 0; + + m_newTop = m_newSel = -1; + + m_isProcessingStatsRead = false; + + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + LeaderboardManager::Instance()->OpenSession(); + + //GetFriends(); + + m_currentLeaderboard = 0; + m_currentDifficulty = 2; + SetLeaderboardHeader(); + + m_currentFilter = LeaderboardManager::eFM_Friends; + wchar_t filterBuffer[40]; + swprintf(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_FRIENDS)); + m_textFilter.SetText(filterBuffer); + + wchar_t entriesBuffer[40]; + swprintf(entriesBuffer, 40, L"%ls%i", app.GetString(IDS_LEADERBOARD_ENTRIES), 0); + m_textEntries.SetText(entriesBuffer); + + ReadStats(-1); + + // title icons + for(int i=0;i<7;i++) + { + m_pHTitleIconSlots[i]=NULL; + m_fTitleIconXPositions[i]=0.0f; + m_fTextXPositions[i]=0.0f; + m_hTextEntryA[i]=NULL; + } + + + bHandled = TRUE; + return S_OK; +} + + +void CScene_Leaderboards::Reposition(int iNumber) +{ + float fIconSize; // including gap + float fNewIconIncrement; + D3DXVECTOR3 vPos; + + fIconSize=(m_fTitleIconXPositions[6]-m_fTitleIconXPositions[0])/6.0f; + fNewIconIncrement=(fIconSize*7.0f)/(float)iNumber; + + // reposition the title icons based on the number there are + for(int i=0;iGetPosition(&vPos); + vPos.x=m_fTitleIconXPositions[0]+(((float)i)*fNewIconIncrement)+(fNewIconIncrement-fIconSize)/2.0f; + m_pHTitleIconSlots[i]->SetPosition(&vPos); + } +} + +void CScene_Leaderboards::RepositionText(int iNumber) +{ + float fTextSize; // including gap + float fNewTextIncrement; + D3DXVECTOR3 vPos; + + fTextSize=(m_fTextXPositions[6]-m_fTextXPositions[0])/6.0f; + fNewTextIncrement=(fTextSize*7.0f)/(float)iNumber; + + // reposition the title icons based on the number there are + for(int i=0;iCancelOperation(); + LeaderboardManager::Instance()->CloseSession(); + + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); + + while( m_isProcessingStatsRead ) + { + Sleep( 10 ); + } + + if( m_friends != NULL ) + delete [] m_friends; + + if( m_filteredFriends != NULL ) + delete [] m_filteredFriends; + + return S_OK; +} + +HRESULT CScene_Leaderboards::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + UpdateTooltips(); + return S_OK; +} + +HRESULT CScene_Leaderboards::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + + if(m_bReady && pNotifySelChangedData->iOldItem!=-1) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + + return S_OK; +} + +void CScene_Leaderboards::UpdateTooltips() +{ + int iTooltipFriendRequest=-1; + int iTooltipGamerCardOrProfile=-1; + if( m_leaderboard.m_currentEntryCount > 0 ) + { + unsigned int selection = (unsigned int)m_listGamers.GetCurSel(); + + // if the selected user is me, don't show Send Friend Request, and show the gamer profile, not the gamer card + + // Check that the index is actually within range of the data we've got before accessing the m_leaderboard.m_entries array + int idx = selection - (m_leaderboard.m_entryStartIndex-1); + if( ( idx < 0 ) || ( idx >= NUM_ENTRIES ) ) + { + return; + } + if(m_leaderboard.m_entries[idx].m_bPlayer) + { + iTooltipGamerCardOrProfile=IDS_TOOLTIPS_VIEW_GAMERPROFILE; + } + else + { + iTooltipGamerCardOrProfile=IDS_TOOLTIPS_VIEW_GAMERCARD; + // if we're on the friends filter, then don't show the Send Friend Request + bool bIsFriend = m_currentFilter == LeaderboardManager::eFM_Friends; + + if(!bIsFriend) + { + // check the entry we're on + if( m_leaderboard.m_currentEntryCount > 0 ) + { + if( selection >= m_leaderboard.m_entryStartIndex-1 && + selection < (m_leaderboard.m_entryStartIndex+m_leaderboard.m_currentEntryCount-1) ) + { + if( (m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1)].m_bFriend==false) && (m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1)].m_bRequestedFriend==false)) + { + iTooltipFriendRequest=IDS_TOOLTIPS_SEND_FRIEND_REQUEST; + } + } + } + } + } + } + + // 4J-PB - no room on the screen for the LT/RT prompt + /* + if(m_leaderboard.m_currentEntryCount>11) + { + ui.SetTooltips(m_iPad, iTooltipFriendRequest, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGE_FILTER, iTooltipGamerCardOrProfile, IDS_TOOLTIPS_PAGEUP, IDS_TOOLTIPS_PAGEDOWN); + } + else*/ + { + ui.SetTooltips(m_iPad, iTooltipFriendRequest, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGE_FILTER, iTooltipGamerCardOrProfile); + } +} + +HRESULT CScene_Leaderboards::OnKeyDown(XUIMessageInput* pInputData, BOOL& bHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + case VK_PAD_RSHOULDER: + case VK_PAD_LSHOULDER: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if(m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + if( pInputData->dwKeyCode == VK_PAD_RSHOULDER ) + { + ++m_currentDifficulty; + if( m_currentDifficulty == 4 ) + m_currentDifficulty = 0; + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 1; + } + else + { + if( m_currentDifficulty == 0 ) + m_currentDifficulty = 4; + --m_currentDifficulty; + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 3; + } + + SetLeaderboardHeader(); + ClearLeaderboardTitlebar(); + + ReadStats(-1); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + + bHandled = TRUE; + } + break; + case VK_PAD_LTHUMB_RIGHT: + case VK_PAD_LTHUMB_LEFT: + case VK_PAD_DPAD_LEFT: + case VK_PAD_DPAD_RIGHT: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if ( m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + m_bReady=false; + if(( pInputData->dwKeyCode == VK_PAD_LTHUMB_RIGHT ) ||(pInputData->dwKeyCode == VK_PAD_DPAD_RIGHT)) + { + ++m_currentLeaderboard; + if( m_currentLeaderboard == NUM_LEADERBOARDS ) + m_currentLeaderboard = 0; + } + else + { + if( m_currentLeaderboard == 0 ) + m_currentLeaderboard = NUM_LEADERBOARDS; + --m_currentLeaderboard; + } + + if( m_currentLeaderboard == LEADERBOARD_KILLS_POSITION && m_currentDifficulty == 0 ) + m_currentDifficulty = 1; + + SetLeaderboardHeader(); + ClearLeaderboardTitlebar(); + + + ReadStats(-1); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + bHandled = TRUE; + } + break; + case VK_PAD_LTRIGGER: + case VK_PAD_RTRIGGER: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if( m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + if( m_leaderboard.m_totalEntryCount <= 10 ) + break; + + if( pInputData->dwKeyCode == VK_PAD_LTRIGGER ) + { + m_newTop = m_listGamers.GetTopItem() - 10; + if( m_newTop < 0 ) + m_newTop = 0; + + m_newSel = m_newTop; + } + else + { + m_newTop = m_listGamers.GetTopItem() + 10; + if( m_newTop+10 > (int)m_leaderboard.m_totalEntryCount ) + { + m_newTop = m_leaderboard.m_totalEntryCount - 10; + if( m_newTop < 0 ) + m_newTop = 0; + } + + m_newSel = m_newTop; + } + //CXuiSceneBase::PlayUISFX(eSFX_Press); + } + bHandled = TRUE; + } + break; + case VK_PAD_X: + { + //Do nothing if a stats read is currently in progress, otherwise the system complains about to many read requests + if( m_bPopulatedOnce && LeaderboardManager::Instance()->isIdle() ) + { + switch( m_currentFilter ) + { + case LeaderboardManager::eFM_Friends: + { + m_currentFilter = LeaderboardManager::eFM_MyScore; + wchar_t filterBuffer[40]; + swprintf_s(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_MYSCORE)); + m_textFilter.SetText(filterBuffer); + } + break; + case LeaderboardManager::eFM_MyScore: + { + m_currentFilter = LeaderboardManager::eFM_TopRank; + wchar_t filterBuffer[40]; + swprintf_s(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_OVERALL)); + m_textFilter.SetText(filterBuffer); + } + break; + case LeaderboardManager::eFM_TopRank: + { + m_currentFilter = LeaderboardManager::eFM_Friends; + wchar_t filterBuffer[40]; + swprintf_s(filterBuffer, 40, L"%ls%ls", app.GetString(IDS_LEADERBOARD_FILTER), app.GetString(IDS_LEADERBOARD_FILTER_FRIENDS)); + m_textFilter.SetText(filterBuffer); + } + break; + } + + ReadStats(-1); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + bHandled = TRUE; + } + break; + case VK_PAD_Y: + { + //Show gamercard + if( m_leaderboard.m_currentEntryCount > 0 ) + { + unsigned int selection = (unsigned int)m_listGamers.GetCurSel(); + if( selection >= m_leaderboard.m_entryStartIndex-1 && + selection < (m_leaderboard.m_entryStartIndex+m_leaderboard.m_currentEntryCount-1) ) + { + PlayerUID xuid = m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1)].m_xuid; + if( xuid != INVALID_XUID ) + { + XShowGamerCardUI(ProfileManager.GetLockedProfile(), xuid); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + } + } + bHandled = TRUE; + } + break; + case VK_PAD_A: + { + //Send friend request if the filter mode is not friend, and they're not a friend or a pending friend + if( m_currentFilter != LeaderboardManager::eFM_Friends ) + { + if( m_leaderboard.m_currentEntryCount > 0 ) + { + unsigned int selection = (unsigned int)m_listGamers.GetCurSel(); + if( selection >= m_leaderboard.m_entryStartIndex-1 && + selection < (m_leaderboard.m_entryStartIndex+m_leaderboard.m_currentEntryCount-1) ) + { + //If not the player and neither currently a friend or requested to be a friend + if( !m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_bPlayer && + !m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_bFriend && + !m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_bRequestedFriend ) + { + PlayerUID xuid = m_leaderboard.m_entries[selection - (m_leaderboard.m_entryStartIndex-1) ].m_xuid; + if( xuid != INVALID_XUID ) + { + XShowFriendRequestUI(ProfileManager.GetLockedProfile(), xuid); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + } + } + } + } + bHandled = TRUE; + } + break; + case VK_PAD_B: + case VK_ESCAPE: + { + BYTE userIndex = pInputData->UserIndex; + if( !app.IsPauseMenuDisplayed(userIndex) ) + { + // If we are not from a pause menu, then we are from the main menu + userIndex = XUSER_INDEX_ANY; + } + + app.NavigateBack(userIndex); + bHandled = TRUE; + } + break; + } + return S_OK; +} + +// DELETING // +#if 0 +void CScene_Leaderboards::GetFriends() +{ + DWORD resultsSize; + HANDLE hEnumerator; + DWORD ret; + + m_numFriends = 0; + + //First, get a list of (up to 100) friends (this is the maximum that the enumerator currently supports) + ret = XFriendsCreateEnumerator( ProfileManager.GetLockedProfile(), 0, 100, &resultsSize, &hEnumerator); + if( ret != ERROR_SUCCESS ) + return; + + m_friends = (XONLINE_FRIEND*) new BYTE[ resultsSize ]; + DWORD numFriends; + + ret = XEnumerate( + hEnumerator, + m_friends, + resultsSize, + &numFriends, + NULL ); + + if( ret != ERROR_SUCCESS ) + numFriends = 0; + + m_numFriends = numFriends; + + m_filteredFriends = new PlayerUID[m_numFriends+1]; + + m_numFilteredFriends = 0; + for( unsigned int friendIndex=0 ; friendIndexGetMyXUID(); +} +#endif + +void CScene_Leaderboards::ReadStats(int startIndex) +{ + //If startIndex == -1, then use default values + if( startIndex == -1 ) + { + m_newEntryIndex = 1; + m_newReadSize = READ_SIZE; + + m_leaderboard.m_totalEntryCount = 0; + m_leaderboard.m_currentEntryCount = 0; + + m_listGamers.DeleteItems(0, m_listGamers.GetItemCount()); + } + else + { + m_newEntryIndex = (unsigned int)startIndex; + m_newReadSize = min((int)READ_SIZE, (int)m_leaderboard.m_totalEntryCount-(startIndex-1)); + } + + //Setup the spec structure for the read request + /* XUSER_STATS_SPEC* spec = new XUSER_STATS_SPEC[1]; // 4j-jev, moved into xboxLeaderboardManager + spec[0].dwViewId = LEADERBOARD_DESCRIPTORS[m_currentLeaderboard][m_currentDifficulty].m_viewId; + spec[0].dwNumColumnIds = LEADERBOARD_DESCRIPTORS[m_currentLeaderboard][m_currentDifficulty].m_columnCount; + for( unsigned int i=0 ; iReadStats( + m_newEntryIndex, + m_newReadSize, + 1, + spec, + (startIndex == -1) ? m_currentFilter : LeaderboardManager::eFM_TopRank, + CScene_Leaderboards::OnStatsReadComplete, + reinterpret_cast(this), + m_numFilteredFriends, + m_filteredFriends);*/ + + switch (startIndex == -1 ? m_currentFilter : LeaderboardManager::eFM_TopRank) + { + case LeaderboardManager::eFM_TopRank: + LeaderboardManager::Instance()->ReadStats_TopRank( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + m_newEntryIndex, m_newReadSize + ); + break; + case LeaderboardManager::eFM_MyScore: + LeaderboardManager::Instance()->ReadStats_MyScore( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + INVALID_XUID/*ignored*/, + m_newReadSize + ); + break; + case LeaderboardManager::eFM_Friends: + LeaderboardManager::Instance()->ReadStats_Friends( this, + m_currentDifficulty, (LeaderboardManager::EStatsType) m_currentLeaderboard, + INVALID_XUID /*ignored*/, + 0 /*ignored*/, 0 /*ignored*/ + ); + break; + } + + //Show the loading message + m_textInfo.SetText(app.GetString(IDS_LEADERBOARD_LOADING)); + m_textInfo.SetShow(true); +} + +bool CScene_Leaderboards::OnStatsReadComplete(bool success, int numResults, LeaderboardManager::ViewOut results) +{ + //CScene_Leaderboards* scene = reinterpret_cast(userdata); + + m_isProcessingStatsRead = true; + + //bool noResults = LeaderboardManager::Instance()->GetStatsState() != XboxLeaderboardManager::eStatsState_Ready; + bool ret; + + if (success) + { + m_numStats = numResults; + m_stats = results; + ret = RetrieveStats(); + } + else ret = true; + + //else LeaderboardManager::Instance()->SetStatsRetrieved(false); + + PopulateLeaderboard(!success); + + m_isProcessingStatsRead = false; + + return ret; +} + + +bool CScene_Leaderboards::RetrieveStats() +{ + + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<SetStatsRetrieved(true); + + return true; + } + + //assert( LeaderboardManager::Instance()->GetStats() != NULL ); + //PXUSER_STATS_READ_RESULTS stats = LeaderboardManager::Instance()->GetStats(); + //if( m_currentFilter == LeaderboardManager::eFM_Friends ) LeaderboardManager::Instance()->SortFriendStats(); + + bool isDistanceLeaderboard = m_stats->pViews[0].dwViewId == STATS_VIEW_TRAVELLING_PEACEFUL || m_stats->pViews[0].dwViewId == STATS_VIEW_TRAVELLING_EASY || m_stats->pViews[0].dwViewId == STATS_VIEW_TRAVELLING_NORMAL || m_stats->pViews[0].dwViewId == STATS_VIEW_TRAVELLING_HARD; + + //First read + if( m_leaderboard.m_totalEntryCount == 0 ) + { + m_leaderboard.m_totalEntryCount = (m_currentFilter == LeaderboardManager::eFM_Friends) ? m_stats->pViews[0].dwNumRows : m_stats->pViews[0].dwTotalViewRows; + m_leaderboard.m_currentEntryCount = m_stats->pViews[0].dwNumRows; + + if( m_leaderboard.m_totalEntryCount == 0 || m_leaderboard.m_currentEntryCount == 0 ) + { + //LeaderboardManager::Instance()->SetStatsRetrieved(false); + return false; + } + + m_leaderboard.m_numColumns = m_stats->pViews[0].pRows[0].dwNumColumns; + + m_leaderboard.m_entryStartIndex = (m_currentFilter == LeaderboardManager::eFM_Friends) ? 1 : m_stats->pViews[0].pRows[0].dwRank; + + for( unsigned int entryIndex=0 ; entryIndexpViews[0].pRows[entryIndex]), &(m_leaderboard.m_entries[entryIndex]), isDistanceLeaderboard); + + //If the filter mode is "My Score" then centre the list around the entries and select the player's score + if( m_currentFilter == LeaderboardManager::eFM_MyScore ) + { + //Centre the leaderboard list on the entries + m_newTop = m_leaderboard.m_entryStartIndex-1; + + //Select the player entry + for( unsigned int i=m_leaderboard.m_entryStartIndex ; i9) + { + m_newTop=m_newSel-9; + } + break; + } + } + } + //Additional read + else + { + unsigned int insertPosition = 0; + + //If the first new entry is at a smaller index than the current first entry + if( m_newEntryIndex < m_leaderboard.m_entryStartIndex ) + { + if( (m_leaderboard.m_entryStartIndex-1) < m_newReadSize ) + m_newReadSize = m_leaderboard.m_entryStartIndex-1; + + //Move current entries forward + memmove((void*)(m_leaderboard.m_entries+m_newReadSize), (void*)m_leaderboard.m_entries, sizeof(LeaderboardEntry)*(NUM_ENTRIES-m_newReadSize)); + + //Set the (now smaller) entry start index + m_leaderboard.m_entryStartIndex = m_newEntryIndex; + + //We will be inserting the new entries at the start of the array + insertPosition = 0; + + //Entry count is either max possible entries or current entry count + read size, whichever is smaller + m_leaderboard.m_currentEntryCount = min((int)NUM_ENTRIES, (int)(m_leaderboard.m_currentEntryCount+m_newReadSize)); + } + //If the last new entry is at a greater position than the last possible entry + else if( m_newEntryIndex+m_newReadSize-1 >= m_leaderboard.m_entryStartIndex+NUM_ENTRIES ) + { + //Calculate the overlap (this is by how much new entries would overhang the end of the array) + int overlap = (((m_newEntryIndex-1)+m_newReadSize-1) - (m_leaderboard.m_entryStartIndex-1)) - NUM_ENTRIES + 1; + + //Move current entries backwards + memmove((void*)m_leaderboard.m_entries, (void*)(m_leaderboard.m_entries+overlap), sizeof(LeaderboardEntry)*(NUM_ENTRIES-overlap)); + + //Set the (now larger) start index + m_leaderboard.m_entryStartIndex += overlap; + + //We will be inserting the new entries at the end of the array + insertPosition = NUM_ENTRIES - m_newReadSize; + + //Entry count is max possible entries + m_leaderboard.m_currentEntryCount = NUM_ENTRIES; + } + //Otherwise we're inserting at the end of the array and there is plenty of room + else + { + insertPosition = m_leaderboard.m_currentEntryCount; + m_leaderboard.m_currentEntryCount += m_newReadSize; + } + + //For each entry in the leaderboard + for( unsigned int entryIndex=0 ; entryIndexpViews[0].pRows[entryIndex]), &(m_leaderboard.m_entries[insertPosition]), isDistanceLeaderboard); + insertPosition++; + } + } + + //LeaderboardManager::Instance()->SetStatsRetrieved(true); + return true; +} + +HRESULT CScene_Leaderboards::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +{ + pGetItemCountData->cItems = m_leaderboard.m_totalEntryCount; + + //if(LeaderboardManager::Instance()->GetStatsRead()) + //{ + // m_bReady=true; + //} + bHandled = TRUE; + + return S_OK; +} + +HRESULT CScene_Leaderboards::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData,BOOL& bHandled) +{ + if( pGetSourceTextData->bItemData ) + { + if( m_newTop != -1 && m_newSel != -1 ) + { + HRESULT ret = m_listGamers.SetTopItem(m_newTop); + assert( ret == S_OK ); + + ret = m_listGamers.SetCurSel(m_newSel); + assert( ret == S_OK ); + + // update the tooltips + + m_newTop = m_newSel = -1; + + bHandled = true; + return S_OK; + } + + unsigned int item = pGetSourceTextData->iItem; + + if( m_leaderboard.m_totalEntryCount > 0 && (item+1) < m_leaderboard.m_entryStartIndex ) + { + if( LeaderboardManager::Instance()->isIdle() ) + { + int readIndex = m_leaderboard.m_entryStartIndex - READ_SIZE; + if( readIndex <= 0 ) + readIndex = 1; + assert( readIndex >= 1 && readIndex <= (int)m_leaderboard.m_totalEntryCount ); + ReadStats(readIndex); + } + } + else if( m_leaderboard.m_totalEntryCount > 0 && (item+1) >= (m_leaderboard.m_entryStartIndex+m_leaderboard.m_currentEntryCount) ) + { + if( LeaderboardManager::Instance()->isIdle() ) + { + int readIndex = m_leaderboard.m_entryStartIndex + m_leaderboard.m_currentEntryCount; + assert( readIndex >= 1 && readIndex <= (int)m_leaderboard.m_totalEntryCount ); + ReadStats(readIndex); + } + } + else + { + unsigned int index = item - (m_leaderboard.m_entryStartIndex-1); + if( 0 == pGetSourceTextData->iData ) + { + pGetSourceTextData->szText = m_leaderboard.m_entries[index].m_wcRank; + bHandled = TRUE; + } + else if( 1 == pGetSourceTextData->iData ) + { + pGetSourceTextData->szText = m_leaderboard.m_entries[index].m_gamerTag; + bHandled = TRUE; + } + else if( pGetSourceTextData->iData >= 3 && pGetSourceTextData->iData <= 9 ) + { + if( m_leaderboard.m_numColumns <= (unsigned int)(pGetSourceTextData->iData-3) ) + pGetSourceTextData->szText = L""; + else + pGetSourceTextData->szText = m_leaderboard.m_entries[index].m_wcColumns[pGetSourceTextData->iData-3]; + bHandled = TRUE; + } + } + } + return S_OK; +} + +HRESULT CScene_Leaderboards::OnGetSourceDataImage(XUIMessageGetSourceImage* pGetImage, BOOL& bHandled) +{ + if( (pGetImage->iData == 2) && (pGetImage->bItemData) ) + { + if( m_newTop != -1 && m_newSel != -1 ) + { + //Do nothing, alignment handled in OnGetSourceDataText + + bHandled = true; + return S_OK; + } + + unsigned int item = pGetImage->iItem; + if( m_leaderboard.m_totalEntryCount > 0 && (item+1) < m_leaderboard.m_entryStartIndex ) + { + //Do nothing, reading handled in OnGetSourceDataText + } + else if( m_leaderboard.m_totalEntryCount > 0 && (item+1) >= (m_leaderboard.m_entryStartIndex+m_leaderboard.m_currentEntryCount) ) + { + //Do nothing, reading handled in OnGetSourceDataText + } + // 4J-PB - we're not allowed to show flags any more +// else +// { +// unsigned int index = item - (m_leaderboard.m_entryStartIndex-1); +// if( m_leaderboard.m_entries[index].m_locale > 0 && m_leaderboard.m_entries[index].m_locale <= XC_LOCALE_RUSSIAN_FEDERATION ) +// pGetImage->szPath = FLAG_ICON_PATHS[ m_leaderboard.m_entries[index].m_locale-1 ]; +// bHandled = TRUE; +// } + } + + return S_OK; +} + +void CScene_Leaderboards::PopulateLeaderboard(bool noResults) +{ + HRESULT hr; + HXUIOBJ visual=NULL; + HXUIOBJ hTemp=NULL; + hr=XuiControlGetVisual(m_listGamers.m_hObj,&visual); + + if(m_pHTitleIconSlots[0]==NULL) + { + VOID *pObj; + HXUIOBJ button; + D3DXVECTOR3 vPos; + + for(int i=0;i<7;i++) + { + // retrieve the visual for the title icon + hr=XuiElementGetChildById(visual,m_TitleIconNameA[i],&button); + + XuiObjectFromHandle( button, &pObj ); + m_pHTitleIconSlots[i] = (CXuiCtrlCraftIngredientSlot *)pObj; + + // store the default position, since we'll be repositioning these depending on how many are valid for each board + m_pHTitleIconSlots[i]->GetPosition(&vPos); + m_fTitleIconXPositions[i]= vPos.x; + } + } + + int iValidSlots=SetLeaderboardTitleIcons(); + + if( !noResults && m_leaderboard.m_totalEntryCount > 0 ) + { + hr=XuiElementGetChildById(visual,L"XuiLabel_Gamertag",&hTemp); + if(hTemp) + { + XuiControlSetText(hTemp,app.GetString( IDS_LEADERBOARD_GAMERTAG )); + XuiElementSetShow(hTemp, TRUE); + } + hr=XuiElementGetChildById(visual,L"XuiLabel_Rank",&hTemp); + if(hTemp) + { + XuiControlSetText(hTemp,app.GetString( IDS_LEADERBOARD_RANK )); + XuiElementSetShow(hTemp, TRUE); + } + + //Update entries display + wchar_t entriesBuffer[40]; + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<SetShow(FALSE); + } + } + else + { + hr=XuiElementGetChildById(visual,L"XuiLabel_Gamertag",&hTemp); + if(hTemp) + { + XuiElementSetShow(hTemp, FALSE); + } + hr=XuiElementGetChildById(visual,L"XuiLabel_Rank",&hTemp); + if(hTemp) + { + XuiElementSetShow(hTemp, FALSE); + } + + //Update entries display (to zero) + wchar_t entriesBuffer[40]; + swprintf(entriesBuffer, 40, L"%ls0", app.GetString(IDS_LEADERBOARD_ENTRIES)); + m_textEntries.SetText(entriesBuffer); + + //Show the no results message + m_textInfo.SetText(app.GetString(IDS_LEADERBOARD_NORESULTS)); + m_textInfo.SetShow(true); + + // hide the icons + for(int i=0;i<7;i++) + { + m_pHTitleIconSlots[i]->SetShow(FALSE); + } + + } + + m_bPopulatedOnce = true; +} + +void CScene_Leaderboards::CopyLeaderboardEntry(PXUSER_STATS_ROW statsRow, LeaderboardEntry* leaderboardEntry, bool isDistanceLeaderboard) +{ + leaderboardEntry->m_xuid = statsRow->xuid; + + //Copy the rank + leaderboardEntry->m_rank = statsRow->dwRank; + DWORD displayRank = leaderboardEntry->m_rank; + if(displayRank > 9999999) displayRank = 9999999; + swprintf_s(leaderboardEntry->m_wcRank, 12, L"%u", displayRank); + + //strcpy(statsRow->szGamertag,"WWWWWWWWWWWWWWWW"); + + //Convert the gamertag from char to wchar_t + MultiByteToWideChar( + CP_UTF8, + 0, + statsRow->szGamertag, + strlen(statsRow->szGamertag), + leaderboardEntry->m_gamerTag, + XUSER_NAME_SIZE ); + //Null terminate the gamertag + leaderboardEntry->m_gamerTag[strlen(statsRow->szGamertag)] = L'\0'; + + //Copy the locale + //leaderboardEntry->m_locale = statsRow->pColumns[0].Value.nData; + + //Copy the other columns + for( unsigned int i=0 ; idwNumColumns ; i++ ) + { + leaderboardEntry->m_columns[i] = statsRow->pColumns[i].Value.nData; + if( !isDistanceLeaderboard ) + { + DWORD displayValue = leaderboardEntry->m_columns[i]; + if(displayValue > 99999) displayValue = 99999; + swprintf_s(leaderboardEntry->m_wcColumns[i], 12, L"%u",displayValue); +#ifdef _DEBUG + app.DebugPrintf("Value - %d\n",leaderboardEntry->m_columns[i]); +#endif + } + else + { + // check how many digits we have + int iDigitC=0; + unsigned int uiVal=leaderboardEntry->m_columns[i]; +// uiVal=0xFFFFFFFF; +// leaderboardEntry->m_columns[i-1]=uiVal; + + while(uiVal!=0) + { + uiVal/=10; + iDigitC++; + } + +#ifdef _DEBUG + app.DebugPrintf("Value - %d\n",leaderboardEntry->m_columns[i]); +#endif + if(iDigitC<4) + { + // m + swprintf_s(leaderboardEntry->m_wcColumns[i], 12, L"%um", leaderboardEntry->m_columns[i]); +#ifdef _DEBUG + app.DebugPrintf("Display - %um\n", leaderboardEntry->m_columns[i]); +#endif + } + else if(iDigitC<8) + { + // km with a .X + swprintf_s(leaderboardEntry->m_wcColumns[i], 12, L"%.1fkm", ((float)leaderboardEntry->m_columns[i])/1000.f); +#ifdef _DEBUG + app.DebugPrintf("Display - %.1fkm\n", ((float)leaderboardEntry->m_columns[i])/1000.f); +#endif + } + else + { + // bigger than that, so no decimal point + swprintf_s(leaderboardEntry->m_wcColumns[i], 12, L"%.0fkm", ((float)leaderboardEntry->m_columns[i])/1000.f); +#ifdef _DEBUG + app.DebugPrintf("Display - %.0fkm\n", ((float)leaderboardEntry->m_columns[i])/1000.f); +#endif + } + } + } + + //Is the player + if( statsRow->xuid == ((XboxLeaderboardManager*)LeaderboardManager::Instance())->GetMyXUID() ) + { + leaderboardEntry->m_bPlayer = true; + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = false; + } + else + { + leaderboardEntry->m_bPlayer = false; + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = false; + + //Check for friend status + for( unsigned int friendIndex=0 ; friendIndexxuid ) + { + if( ( m_friends[friendIndex].dwFriendState & ( XONLINE_FRIENDSTATE_FLAG_SENTREQUEST | XONLINE_FRIENDSTATE_FLAG_RECEIVEDREQUEST ) ) == 0 ) + { + //Is friend, might be online + leaderboardEntry->m_bFriend = true; + leaderboardEntry->m_bOnline = ( m_friends[friendIndex].dwFriendState & XONLINE_FRIENDSTATE_FLAG_ONLINE ); + leaderboardEntry->m_bRequestedFriend = false; + } + else + { + //Friend request sent but not accepted yet + leaderboardEntry->m_bOnline = false; + leaderboardEntry->m_bFriend = false; + leaderboardEntry->m_bRequestedFriend = true; + } + + break; + } + } + } +} + +void CScene_Leaderboards::SetLeaderboardHeader() +{ + WCHAR buffer[40]; + DWORD bufferLength = 40; + + DWORD ret = XResourceGetString(LEADERBOARD_HEADERS[m_currentLeaderboard][m_currentDifficulty], buffer, &bufferLength, NULL); + + if( ret == ERROR_SUCCESS ) + m_textLeaderboard.SetText(buffer); +} + +int CScene_Leaderboards::SetLeaderboardTitleIcons() +{ + int iValidIcons=0; + + for(int i=0;i<7;i++) + { + if(TitleIcons[m_currentLeaderboard][i]==0) + { + m_pHTitleIconSlots[i]->SetShow(FALSE); + } + else + { + iValidIcons++; + m_pHTitleIconSlots[i]->SetIcon(DEFAULT_XUI_MENU_USER,TitleIcons[m_currentLeaderboard][i],0,1,10,31,false,FALSE); + } + } + + Reposition(iValidIcons); + + return iValidIcons; +} +void CScene_Leaderboards::ClearLeaderboardTitlebar() +{ + for(int i=0;i<7;i++) + { + m_pHTitleIconSlots[i]->SetShow(FALSE); + } + + HXUIOBJ visual=NULL; + HXUIOBJ hTemp=NULL; + HRESULT hr; + hr=XuiControlGetVisual(m_listGamers.m_hObj,&visual); + + hr=XuiElementGetChildById(visual,L"XuiLabel_Gamertag",&hTemp); + if(hTemp) + { + XuiElementSetShow(hTemp, FALSE); + } + hr=XuiElementGetChildById(visual,L"XuiLabel_Rank",&hTemp); + if(hTemp) + { + XuiElementSetShow(hTemp, FALSE); + } +} diff --git a/Minecraft.Client/Common/XUI/XUI_Leaderboards.h b/Minecraft.Client/Common/XUI/XUI_Leaderboards.h new file mode 100644 index 0000000..e06f13e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Leaderboards.h @@ -0,0 +1,169 @@ +#pragma once +#include "XUI_Helper.h" +#include "../media/xuiscene_leaderboards.h" + +#include "..\Leaderboards\LeaderboardManager.h" + +class CXuiCtrlCraftIngredientSlot; + +class CScene_Leaderboards : public CXuiSceneImpl, public LeaderboardReadListener +{ +private: + // 4J Stu - Because the kills leaderboard doesn't a peaceful entry there are some special + // handling to make it skip that. We have re-arranged the order of the leaderboards so + // I am making this in case we do it again. + // 4J Stu - Made it a member of the class, rather than a #define + static const int LEADERBOARD_KILLS_POSITION = 3; + + static const int NUM_LEADERBOARDS = 4;//6; //Number of leaderboards + static const int NUM_ENTRIES = 101; //Cache up to this many entries + static const int READ_SIZE = 15; //Read this many entries at a time + +// static LPCWSTR FLAG_ICON_PATHS[37]; + int m_iPad; + + bool m_bPopulatedOnce; + + static const int LEADERBOARD_HEADERS[NUM_LEADERBOARDS][4]; + + static const int TitleIcons[NUM_LEADERBOARDS][7]; + static LPCWSTR m_TitleIconNameA[7]; + static LPCWSTR m_TextColumnNameA[7]; + HXUIOBJ m_hTextEntryA[7]; + + struct LeaderboardDescriptor { + DWORD m_viewId; + DWORD m_columnCount; + WORD m_columnIds[8]; + + LeaderboardDescriptor(DWORD viewId, DWORD columnCount, WORD columnId_0, WORD columnId_1, WORD columnId_2, WORD columnId_3, WORD columnId_4, WORD columnId_5, WORD columnId_6, WORD columnId_7) + { + m_viewId = viewId; + m_columnCount = columnCount; + m_columnIds[0] = columnId_0; + m_columnIds[1] = columnId_1; + m_columnIds[2] = columnId_2; + m_columnIds[3] = columnId_3; + m_columnIds[4] = columnId_4; + m_columnIds[5] = columnId_5; + m_columnIds[6] = columnId_6; + m_columnIds[7] = columnId_7; + } + }; + + static const LeaderboardDescriptor LEADERBOARD_DESCRIPTORS[NUM_LEADERBOARDS][4]; + + struct LeaderboardEntry { + PlayerUID m_xuid; + DWORD m_rank; + WCHAR m_wcRank[12]; + WCHAR m_gamerTag[XUSER_NAME_SIZE+1]; + //int m_locale; + unsigned int m_columns[7]; + WCHAR m_wcColumns[7][12]; + bool m_bPlayer; //Is the player + bool m_bOnline; //Is online + bool m_bFriend; //Is friend + bool m_bRequestedFriend; //Friend request sent but not answered + }; + + struct Leaderboard { + DWORD m_totalEntryCount; //Either total number of entries in leaderboard, or total number of results for a friends query + DWORD m_entryStartIndex; //Index of first entry + DWORD m_currentEntryCount; //Current number of entries + LeaderboardEntry m_entries[NUM_ENTRIES]; + DWORD m_numColumns; + }; + + Leaderboard m_leaderboard; //All leaderboard data for the currently selected filter + + unsigned int m_currentLeaderboard; //The current leaderboard selected for view + +#ifdef _XBOX + LeaderboardManager::EFilterMode m_currentFilter; //The current filter selected +#endif + unsigned int m_currentDifficulty; //The current difficulty selected + + unsigned int m_newEntryIndex; //Index of the first entry being read + unsigned int m_newReadSize; //Number of entries in the current read operation + + int m_newTop; //Index of the element that should be at the top of the list + int m_newSel; //Index of the element that should be selected in the list + + XONLINE_FRIEND* m_friends; //Current player's friends + unsigned int m_numFriends; //Count of friends + PlayerUID* m_filteredFriends; //List of all friend XUIDs (and player's), only counting actual friends, not pending + unsigned int m_numFilteredFriends; //Count of filtered friends + + CXuiList m_listGamers; //The XUI list showing the leaderboard info + CXuiControl m_textLeaderboard; //The XUI text box showing the current leaderboard name + CXuiControl m_textInfo; //The XUI text box showing info messages (loading, no results, etc.) + CXuiControl m_textFilter; //The XUI text box showing the current filter + CXuiControl m_textEntries; //The XUI text box showing the total number of entries in this leaderboard + CXuiCtrlCraftIngredientSlot *m_pHTitleIconSlots[7]; + float m_fTitleIconXPositions[7]; + float m_fTextXPositions[7]; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_NOTIFY_SELCHANGED(OnNotifySelChanged) + XUI_ON_XM_NOTIFY_SET_FOCUS(OnNotifySetFocus) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiListGamers, m_listGamers) + MAP_CONTROL(IDC_XuiTextLeaderboard, m_textLeaderboard) + MAP_CONTROL(IDC_XuiTextInfo, m_textInfo) + MAP_CONTROL(IDC_XuiTextFilter, m_textFilter) + MAP_CONTROL(IDC_XuiTextEntries, m_textEntries) + END_CONTROL_MAP() + + HRESULT OnInit(XUIMessageInit* pInitData, BOOL& bHandled); + HRESULT OnDestroy(); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage* pGetImage, BOOL& bHandled); + HRESULT OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled); + + //Start a read request with the current parameters + void ReadStats(int startIndex); + + //Callback function called when stats read completes, userdata contains pointer to instance of CScene_Leaderboards + virtual bool OnStatsReadComplete(bool success, int numResults, LeaderboardManager::ViewOut results); + + //Copy the stats from the raw m_stats structure into the m_leaderboards structure + int m_numStats; + PXUSER_STATS_READ_RESULTS m_stats; + bool RetrieveStats(); + + //Populate the XUI leaderboard with the contents of m_leaderboards + void PopulateLeaderboard(bool noResults); + + //Copy a leaderboard entry from the stats row + void CopyLeaderboardEntry(PXUSER_STATS_ROW statsRow, LeaderboardEntry* leaderboardEntry, bool isDistanceLeaderboard); + + //Set the header text of the leaderboard + void SetLeaderboardHeader(); + + // Set the title icons + int SetLeaderboardTitleIcons(); + void ClearLeaderboardTitlebar(); + void Reposition(int iNumber); + void RepositionText(int iNumber); + void UpdateTooltips(); + +protected: + bool m_isProcessingStatsRead; + bool m_bReady; +public: + + XUI_IMPLEMENT_CLASS( CScene_Leaderboards, L"CScene_Leaderboards", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_LoadSettings.cpp b/Minecraft.Client/Common/XUI/XUI_LoadSettings.cpp new file mode 100644 index 0000000..7f32ff8 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_LoadSettings.cpp @@ -0,0 +1,1686 @@ +#include "stdafx.h" +#include +#include +#include +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h" +#include "..\..\LocalPlayer.h" +#include "..\..\Minecraft.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\ArrayWithLength.h" +#include "..\..\..\Minecraft.World\File.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Options.h" +#include "XUI_Ctrl_4JList.h" +#include "XUI_MultiGameInfo.h" +#include "XUI_MultiGameJoinLoad.h" +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_LoadSettings.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\GameRules\ConsoleGameRules.h" +#include "..\..\StringTable.h" +#include "..\..\DLCTexturePack.h" + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 1 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 100 + +int CScene_LoadGameSettings::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + + +HRESULT CScene_LoadGameSettings::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_hXuiBrush = NULL; + m_bSetup = false; + m_texturePackDescDisplayed = false; + m_iConfigA=NULL; + + WCHAR TempString[256]; + + m_params = (LoadMenuInitData *)pInitData->pvInitData; + + m_MoreOptionsParams.bGenerateOptions=FALSE; + m_MoreOptionsParams.bPVP = TRUE; + m_MoreOptionsParams.bTrust = TRUE; + m_MoreOptionsParams.bFireSpreads = TRUE; + m_MoreOptionsParams.bTNT = TRUE; + m_MoreOptionsParams.bHostPrivileges = FALSE; + m_MoreOptionsParams.bResetNether = FALSE; + m_MoreOptionsParams.iPad = m_params->iPad; + + // 4J-JEV: Fix for: + // TU12: Content: Gameplay: New "Mass Effect World" remembers and uses the settings of another - lately created - World. + m_MoreOptionsParams.bBonusChest = FALSE; + m_MoreOptionsParams.bFlatWorld = FALSE; + m_MoreOptionsParams.bStructures = TRUE; + + m_iPad=m_params->iPad; + m_iSaveGameInfoIndex=m_params->iSaveGameInfoIndex; + m_levelGen = m_params->levelGen; + + MapChildControls(); + + XuiControlSetText(m_MoreOptions,app.GetString(IDS_MORE_OPTIONS)); + XuiControlSetText(m_ButtonLoad,app.GetString(IDS_LOAD)); + XuiControlSetText(m_pTexturePacksList->m_hObj,app.GetString(IDS_DLC_MENU_TEXTUREPACKS)); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineSettingChangedBySystem=false; + + // Set the text for friends of friends, and default to on + if( m_bMultiplayerAllowed) + { + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + if(bGameSetting_Online) + { + // The profile settings say Online, but either the player is offline, or they are not allowed to play online + m_MoreOptionsParams.bOnlineSettingChangedBySystem=true; + } + } + + XuiSetTimer(m_hObj,GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME); + XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); + + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + m_CurrentDifficulty=app.GetGameSettings(m_iPad,eGameSetting_Difficulty); + m_SliderDifficulty.SetValue(m_CurrentDifficulty); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[m_CurrentDifficulty])); + m_SliderDifficulty.SetText(TempString); + + m_bHasBeenInCreative = false; + + if(m_levelGen) + { + m_GameName.SetText(m_levelGen->getDisplayName()); + if(m_levelGen->requiresTexturePack()) + { + m_MoreOptionsParams.dwTexturePack = m_levelGen->getRequiredTexturePackId(); + m_pTexturePacksList->SetEnable(FALSE); + + // retrieve the save icon from the texture pack, if there is one + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hXuiBrush); + } + + // Set this level as created in creative mode, so that people can't use the themed worlds as an easy way to get achievements + m_bHasBeenInCreative = true; + m_GameCreatedMode.SetText( app.GetString(IDS_CREATED_IN_CREATIVE) ); + } + } + else + { + // set the save icon + PBYTE pbImageData=NULL; + DWORD dwImageBytes=0; + + StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex,m_XContentData); + StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex,&pbImageData,&dwImageBytes); + + // if there is no thumbnail, retrieve the default one from the file. + // Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save + bool bHostOptionsRead = false; + unsigned int uiHostOptions = 0; + if(pbImageData==NULL) + { + DWORD dwResult=XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&m_XContentData,NULL,&dwImageBytes,NULL); + if(dwResult==ERROR_SUCCESS) + { + pbImageData = new BYTE[dwImageBytes]; + XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&m_XContentData,pbImageData,&dwImageBytes,NULL); + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hXuiBrush); + } + } + else + { + // retrieve the seed value from the image metadata + ZeroMemory(m_szSeed,50); + app.GetImageTextData(pbImageData,dwImageBytes,(unsigned char *)&m_szSeed,uiHostOptions,bHostOptionsRead,m_MoreOptionsParams.dwTexturePack); + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hXuiBrush); + +// #ifdef _DEBUG +// // dump out the thumbnail +// HANDLE hThumbnail = CreateFile("GAME:\\thumbnail.png", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL); +// DWORD dwBytes; +// WriteFile(hThumbnail,pbImageData,dwImageBytes,&dwBytes,NULL); +// XCloseHandle(hThumbnail); +// #endif + + if(m_szSeed[0]!=0) + { + swprintf( (WCHAR *)TempString, 256, L"%ls: %hs", app.GetString( IDS_SEED ),m_szSeed); + m_GameSeed.SetText(TempString); + } + else + { + m_GameSeed.SetText(L""); + } + } + + // Setup all the text and checkboxes to match what the game was saved with on + if(bHostOptionsRead) + { + m_MoreOptionsParams.bPVP = app.GetGameHostOption(uiHostOptions,eGameHostOption_PvP)>0?TRUE:FALSE; + m_MoreOptionsParams.bTrust = app.GetGameHostOption(uiHostOptions,eGameHostOption_TrustPlayers)>0?TRUE:FALSE; + m_MoreOptionsParams.bFireSpreads = app.GetGameHostOption(uiHostOptions,eGameHostOption_FireSpreads)>0?TRUE:FALSE; + m_MoreOptionsParams.bTNT = app.GetGameHostOption(uiHostOptions,eGameHostOption_TNT)>0?TRUE:FALSE; + m_MoreOptionsParams.bHostPrivileges = app.GetGameHostOption(uiHostOptions,eGameHostOption_CheatsEnabled)>0?TRUE:FALSE; + + m_bHasBeenInCreative = app.GetGameHostOption(uiHostOptions,eGameHostOption_HasBeenInCreative)>0; + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_HasBeenInCreative)>0) + { + m_GameCreatedMode.SetText( app.GetString(IDS_CREATED_IN_CREATIVE) ); + } + else + { + m_GameCreatedMode.SetText( app.GetString(IDS_CREATED_IN_SURVIVAL) ); + } + + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_GameType)>0) + { + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + + if(app.GetGameHostOption(uiHostOptions,eGameHostOption_FriendsOfFriends) && !(m_bMultiplayerAllowed && bGameSetting_Online)) + { + m_MoreOptionsParams.bAllowFriendsOfFriends = TRUE; + } + } + + m_GameName.SetText(m_XContentData.szDisplayName); + } + + // 4J-PB - Load up any texture pack data we have locally in the XZP + for(int i=0;iRecordMenuShown(m_iPad, eUIScene_LoadMenu, 0); + m_iTexturePacksNotInstalled=0; + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true) + { + // not doing a mount, so enable input + m_bIgnoreInput=true; + } + else + { + m_bIgnoreInput = false; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + m_pTexturePacksList->SetSelectionChangedHandle(m_hObj); + + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + HRESULT hr; + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + ListInfo.fEnabled = TRUE; + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tp; + if(pDLCTexPack) + { + int id=pDLCTexPack->getDLCParentPackId(); + + if(id==0) + { + // default texture pack - should come first + ListInfo.iSortIndex=0x0FFFFFFF; + } + else + { + ListInfo.iSortIndex=id; + ListInfo.iData=id; + } + } +#ifdef _DEBUG + app.DebugPrintf("TP - "); + OutputDebugStringW(tp->getName().c_str()); + app.DebugPrintf(", sort index - %d\n",ListInfo.iSortIndex); +#endif + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + } + } + + m_currentTexturePackIndex = m_pTexturePacksList->GetIndexByUserData(m_MoreOptionsParams.dwTexturePack); + m_pTexturePacksList->SetCurSel(m_currentTexturePackIndex); + m_pTexturePacksList->SetTopItem(m_currentTexturePackIndex); // scroll the item into view if it's not visible + UpdateTexturePackDescription(m_currentTexturePackIndex); + + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + } + + m_bSetup = true; + + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_LoadGameSettings::LaunchGame(void) +{ + // stop the timer running that causes a check for new texture packs in TMS but not installed, since this will run all through the load game, and will crash if it tries to create an hbrush + XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID); + + if( (m_bGameModeSurvival != true || m_bHasBeenInCreative) || m_MoreOptionsParams.bHostPrivileges == TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + if(m_bGameModeSurvival != true || m_bHasBeenInCreative) + { + // 4J-PB - Need different text for Survival mode with a level that has been saved in Creative + if(m_bGameModeSurvival) + { + StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_SAVEDINCREATIVE, uiIDA, 2, m_iPad,&CScene_LoadGameSettings::ConfirmLoadReturned,this,app.GetStringTable()); + } + else // it's creative mode + { + // has it previously been saved in creative? + if(m_bHasBeenInCreative) + { + // 4J-PB - We don't really need to tell the user this will have achievements disabled, since they already saved it in creative + // and they got the warning then + // inform them that leaderboard writes and achievements will be disabled + //StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_SAVEDINCREATIVE_CONTINUE, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::ConfirmLoadReturned,this,app.GetStringTable()); + + if(m_levelGen != NULL) + { + LoadLevelGen(m_levelGen); + } + else + { + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&m_XContentData,CScene_LoadGameSettings::LoadSaveDataReturned,this); + + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,this); + + } + } + } + else + { + // ask if they're sure they want to turn this into a creative map + StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_CREATIVE, uiIDA, 2, m_iPad,&CScene_LoadGameSettings::ConfirmLoadReturned,this,app.GetStringTable()); + } + } + } + else + { + StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_HOST_PRIVILEGES, uiIDA, 2, m_iPad,&CScene_LoadGameSettings::ConfirmLoadReturned,this,app.GetStringTable()); + } + } + else + { + if(m_levelGen != NULL) + { + LoadLevelGen(m_levelGen); + } + else + { + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&m_XContentData,CScene_LoadGameSettings::LoadSaveDataReturned,this); + + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,this); + } + } + } + return 0; +} + +int CScene_LoadGameSettings::CheckResetNetherReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_LoadGameSettings* pClass = (CScene_LoadGameSettings*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // continue and reset the nether + pClass->LaunchGame(); + } + else + { + // turn off the reset nether and continue + pClass->m_MoreOptionsParams.bResetNether=FALSE; + pClass->LaunchGame(); + } + return 0; +} + +HRESULT CScene_LoadGameSettings::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // 4J-PB - stop people double pressing this + if(m_bIgnoreInput) return S_OK; + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_ButtonLoad) + { + // Check if we need to upsell the texture pack + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { + // They've selected a texture pack they don't have yet + // upsell + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + // 4J-PB - if the full offer id is 0, then the texture pack dlc load failed + if(ullOfferID_Full!=0LL) + { + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + // Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_LoadGameSettings::TexturePackDialogReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_LoadGameSettings::TexturePackDialogReturned,this,app.GetStringTable()); + } + + return S_OK; + } + } + } + + // if the profile data has been changed, then force a profile write (we save the online/invite/friends of friends settings) + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + // check the checkboxes + + // Only save the online setting if the user changed it - we may change it because we're offline, but don't want that saved + if(!m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + app.SetGameSettings(m_iPad,eGameSetting_Online,m_MoreOptionsParams.bOnlineGame?1:0); + } + app.SetGameSettings(m_iPad,eGameSetting_InviteOnly,m_MoreOptionsParams.bInviteOnly?1:0); + app.SetGameSettings(m_iPad,eGameSetting_FriendsOfFriends,m_MoreOptionsParams.bAllowFriendsOfFriends?1:0); + + app.CheckGameSettingsChanged(true,pNotifyPressData->UserIndex); + + SetShow( FALSE ); + m_bIgnoreInput = true; + + // Check that we have the rights to use a texture pack we have selected. + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { + // DLC corrupt, so use the default textures + m_MoreOptionsParams.dwTexturePack=0; + } + else + { + m_pDLCPack=pTexturePack->getDLCPack(); + // do we have a license? + if(m_pDLCPack && !m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // no + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + if(!ProfileManager.IsSignedInLive(pNotifyPressData->UserIndex)) + { + // need to be signed in to live + StorageManager.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + return S_OK; + } + else + { + // upsell + + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_pDLCPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=pTexturePack->getDLCPack()->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the texture pack + TelemetryManager->RecordUpsellPresented(pNotifyPressData->UserIndex, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + //StorageManager.RequestMessageBox(IDS_UNLOCK_DLC_TITLE, IDS_UNLOCK_DLC_SKIN, uiIDA, 2, pInputData->UserIndex,&CScene_SkinSelect::UnlockSkinReturned,this,app.GetStringTable()); + StorageManager.RequestMessageBox(IDS_UNLOCK_DLC_TEXTUREPACK_TITLE, IDS_UNLOCK_DLC_TEXTUREPACK_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_LoadGameSettings::UnlockTexturePackReturned,this,app.GetStringTable()); + return S_OK; + } + } + } + } + + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + + // Check if they have the Reset Nether flag set, and confirm they want to do this + if(m_MoreOptionsParams.bResetNether==TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_DONT_RESET_NETHER; + uiIDA[1]=IDS_RESET_NETHER; + + StorageManager.RequestMessageBox(IDS_RESETNETHER_TITLE, IDS_RESETNETHER_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_LoadGameSettings::CheckResetNetherReturned,this,app.GetStringTable()); + } + else + { + LaunchGame(); + } + } + else if(hObjPressed==m_MoreOptions) + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_LaunchMoreOptionsMenu,&m_MoreOptionsParams); + } + else if(hObjPressed == m_ButtonGameMode) + { + if(m_bGameModeSurvival) + { + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + else + { + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + } + } + else if(hObjPressed == m_pTexturePacksList->m_hObj) + { + UpdateCurrentTexturePack(); + } + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + app.SetCorruptSaveDeleted(false); + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + case VK_PAD_RSHOULDER: +/* if(app.m_bTransferSavesToXboxOne && ProfileManager.IsFullVersion()) + { + app.NavigateToScene(m_iPad,eUIScene_TransferToXboxOne,m_params); + } + */ + break; + } + + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnFontRendererChange() +{ + int iRB=-1; + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1,-1,-1,-1,-1,-1,true); + + return S_OK; +} + +int CScene_LoadGameSettings::ConfirmLoadReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_LoadGameSettings* pClass = (CScene_LoadGameSettings*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(pClass->m_levelGen != NULL) + { + pClass->LoadLevelGen(pClass->m_levelGen); + } + else + { + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&pClass->m_XContentData,CScene_LoadGameSettings::LoadSaveDataReturned,pClass); + + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(pClass->m_iPad,false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, pClass->m_iPad,&CScene_LoadGameSettings::DeviceRemovedDialogReturned,pClass); + } + } + } + else + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + } + return 0; +} + +HRESULT CScene_LoadGameSettings::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly + switch(pTimer->nId) + { + case GAME_CREATE_ONLINE_TIMER_ID: + { + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + // also check for any new texture packs info being available + // for each item in the mem list, check it's in the data list + + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + // for each iConfig, check if the data is available, and add it to the List, then remove it from the viConfig + for(int i=0;i 0 && pbData) + { + DWORD dwImageBytes=0; + PBYTE pbImageData=NULL; + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes ); + ListInfo.fEnabled = TRUE; + ListInfo.iData = m_iConfigA[i]; + HRESULT hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + + ListInfo.iSortIndex=m_iConfigA[i]; + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + + m_iConfigA[i]=-1; + + m_currentTexturePackIndex = m_pTexturePacksList->GetIndexByUserData(m_MoreOptionsParams.dwTexturePack); + m_pTexturePacksList->SetCurSel(m_currentTexturePackIndex); + m_pTexturePacksList->SetTopItem(m_currentTexturePackIndex); // scroll the item into view if it's not visible + } + } + } + bool bAllDone=true; + for(int i=0;im_MoreOptionsParams.bOnlineGame; + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(!isClientSide || connectedControllers == 1 || !RenderManager.IsHiDef()) + { + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + DWORD dwLocalUsersMask = CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + + // No guest problems so we don't need to force a sign-in of players here + StartGameFromSave(pClass, dwLocalUsersMask); + } + } + else + { + ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_LoadGameSettings::StartGame_SignInReturned, pParam,ProfileManager.GetPrimaryPad()); + } + } + else + { + // the save is corrupt! + + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + + // give the option to delete the save + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, + pClass->m_iPad,&CScene_LoadGameSettings::DeleteSaveDialogReturned,pClass, app.GetStringTable()); + + } + return 0; +} + +int CScene_LoadGameSettings::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_LoadGameSettings* pClass = (CScene_LoadGameSettings*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { +#ifdef _XBOX + StorageManager.DeleteSaveData(&pClass->m_XContentData,CScene_LoadGameSettings::DeleteSaveDataReturned,pClass); +#endif + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int CScene_LoadGameSettings::DeleteSaveDataReturned(void *pParam,bool bSuccess) +{ + CScene_LoadGameSettings* pClass = (CScene_LoadGameSettings*)pParam; + + app.SetCorruptSaveDeleted(true); + app.NavigateBack(pClass->m_iPad); + + return 0; +} + +int CScene_LoadGameSettings::DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // need to back out of this scene + app.NavigateBack(iPad); + + return 0; +} + +// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not +void CScene_LoadGameSettings::StartGameFromSave(CScene_LoadGameSettings* pClass, DWORD dwLocalUsersMask) +{ + INT saveOrCheckpointId = 0; + bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + TelemetryManager->RecordLevelResume(pClass->m_iPad, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, app.GetGameSettings(pClass->m_iPad,eGameSetting_Difficulty), app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount(), saveOrCheckpointId); + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool isPrivate = (app.GetGameSettings(pClass->m_iPad,eGameSetting_InviteOnly)>0)?true:false; + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = NULL; + param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack); + //pMinecraft->skins->updateUI(); + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,app.GetGameSettings(pClass->m_iPad,eGameSetting_FriendsOfFriends)); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(pClass->m_iPad,eGameSetting_GamertagsVisible)); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(pClass->m_iPad,eGameSetting_BedrockFog)?1:0); + + app.SetGameHostOption(eGameHostOption_PvP,pClass->m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,pClass->m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,pClass->m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,pClass->m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,pClass->m_MoreOptionsParams.bHostPrivileges ); + + // flag if the user wants to reset the Nether to force a Fortress with netherwart etc. + app.SetResetNether((pClass->m_MoreOptionsParams.bResetNether==TRUE)?true:false); + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + app.SetGameHostOption(eGameHostOption_GameType,pClass->m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId()); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave timer + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +int CScene_LoadGameSettings::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + CScene_LoadGameSettings* pClass = (CScene_LoadGameSettings*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad)) + { + DWORD dwLocalUsersMask = 0; + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool noPrivileges = false; + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index); + } + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(), false, &pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && (noPrivileges || noUGC) ) + { + if( noUGC ) + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + //pClass->m_bAbortSearch=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + //pClass->m_bAbortSearch=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + } + else + { + StartGameFromSave(pClass, dwLocalUsersMask); + } + } + } + else + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + //pClass->m_bAbortSearch=false; + } + return 0; +} + + +HRESULT CScene_LoadGameSettings::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + if( pGetSourceImageData->bItemData ) + { + pGetSourceImageData->hBrush = m_hXuiBrush; + bHandled = TRUE; + } + return S_OK; +} + + +HRESULT CScene_LoadGameSettings::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderDifficulty.GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[pNotifyValueChanged->nValue])); + m_SliderDifficulty.SetText(TempString); + } + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY || + pTransition->dwTransType == XUI_TRANSITION_FROM || pTransition->dwTransType == XUI_TRANSITION_BACKFROM) + { + // 4J Stu - We may have had to unload our font renderer in this scene if one of the save files + // uses characters not in our font (eg asian chars) so restore our font renderer + // This will not do anything if our font renderer is already loaded + app.OverrideFontRenderer(true,true); + } + else if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + m_SliderDifficulty.SetValueDisplay(FALSE); + // 4J-PB - Need to check for installed DLC, which might have happened while you were on the More Options scene + if(pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_pTexturePacksList->RemoveAllData(); + } + } + } + + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY || + pTransition->dwTransType == XUI_TRANSITION_FROM || pTransition->dwTransType == XUI_TRANSITION_BACKFROM) + { + } + else if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + if(m_bSetup && m_texturePackDescDisplayed) + { + XUITimeline *timeline; + XUINamedFrame *startFrame, *endFrame; + GetTimeline( &timeline ); + startFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + endFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + m_texturePackDescDisplayed = true; + } + } + + return S_OK; +} + +int CScene_LoadGameSettings::UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_LoadGameSettings* pScene = (CScene_LoadGameSettings*)pParam; + + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + ULONGLONG ullIndexA[1]; + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(pScene->m_pDLCPack->getPurchaseOfferId()); + + if(pDLCInfo!=NULL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Full; + } + else + { + ullIndexA[0]=pScene->m_pDLCPack->getPurchaseOfferId(); + } + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( pScene->m_pDLCPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } + + pScene->m_bIgnoreInput = false; + pScene->SetShow( TRUE ); + + return 0; +} + +HRESULT CScene_LoadGameSettings::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if(hObjSource == m_pTexturePacksList->m_hObj) + { + UpdateTexturePackDescription(pNotifySelChangedData->iItem); + + if(m_bSetup && !m_texturePackDescDisplayed) + { + XUITimeline *timeline; + XUINamedFrame *startFrame, *endFrame; + GetTimeline( &timeline ); + startFrame = timeline->FindNamedFrame( L"SlideOut" ); + endFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + m_texturePackDescDisplayed = true; + } + } + + return S_OK; +} + +HRESULT CScene_LoadGameSettings::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + HXUIOBJ hSourceParent, hDestParent; + XuiElementGetParent(hObjSource,&hSourceParent); + XuiElementGetParent(pNotifyFocusData->hObjOther,&hDestParent); + if(hSourceParent != hDestParent && pNotifyFocusData->hObjOther != m_pTexturePacksList->m_hObj && hSourceParent == m_pTexturePacksList->m_hObj) + { + m_pTexturePacksList->SetCurSel(m_currentTexturePackIndex); + m_pTexturePacksList->SetTopItem(m_currentTexturePackIndex); // scroll the item into view if it's not visible + } + else if(!m_texturePackDescDisplayed && pNotifyFocusData->hObjOther == m_pTexturePacksList->m_hObj) + { + int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount(); + if(texturePacksCount == 1) + { + XUITimeline *timeline; + XUINamedFrame *startFrame, *endFrame; + GetTimeline( &timeline ); + startFrame = timeline->FindNamedFrame( L"SlideOut" ); + endFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + m_texturePackDescDisplayed = true; + } + } + + return S_OK; +} + +void CScene_LoadGameSettings::UpdateTexturePackDescription(int index) +{ + int iTexPackId=m_pTexturePacksList->GetData(index).iData; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(iTexPackId); + + if(tp==NULL) + { + // this is probably a texture pack icon added from TMS + DWORD dwBytes=0,dwFileBytes=0; + PBYTE pbData=NULL,pbFileData=NULL; + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(index); + + app.GetTPD(ListItem.iData,&pbData,&dwBytes); + + app.GetFileFromTPD(eTPDFileType_Loc,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes > 0 && pbFileData) + { + StringTable *pStringTable = new StringTable(pbFileData, dwFileBytes); + m_texturePackTitle.SetText(pStringTable->getString(L"IDS_DISPLAY_NAME")); + m_texturePackDescription.SetText(pStringTable->getString(L"IDS_TP_DESCRIPTION")); + } + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackIconBrush); + m_texturePackIcon->UseBrush(m_hTexturePackIconBrush); + } + app.GetFileFromTPD(eTPDFileType_Comparison,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackComparisonBrush); + m_texturePackComparison->UseBrush(m_hTexturePackComparisonBrush); + } + else + { + m_texturePackComparison->UseBrush(NULL); + } + } + else + { + m_texturePackTitle.SetText(tp->getName().c_str()); + m_texturePackDescription.SetText(tp->getDesc1().c_str()); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hTexturePackIconBrush); + m_texturePackIcon->UseBrush(m_hTexturePackIconBrush); + } + else + { + m_texturePackIcon->UseBrush(NULL); + } + + pbImageData = tp->getPackComparison(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hTexturePackComparisonBrush); + m_texturePackComparison->UseBrush(m_hTexturePackComparisonBrush); + } + else + { + m_texturePackComparison->UseBrush(NULL); + } + } +} + +void CScene_LoadGameSettings::ClearTexturePackDescription() +{ + m_texturePackTitle.SetText(L" "); + m_texturePackDescription.SetText(L" "); + m_texturePackComparison->UseBrush(NULL); + m_texturePackIcon->UseBrush(NULL); +} + +void CScene_LoadGameSettings::UpdateCurrentTexturePack() +{ + m_currentTexturePackIndex = m_pTexturePacksList->GetCurSel(); + int iTexPackId=m_pTexturePacksList->GetData(m_currentTexturePackIndex).iData; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(iTexPackId); + + // if the texture pack is null, you don't have it yet + if(tp==NULL) + { + // Upsell + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + // Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_LoadGameSettings::TexturePackDialogReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_LoadGameSettings::TexturePackDialogReturned,this,app.GetStringTable()); + } + + // do set the texture pack id, and on the user pressing create world, check they have it + m_MoreOptionsParams.dwTexturePack = ListItem.iData; + + return ; + } + else + { + m_MoreOptionsParams.dwTexturePack = tp->getId(); + } +} + +HRESULT CScene_LoadGameSettings::OnDestroy() +{ + if( m_hXuiBrush ) + { + XuiDestroyBrush( m_hXuiBrush ); + } + + // clear out the texture pack data + for(int i=0;igetDefaultSaveName().c_str()); + + bool isPrivate = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)>0)?true:false; + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = NULL; + param->levelGen = levelGen; + + if(levelGen->requiresTexturePack()) + { + param->texturePackId = levelGen->getRequiredTexturePackId(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(param->texturePackId); + //pMinecraft->skins->updateUI(); + } + + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(m_iPad,eGameSetting_GamertagsVisible)); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(m_iPad,eGameSetting_BedrockFog)?1:0); + + // 4J-JEV: Fix for: + // TU12: Content: Gameplay: New "Mass Effect World" remembers and uses the settings of another - lately created - World. + app.SetGameHostOption(eGameHostOption_LevelType,m_MoreOptionsParams.bFlatWorld); + app.SetGameHostOption(eGameHostOption_Structures,m_MoreOptionsParams.bStructures); + app.SetGameHostOption(eGameHostOption_BonusChest,m_MoreOptionsParams.bBonusChest); + + app.SetGameHostOption(eGameHostOption_PvP,m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,m_MoreOptionsParams.bHostPrivileges ); + + // flag if the user wants to reset the Nether to force a Fortress with netherwart etc. + app.SetResetNether((m_MoreOptionsParams.bResetNether==TRUE)?true:false); + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + app.SetGameHostOption(eGameHostOption_GameType,m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId()); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave timer + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + + +HRESULT CScene_LoadGameSettings::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + // clear out the texture pack list and do again + m_pTexturePacksList->RemoveAllData(); + m_iTexturePacksNotInstalled=0; + + } + + // this will send a CustomMessage_DLCMountingComplete when done + return S_OK; +} + + +HRESULT CScene_LoadGameSettings::OnCustomMessage_DLCMountingComplete() +{ + m_pTexturePacksList->SetSelectionChangedHandle(m_hObj); + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + HRESULT hr; + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + ListInfo.fEnabled = TRUE; + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tp; + if(pDLCTexPack) + { + int id=pDLCTexPack->getDLCParentPackId(); + + if(id==0) + { + // default texture pack - should come first + ListInfo.iSortIndex=0x0FFFFFFF; + } + else + { + ListInfo.iSortIndex=id; + ListInfo.iData=id; + } + } + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + } + } + m_currentTexturePackIndex = m_pTexturePacksList->GetIndexByUserData(m_MoreOptionsParams.dwTexturePack); + m_pTexturePacksList->SetCurSel(m_currentTexturePackIndex); + m_pTexturePacksList->SetTopItem(m_currentTexturePackIndex); // scroll the item into view if it's not visible + + m_iTexturePacksNotInstalled=0; + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + if(m_iConfigA!=NULL) + { + delete m_iConfigA; + } + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + + UpdateTexturePackDescription(m_currentTexturePackIndex); + + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + return S_OK; +} + +int CScene_LoadGameSettings::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_LoadGameSettings *pClass = (CScene_LoadGameSettings *)pParam; +#ifdef _XBOX + pClass->m_currentTexturePackIndex = pClass->m_pTexturePacksList->GetCurSel(); + // Exit with or without saving + // Decline means install full version of the texture pack in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultAccept) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=pClass->m_pTexturePacksList->GetData(pClass->m_currentTexturePackIndex); + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + } + else // trial version + { + // if there is no trial version, this is a Cancel + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + } +#endif + pClass->m_bIgnoreInput=false; + return 0; +} + +HRESULT CScene_LoadGameSettings::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + // update the tooltips + int iRB=-1; +#ifdef _XBOX + +#endif + CXuiSceneBase::SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1,-1,-1,-1,iRB); + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_LoadSettings.h b/Minecraft.Client/Common/XUI/XUI_LoadSettings.h new file mode 100644 index 0000000..61e934b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_LoadSettings.h @@ -0,0 +1,154 @@ +#pragma once +#include "../media/xuiscene_load_settings.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_CustomMessages.h" +#include "XUI_MultiGameLaunchMoreOptions.h" + +class CScene_LoadGameSettings : public CXuiSceneImpl +{ +public: + typedef struct + { + unsigned int uiLen; + unsigned int uiCode; + } + PNG_CHUNK; +protected: + // Control and Element wrapper objects. + CXuiScene m_MainScene; + CXuiScene m_TexturePackDetails; + CXuiControl m_GameName; + CXuiControl m_GameSeed; + CXuiControl m_GameCreatedMode; + CXuiControl m_ButtonLoad; + CXuiControl m_ButtonGameMode; + CXuiControl m_MoreOptions; + CXuiCtrl4JIcon m_GameIcon; + CXuiCtrlSliderWrapper m_SliderDifficulty; + CXuiCtrl4JList *m_pTexturePacksList; + CXuiControl m_texturePackTitle, m_texturePackDescription; + CXuiCtrl4JIcon *m_texturePackIcon, *m_texturePackComparison; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_TRANSITION_END(OnTransitionEnd) + XUI_ON_XM_FONTRENDERERCHANGE_MESSAGE(OnFontRendererChange) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_NOTIFY_KILL_FOCUS( OnNotifyKillFocus ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_MainScene, m_MainScene) + BEGIN_MAP_CHILD_CONTROLS(m_MainScene) + MAP_CONTROL(IDC_XuiLoadSettings, m_ButtonLoad) + MAP_CONTROL(IDC_XuiGameModeToggle, m_ButtonGameMode) + MAP_CONTROL(IDC_XuiMoreOptions, m_MoreOptions) + MAP_CONTROL(IDC_XuiGameIcon, m_GameIcon); + MAP_CONTROL(IDC_XuiGameName, m_GameName) + MAP_CONTROL(IDC_XuiGameSeed, m_GameSeed) + MAP_CONTROL(IDC_XuiCreatedMode, m_GameCreatedMode) + MAP_CONTROL(IDC_XuiSliderDifficulty, m_SliderDifficulty) + MAP_OVERRIDE(IDC_TexturePacksList, m_pTexturePacksList) + //MAP_CONTROL(IDC_XuiGameMode, m_GameMode) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TexturePackDetails, m_TexturePackDetails) + BEGIN_MAP_CHILD_CONTROLS(m_TexturePackDetails) + MAP_CONTROL(IDC_TexturePackName, m_texturePackTitle) + MAP_CONTROL(IDC_TexturePackDescription, m_texturePackDescription) + MAP_OVERRIDE(IDC_Icon, m_texturePackIcon) + MAP_OVERRIDE(IDC_ComparisonPic, m_texturePackComparison) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnFontRendererChange(); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnDestroy(); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + + + static int LoadSaveDataReturned(void *pParam,bool bContinue); + static int LoadSaveDataForNameChangeReturned(void *pParam,bool bContinue); + static int DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static void StartGameFromSave(CScene_LoadGameSettings* pClass, DWORD dwLocalUsersMask); + static int StartGame_SignInReturned(void *pParam,bool bContinue, int iPad); + static int DeviceSelectReturned(void *pParam,bool bContinue); + static int ConfirmLoadReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDataReturned(void *pParam,bool bSuccess); + static int CheckResetNetherReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + static int Progress(void *pParam,float fProgress); + + void LoadLevelGen(LevelGenerationOptions *levelGen); + + HRESULT LaunchGame(void); +public: + static unsigned char szPNG[8]; + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_LoadGameSettings, L"CScene_LoadGameSettings", XUI_CLASS_SCENE ) + +private: + static int UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + void UpdateTexturePackDescription(int index); + void ClearTexturePackDescription(); + void UpdateCurrentTexturePack(); + + bool m_bIgnoreInput; + HXUIBRUSH m_hXuiBrush; + HXUIBRUSH m_hTexturePackIconBrush; + HXUIBRUSH m_hTexturePackComparisonBrush; + + int m_iPad; + int m_iSaveGameInfoIndex; + bool m_bMultiplayerAllowed; + static int m_iDifficultyTitleSettingA[4]; + int m_CurrentDifficulty; + bool m_bHasBeenInCreative; + bool m_bSetup; + bool m_texturePackDescDisplayed; + + DWORD m_dwSaveFileC; +#ifdef _XBOX + C4JStorage::CACHEINFOSTRUCT *m_InfoA; +#endif + unsigned char m_szSeed[50]; + XCONTENT_DATA m_XContentData; + LaunchMoreOptionsMenuInitData m_MoreOptionsParams; + bool m_bGameModeSurvival; + unsigned int m_currentTexturePackIndex; + DLCPack * m_pDLCPack; + LevelGenerationOptions *m_levelGen; + + int m_iTexturePacksNotInstalled; + int *m_iConfigA; // track the texture packs that we don't have installed + LoadMenuInitData *m_params; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MainMenu.cpp b/Minecraft.Client/Common/XUI/XUI_MainMenu.cpp new file mode 100644 index 0000000..7b9c1a5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MainMenu.cpp @@ -0,0 +1,1288 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\XUI\XUI_MainMenu.h" +#include "..\..\..\Minecraft.Client\SurvivalMode.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h" +#include "..\..\LocalPlayer.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\User.h" +//#include "XUI_CreateLoad.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\Font.h" +#include "..\..\Common\GameRules\ConsoleGameRules.h" + +#define DLC_INSTALLED_TIMER_ID 1 +#define DLC_INSTALLED_TIMER_TIME 100 +#define TMS_TIMER_ID 2 +#define TMS_TIMER_TIME 100 +Random *CScene_Main::random = new Random(); + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Main::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_Buttons[BUTTON_PLAYGAME],app.GetString(IDS_PLAY_GAME)); + XuiControlSetText(m_Buttons[BUTTON_LEADERBOARDS],app.GetString(IDS_LEADERBOARDS)); + XuiControlSetText(m_Buttons[BUTTON_ACHIEVEMENTS],app.GetString(IDS_ACHIEVEMENTS)); + XuiControlSetText(m_Buttons[BUTTON_HELPANDOPTIONS],app.GetString(IDS_HELP_AND_OPTIONS)); + XuiControlSetText(m_Buttons[BUTTON_UNLOCKFULLGAME],app.GetString(IDS_UNLOCK_FULL_GAME)); + XuiControlSetText(m_Buttons[BUTTON_EXITGAME],app.GetString(IDS_EXIT_GAME)); + + m_Timer.SetShow(FALSE); + m_eAction=eAction_None; + + // Display the tooltips + HRESULT hr = S_OK; + ui.SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT); + + // can't set presence until someone is signed in and playing + + // Need to check which menu items to display + + // Are we the trial version? + // store the exitgame position + m_Buttons[BUTTON_EXITGAME].GetPosition(&m_vPosExitGame); + + if(ProfileManager.IsFullVersion()) + { + // Replace the Unlock Full Game with Downloadable Content + m_Buttons[BUTTON_UNLOCKFULLGAME].SetText(app.GetString(IDS_DOWNLOADABLECONTENT)); + XuiElementSetShow(m_Buttons[BUTTON_UNLOCKFULLGAME],TRUE); + } + + // Do we have downloadable content? - We need to have this in for a Pre-Cert test, whether or not we have DLC at the time. + + + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + // load from the .xzp file + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/splashes.txt"); + + BYTE *splashesData; + UINT splashesSize; + hr = XuiResourceLoadAllNoLoc(szResourceLocator, &splashesData, &splashesSize); + + if( HRESULT_SUCCEEDED( hr ) ) + { + //BufferedReader *br = new BufferedReader(new InputStreamReader(InputStream::getResourceAsStream(L"res\\title\\splashes.txt"))); //, Charset.forName("UTF-8") + byteArray splashesArray(splashesSize); + memcpy(splashesArray.data, splashesData, splashesSize); + ByteArrayInputStream *bais= new ByteArrayInputStream(splashesArray); + InputStreamReader *isr = new InputStreamReader( bais ); + BufferedReader *br = new BufferedReader( isr ); + + wstring line = L""; + while ( !(line = br->readLine()).empty() ) + { + line = trimString( line ); + if (line.length() > 0) + { + m_splashes.push_back(line); + } + } + + XuiFree(splashesData); // Frees copy returned from XuiResourceLoadAllNoLoc + + br->close(); + delete br; + delete isr; + delete bais; // Frees copy made in splashesArray + } + + XuiElementGetBounds(m_Subtitle,&m_fSubtitleWidth, &m_fSubtitleHeight); + +#if 1 + XuiElementSetShow(m_Subtitle, FALSE); + XuiElementSetShow(m_SubtitleMCFont, TRUE); +#else + XuiElementSetShow(m_Subtitle, TRUE); + XuiElementSetShow(m_SubtitleMCFont, FALSE); +#endif + + m_bIgnorePress=false; + + // 4J Stu - Clear out any loaded game rules + app.setLevelGenerationOptions(NULL); + + // Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + return S_OK; +} + +HRESULT CScene_Main::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + // If we sign out when in the saves list, we get a notifysetfocus in the saves list after the init of the main menu + ui.SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT); + + return S_OK; +} + + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Main::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // should we ignore the button press? This will be set if we're waiting for a callback from a function launched from a button press + if(m_bIgnorePress) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + //Minecraft *pMinecraft=Minecraft::GetInstance(); + + while((uiButtonCounterUserIndex); + ProfileManager.SetLockedProfile(-1); + + // Determine which button was pressed, + // and call the appropriate function. + switch(uiButtonCounter) + { + case BUTTON_PLAYGAME: + // Move to the new/load game screen + // need a signed in user here + + ProfileManager.SetCurrentGameActivity(pNotifyPressData->UserIndex,CONTEXT_PRESENCE_MENUS,true); + + m_eAction=eAction_RunGame; + if(ProfileManager.IsSignedIn(pNotifyPressData->UserIndex)) + { + RunPlayGame(pNotifyPressData->UserIndex); + } + else + { + // get them to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + StorageManager.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_Main::MustSignInReturned,this, app.GetStringTable()); + } + + break; + + case BUTTON_LEADERBOARDS: + m_eAction=eAction_RunLeaderboards; + if(ProfileManager.IsSignedIn(pNotifyPressData->UserIndex)) + { + RunLeaderboards(pNotifyPressData->UserIndex); + } + else + { + // get them to sign in + //ProfileManager.RequestSignInUI(false, false, true,false,true, &CScene_Main::Leaderboards_SignInReturned, this); + // get them to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + StorageManager.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_Main::MustSignInReturned,this, app.GetStringTable()); + + } + break; + case BUTTON_ACHIEVEMENTS: + m_eAction=eAction_RunAchievements; + if(ProfileManager.IsSignedIn(pNotifyPressData->UserIndex)) + { + RunAchievements(pNotifyPressData->UserIndex); + } + else + { + // get them to sign in + //ProfileManager.RequestSignInUI(false, false, true,false,true,&CScene_Main::Achievements_SignInReturned,this ); + // get them to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + StorageManager.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_Main::MustSignInReturned,this, app.GetStringTable()); + + } + + break; + case BUTTON_HELPANDOPTIONS: + // need a signed in user here, so we have a profile to write to + ProfileManager.SetLockedProfile(pNotifyPressData->UserIndex); + + m_eAction=eAction_RunHelpAndOptions; + if(ProfileManager.IsSignedIn(pNotifyPressData->UserIndex)) + { + RunHelpAndOptions(pNotifyPressData->UserIndex); + } + else + { + // get them to sign in + //ProfileManager.RequestSignInUI(false, false, true,false,true,&CScene_Main::HelpAndOptions_SignInReturned,this ); + // get them to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + StorageManager.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_Main::MustSignInReturned,this, app.GetStringTable()); + + } + break; + // case BUTTON_RETURNTOARCADE: + // break; + case BUTTON_UNLOCKFULLGAME: + { + // need a signed in user here + ProfileManager.SetLockedProfile(pNotifyPressData->UserIndex); + + m_eAction=eAction_RunUnlockOrDLC; + if(ProfileManager.IsSignedIn(pNotifyPressData->UserIndex)) + { + RunUnlockOrDLC(pNotifyPressData->UserIndex); + } + else + { + // get them to sign in + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + StorageManager.RequestMessageBox(IDS_MUST_SIGN_IN_TITLE, IDS_MUST_SIGN_IN_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_Main::MustSignInReturned,this, app.GetStringTable()); + } + } + break; + case BUTTON_EXITGAME: + if( ProfileManager.IsFullVersion() ) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CANCEL; + uiIDA[1]=IDS_OK; + StorageManager.RequestMessageBox(IDS_WARNING_ARCADE_TITLE, IDS_WARNING_ARCADE_TEXT, uiIDA, 2, XUSER_INDEX_ANY,&CScene_Main::ExitGameReturned,this); + } + else + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_TrialExitUpsell); + } + break; + + default: + break; + } + + + + return S_OK; +} + +HRESULT CScene_Main::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT); + CXuiSceneBase::ShowLogo(DEFAULT_XUI_MENU_USER,TRUE); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + // unlock the locked profile - anyone can navigate the main menu + ProfileManager.SetLockedProfile(-1); + + // incase the debug trial version has been set + // Are we the trial version? + + m_Buttons[BUTTON_EXITGAME].GetPosition(&m_vPosExitGame); + + for(int i=0;idwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - remove the "hobo humping" message legal (Sony) say we can't have - pretty sure Microsoft would say the same if they noticed it. + int splashIndex = eSplashRandomStart + 1 + random->nextInt( (int)m_splashes.size() - (eSplashRandomStart + 1) ); + + // Override splash text on certain dates + SYSTEMTIME LocalSysTime; + GetLocalTime( &LocalSysTime ); + if (LocalSysTime.wMonth == 11 && LocalSysTime.wDay == 9) + { + splashIndex = eSplashHappyBirthdayEx; + } + else if (LocalSysTime.wMonth == 6 && LocalSysTime.wDay == 1) + { + splashIndex = eSplashHappyBirthdayNotch; + } + else if (LocalSysTime.wMonth == 12 && LocalSysTime.wDay == 24) // the Java game shows this on Christmas Eve, so we will too + { + splashIndex = eSplashMerryXmas; + } + else if (LocalSysTime.wMonth == 1 && LocalSysTime.wDay == 1) + { + splashIndex = eSplashHappyNewYear; + } + //splashIndex = 47; // Very short string + //splashIndex = 197; // Very long string + //splashIndex = 296; // Coloured + //splashIndex = 297; // Noise + wstring splash = m_splashes.at( splashIndex ); + m_Subtitle.SetText(splash.c_str()); + m_SubtitleMCFont.SetText(splash.c_str()); + +#ifndef OVERRIDE_XUI_FONT_RENDERER + XUIRect xuiRect; + HRESULT hr=S_OK; + float fWidth,fHeight; + + HXUIOBJ visual=NULL; + HXUIOBJ pulser, subtitle, text; + hr=XuiControlGetVisual(m_Subtitle.m_hObj,&visual); + hr=XuiElementGetChildById(visual,L"Pulser",&pulser); + hr=XuiElementGetChildById(pulser,L"SubTitle",&subtitle); + hr=XuiElementGetChildById(subtitle,L"Text_String",&text); + + memset(&xuiRect, 0, sizeof(xuiRect)); + // Start with a base size + XuiElementSetBounds(m_Subtitle,m_fSubtitleWidth, m_fSubtitleHeight); + hr=XuiTextPresenterMeasureText(text, splash.c_str(), &xuiRect); + XuiElementGetBounds(text,&fWidth, &fHeight); + + float diff = fWidth / (xuiRect.right+5); + + diff = min(diff,MAIN_MENU_MAX_TEXT_SCALE); + + // Resize + XuiElementGetBounds(m_Subtitle,&fWidth, &fHeight); + XuiElementSetBounds(m_Subtitle,fWidth/diff, fHeight); + + // Scale + D3DXVECTOR3 vScale(diff,diff,0); + XuiElementSetScale(m_Subtitle,&vScale); + + //Adjust pivot for animation + D3DXVECTOR3 vPivot; + XuiElementGetPivot(subtitle,&vPivot); + vPivot.x = vPivot.x + ( ( (fWidth/diff) - fWidth) / 2 ); + XuiElementSetPivot(subtitle,&vPivot); + + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fTextVisualLen; + float fMaxButton; + + hr=XuiControlGetVisual(m_Buttons[0].m_hObj,&visual); + hr=XuiElementGetChildById(visual,L"text_Label",&text); + hr=XuiElementGetBounds(text,&fTextVisualLen,&fHeight); + m_Buttons[0].GetBounds(&fMaxButton,&fHeight); + + + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + } + + if(fTextVisualLenhObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } +#endif + return S_OK; +} + +HRESULT CScene_Main::OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Don't set handled to true. + + return S_OK; +} + +///////////////////////////////////////////////////////////// + +int CScene_Main::SignInReturned(void *pParam,bool bContinue) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + + if(bContinue==true) + { + StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,pClass); + } + + + return 0; +} + +int CScene_Main::DeviceSelectReturned(void *pParam,bool bContinue) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + //HRESULT hr; + + if(bContinue==true) + { + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); + // check for DLC + // start timer to track DLC check finished + pClass->m_Timer.SetShow(TRUE); + XuiSetTimer(pClass->m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); + } + else + { + // unlock the profile + ProfileManager.SetLockedProfile(-1); + ProfileManager.SetPrimaryPad(-1); + for(int i=0;iuser->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + if(ProfileManager.IsFullVersion()) + { + if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,pClass)) + { + // save device already selected + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); + + // check for DLC + // start timer to track DLC check finished + pClass->m_Timer.SetShow(TRUE); + XuiSetTimer(pClass->m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); + } + } + else + { + // 4J-PB - if this is the trial game, we can't have any networking + // Can't apply the player's settings here - they haven't come back from the QuerySignInStatud call above yet. + // Need to let them action in the main loop when they come in + // ensure we've applied this player's settings + //app.ApplyGameSettingsChanged(iPad); + // go straight in to the trial level + LoadTrial(); + } + } + else + { + // force a sign-in - they were offline, and they want to be online, so don't let it display offline players + // set the bAddUser to false to allow offline to go online by selecting the already signed in player again + ProfileManager.RequestSignInUI(false, false, true,false,false,&CScene_Main::CreateLoad_SignInReturned,pClass, ProfileManager.GetPrimaryPad() ); + } + + return 0; +} + +int CScene_Main::CreateLoad_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + + if(bContinue==true) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + if(ProfileManager.IsGuest(ProfileManager.GetPrimaryPad())) + { + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + ProfileManager.SetLockedProfile(ProfileManager.GetPrimaryPad()); + + + // change the minecraft player name + Minecraft::GetInstance()->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + if(ProfileManager.IsFullVersion()) + { + // Check if we're signed in to LIVE + if(ProfileManager.IsSignedInLive(iPad)) + { + // 4J-PB - Need to check for installed DLC + if(!app.DLCInstallProcessCompleted()) app.StartInstallDLCProcess(iPad); + + if(ProfileManager.IsGuest(iPad)) + { + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + // check if all the TMS files are loaded + if(app.GetTMSDLCInfoRead() && app.GetTMSXUIDsFileRead() && app.GetBanListRead(iPad)) + { + if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,pClass)==true) + { + // save device already selected + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(ProfileManager.GetPrimaryPad()); + // check for DLC + // start timer to track DLC check finished + pClass->m_Timer.SetShow(TRUE); + XuiSetTimer(pClass->m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + //app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_MultiGameJoinLoad); + } + } + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + + // block all input + pClass->m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;im_Buttons[i].SetShow(FALSE); + } + + // turn off tooltips + ui.SetTooltips(DEFAULT_XUI_MENU_USER, -1); + + pClass->m_Timer.SetShow(TRUE); + } + } + } + else + { + // offline + ProfileManager.DisplayOfflineProfile(&CScene_Main::CreateLoad_OfflineProfileReturned,pClass, ProfileManager.GetPrimaryPad() ); + } + } + else + { + // 4J-PB - if this is the trial game, we can't have any networking + // Can't apply the player's settings here - they haven't come back from the QuerySignInStatud call above yet. + // Need to let them action in the main loop when they come in + // ensure we've applied this player's settings + //app.ApplyGameSettingsChanged(iPad); + + // go straight in to the trial level + LoadTrial(); + } + } + } + else + { + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_eAction) + { + case eAction_RunGame: + ProfileManager.RequestSignInUI(false, true, false,false,true,&CScene_Main::CreateLoad_SignInReturned,pClass ,iPad); + break; + case eAction_RunLeaderboards: + ProfileManager.RequestSignInUI(false, false, true,false,true, &CScene_Main::Leaderboards_SignInReturned, pClass,iPad); + break; + case eAction_RunAchievements: + ProfileManager.RequestSignInUI(false, false, true,false,true,&CScene_Main::Achievements_SignInReturned,pClass,iPad ); + break; + case eAction_RunHelpAndOptions: + ProfileManager.RequestSignInUI(false, false, true,false,true,&CScene_Main::HelpAndOptions_SignInReturned,pClass,iPad ); + break; + case eAction_RunUnlockOrDLC: + ProfileManager.RequestSignInUI(false, false, true,false,true,&CScene_Main::UnlockFullGame_SignInReturned,pClass,iPad ); + break; + + } + } + else + { + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;im_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;im_Buttons[i].SetShow(FALSE); + } + + // turn off tooltips + ui.SetTooltips(DEFAULT_XUI_MENU_USER, -1); + + pClass->m_Timer.SetShow(TRUE); + } + } + else + { + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;iRunUnlockOrDLC(iPad); + } + else + { + // unlock the profile + ProfileManager.SetLockedProfile(-1); + for(int i=0;iseed = 0; + param->saveData = NULL; + param->settings = app.GetGameHostOption( eGameHostOption_Tutorial ); + + vector *generators = app.getLevelGenerators(); + param->levelGen = generators->at(0); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = ProfileManager.GetPrimaryPad(); + loadingParams->completionData = completionData; + + CXuiSceneBase::ShowTrialTimer(TRUE); + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + + + +void CScene_Main::RunPlayGame(int iPad) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + app.ReleaseSaveThumbnail(); + + if(ProfileManager.IsGuest(iPad)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + ProfileManager.SetLockedProfile(iPad); + + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + // 4J-PB - Need to check for installed DLC + if(!app.DLCInstallProcessCompleted()) app.StartInstallDLCProcess(iPad); + + if(ProfileManager.IsFullVersion()) + { + // are we offline? + if(!ProfileManager.IsSignedInLive(iPad)) + { + + ProfileManager.DisplayOfflineProfile(&CScene_Main::CreateLoad_OfflineProfileReturned,this,iPad ); + } + else + { + // Check if there is any new DLC + app.ClearNewDLCAvailable(); + StorageManager.GetAvailableDLCCount(iPad); + + // check if all the TMS files are loaded + if(app.GetTMSDLCInfoRead() && app.GetTMSXUIDsFileRead() && app.GetBanListRead(iPad)) + { + if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,this)==true) + { + // change the minecraft player name + pMinecraft->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + // save device already selected + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + // check for DLC + // start timer to track DLC check finished + m_Timer.SetShow(TRUE); + XuiSetTimer(m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + //app.NavigateToScene(iPad,eUIScene_MultiGameJoinLoad); + } + } + else + { + // Changing to async TMS calls + + // flag the timer to start the TMS calls when there is nothing happening with TMS. + // fix for X360 - 162325 - TCR 001: BAS Game Stability: TU17: The game goes into an infinite loading upon entering the Play Game menu shortly after visiting the Minecraft Store + if(app.GetTMSAction(iPad)!=eTMSAction_Idle) + { + XuiSetTimer(m_hObj,TMS_TIMER_ID,TMS_TIMER_TIME); + } + else + { + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + } + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;iuser->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + + // Can't apply the player's settings here - they haven't come back from the QuerySignInStatud call above yet. + // Need to let them action in the main loop when they come in + // ensure we've applied this player's settings + //app.ApplyGameSettingsChanged(iPad); + LoadTrial(); + } + } +} + +HRESULT CScene_Main::OnTMSBanFileRetrieved() +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + int iPad=ProfileManager.GetLockedProfile(); + + if(StorageManager.SetSaveDevice(&CScene_Main::DeviceSelectReturned,this)==true) + { + // change the minecraft player name + pMinecraft->user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad())); + // save device already selected + + // ensure we've applied this player's settings + app.ApplyGameSettingsChanged(iPad); + // check for DLC + // start timer to track DLC check finished + m_Timer.SetShow(TRUE); + XuiSetTimer(m_hObj,DLC_INSTALLED_TIMER_ID,DLC_INSTALLED_TIMER_TIME); + //app.NavigateToScene(iPad,eUIScene_MultiGameJoinLoad); + } + return S_OK; +} + +void CScene_Main::RunLeaderboards(int iPad) +{ + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // guests can't look at leaderboards + if(ProfileManager.IsGuest(iPad)) + { + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else if(!ProfileManager.IsSignedInLive(iPad)) + { + StorageManager.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + } + else + { + ProfileManager.SetLockedProfile(iPad); + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + + app.NavigateToScene(iPad, eUIScene_LeaderboardsMenu); + } +} +void CScene_Main::RunAchievements(int iPad) +{ + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // guests can't look at achievements + if(ProfileManager.IsGuest(iPad)) + { + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + XShowAchievementsUI( iPad ); + } +} +void CScene_Main::RunHelpAndOptions(int iPad) +{ + if(ProfileManager.IsGuest(iPad)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + // If the player was signed in before selecting play, we'll not have read the profile yet, so query the sign-in status to get this to happen + ProfileManager.QuerySigninStatus(); + + // 4J-PB - You can be offline and still can go into help and options + if(app.GetTMSDLCInfoRead() || !ProfileManager.IsSignedInLive(iPad)) + { + app.NavigateToScene(iPad,eUIScene_HelpAndOptionsMenu); + } + else + { + // Changing to async TMS calls + app.SetTMSAction(iPad,eTMSAction_TMSPP_RetrieveFiles_HelpAndOptions); + + // block all input + m_bIgnorePress=true; + // We want to hide everything in this scene and display a timer until we get a completion for the TMS files + for(int i=0;iRecordUpsellPresented(iPad, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + ProfileManager.DisplayFullVersionPurchase(false,iPad,eSen_UpsellID_Full_Version_Of_Game); + } + } +} + +int CScene_Main::TMSReadFileListReturned(void *pParam,int iPad,C4JStorage::PTMSPP_FILE_LIST pTmsFileList) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + + // push the file details in to a unordered map if they are not already in there +// for(int i=0;iiCount;i++) +// { +// app.PutTMSPP_FileSize(filenametowstring(pTmsFileList->FileDetailsA[i].szFilename),pTmsFileList->FileDetailsA[i].iFileSize); +// } + return 0; +} + +int CScene_Main::TMSFileWriteReturned(void *pParam,int iPad,int iResult) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + + // push the file details in to a unordered map if they are not already in there + // for(int i=0;iiCount;i++) + // { + // app.PutTMSPP_FileSize(filenametowstring(pTmsFileList->FileDetailsA[i].szFilename),pTmsFileList->FileDetailsA[i].iFileSize); + // } + return 0; +} + +int CScene_Main::TMSFileReadReturned(void *pParam,int iPad,C4JStorage::PTMSPP_FILEDATA pData) +{ + CScene_Main* pClass = (CScene_Main*)pParam; + + // push the file details in to a unordered map if they are not already in there + // for(int i=0;iiCount;i++) + // { + // app.PutTMSPP_FileSize(filenametowstring(pTmsFileList->FileDetailsA[i].szFilename),pTmsFileList->FileDetailsA[i].iFileSize); + // } + return 0; +} + +HRESULT CScene_Main::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly + if( pTimer->nId == DLC_INSTALLED_TIMER_ID) + { + if(!app.DLCInstallPending()) + { + XuiKillTimer(m_hObj,DLC_INSTALLED_TIMER_ID); + m_Timer.SetShow(FALSE); + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadOrJoinMenu); + } + } + + else if( pTimer->nId == TMS_TIMER_ID) + { + if(app.GetTMSAction(ProfileManager.GetPrimaryPad())==eTMSAction_Idle) + { + app.SetTMSAction(ProfileManager.GetPrimaryPad(),eTMSAction_TMSPP_RetrieveFiles_RunPlayGame); + XuiKillTimer(m_hObj,TMS_TIMER_ID); + } + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MainMenu.h b/Minecraft.Client/Common/XUI/XUI_MainMenu.h new file mode 100644 index 0000000..2e4279e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MainMenu.h @@ -0,0 +1,128 @@ +#pragma once + +#include "../media/xuiscene_main.h" +#include "XUI_CustomMessages.h" + +#define BUTTON_PLAYGAME 0 +#define BUTTON_LEADERBOARDS 1 +#define BUTTON_ACHIEVEMENTS 2 +#define BUTTON_HELPANDOPTIONS 3 +#define BUTTON_UNLOCKFULLGAME 4 +#define BUTTON_EXITGAME 5 +#define BUTTONS_MAX BUTTON_EXITGAME + 1 + +#define MAIN_MENU_MAX_TEXT_SCALE 1.5f + +class Random; + +class CScene_Main : public CXuiSceneImpl +{ +private: + static Random *random; + vector m_splashes; + D3DXVECTOR3 m_vPosExitGame; + bool m_bIgnorePress; + float m_fSubtitleHeight, m_fSubtitleWidth; + CXuiControl m_Timer; + + // 4J Added + enum eSplashIndexes + { + eSplashHappyBirthdayEx = 0, + eSplashHappyBirthdayNotch, + eSplashMerryXmas, + eSplashHappyNewYear, + + // The start index in the splashes vector from which we can select a random splash + eSplashRandomStart, + }; + + enum eActions + { + eAction_None=0, + eAction_RunGame, + eAction_RunLeaderboards, + eAction_RunAchievements, + eAction_RunHelpAndOptions, + eAction_RunUnlockOrDLC, + }; + +protected: + // Control and Element wrapper objects. + CXuiScene m_Scene; + CXuiControl m_Buttons[BUTTONS_MAX]; + CXuiControl m_Subtitle, m_SubtitleMCFont; + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_SET_FOCUS(OnNotifySetFocus) + XUI_ON_XM_TMS_BANFILE_RETRIEVED_MESSAGE(OnTMSBanFileRetrieved) + XUI_ON_XM_TMS_DLCFILE_RETRIEVED_MESSAGE(OnTMSDLCFileRetrieved) + XUI_ON_XM_TIMER( OnTimer ) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiButton1, m_Buttons[BUTTON_PLAYGAME]) + MAP_CONTROL(IDC_XuiButton2, m_Buttons[BUTTON_LEADERBOARDS ]) + MAP_CONTROL(IDC_XuiButton3, m_Buttons[BUTTON_ACHIEVEMENTS ]) + MAP_CONTROL(IDC_XuiButton4, m_Buttons[BUTTON_HELPANDOPTIONS]) + MAP_CONTROL(IDC_XuiButton5, m_Buttons[BUTTON_UNLOCKFULLGAME]) + MAP_CONTROL(IDC_XuiButton6, m_Buttons[BUTTON_EXITGAME]) + MAP_CONTROL(IDC_XuiSplash, m_Subtitle) + MAP_CONTROL(IDC_XuiSplashMCFont, m_SubtitleMCFont) + MAP_CONTROL(IDC_Timer, m_Timer) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnTMSBanFileRetrieved(); + HRESULT OnTMSDLCFileRetrieved( ); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + + int SetSaveDevice(); + static void LoadTrial(); + + void RunPlayGame(int iPad); + void RunLeaderboards(int iPad); + void RunAchievements(int iPad); + void RunHelpAndOptions(int iPad); + void RunUnlockOrDLC(int iPad); + + eActions m_eAction; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Main, L"CScene_Main", XUI_CLASS_SCENE ) + + static int SignInReturned(void *pParam,bool bContinue); + static int CreateLoad_SignInReturned(void *pParam,bool bContinue, int iPad); + static int CreateLoad_OfflineProfileReturned(void *pParam,bool bContinue, int iPad); + static int DeviceSelectReturned(void *pParam,bool bContinue); + static int SaveGameReturned(void *pParam,bool bContinue); + static int HelpAndOptions_SignInReturned(void *pParam,bool bContinue,int iPad); + static int ExitGameReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int AchievementsDeviceSelectReturned(void *pParam,bool bContinue); + static int Achievements_SignInReturned(void *pParam,bool bContinue,int iPad); + static int Leaderboards_SignInReturned(void* pParam, bool bContinue, int iPad); + static int UnlockFullGame_SignInReturned(void *pParam,bool bContinue,int iPad); + static int MustSignInReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#ifdef _XBOX + static int TMSReadFileListReturned(void *pParam,int iPad,C4JStorage::PTMSPP_FILE_LIST pTmsFileList); + static int TMSFileWriteReturned(void *pParam,int iPad,int iResult); + static int TMSFileReadReturned(void *pParam,int iPad,C4JStorage::PTMSPP_FILEDATA pData); +#endif +}; diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.cpp b/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.cpp new file mode 100644 index 0000000..b3608b0 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.cpp @@ -0,0 +1,1397 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XUI_MultiGameCreate.h" +#include "XUI_Controls.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "XUI_MultiGameLaunchMoreOptions.h" +#include "..\..\..\Minecraft.World\BiomeSource.h" +#include "..\..\..\Minecraft.World\IntCache.h" +#include "..\..\..\Minecraft.World\LevelType.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\DLC\DLCLocalisationFile.h" +#include "..\..\StringTable.h" +#include "..\..\DLCTexturePack.h" + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 1 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 100 + +int CScene_MultiGameCreate::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameCreate::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_bSetup = false; + m_texturePackDescDisplayed = false; + m_iConfigA=NULL; + + WCHAR TempString[256]; + MapChildControls(); + + XuiControlSetText(m_EditWorldName,app.GetString(IDS_DEFAULT_WORLD_NAME)); + XuiControlSetText(m_MoreOptions,app.GetString(IDS_MORE_OPTIONS)); + XuiControlSetText(m_NewWorld,app.GetString(IDS_CREATE_NEW_WORLD)); + + XuiControlSetText(m_labelWorldName,app.GetString(IDS_WORLD_NAME)); + XuiControlSetText(m_labelSeed,app.GetString(IDS_CREATE_NEW_WORLD_SEED)); + XuiControlSetText(m_labelRandomSeed,app.GetString(IDS_CREATE_NEW_WORLD_RANDOM_SEED)); + XuiControlSetText(m_pTexturePacksList->m_hObj,app.GetString(IDS_DLC_MENU_TEXTUREPACKS)); + + CreateWorldMenuInitData *params = (CreateWorldMenuInitData *)pInitData->pvInitData; + + m_MoreOptionsParams.bGenerateOptions=TRUE; + m_MoreOptionsParams.bStructures=TRUE; + m_MoreOptionsParams.bFlatWorld=FALSE; + m_MoreOptionsParams.bBonusChest=FALSE; + m_MoreOptionsParams.bPVP = TRUE; + m_MoreOptionsParams.bTrust = TRUE; + m_MoreOptionsParams.bFireSpreads = TRUE; + m_MoreOptionsParams.bHostPrivileges = FALSE; + m_MoreOptionsParams.bTNT = TRUE; + m_MoreOptionsParams.iPad = params->iPad; + m_iPad=params->iPad; + delete params; + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + // 4J-PB - read the settings for the online flag. We'll only save this setting if the user changed it. + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineSettingChangedBySystem=false; + + // Set the text for friends of friends, and default to on + if( m_bMultiplayerAllowed) + { + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + if(bGameSetting_Online) + { + // The profile settings say Online, but either the player is offline, or they are not allowed to play online + m_MoreOptionsParams.bOnlineSettingChangedBySystem=true; + } + } + + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + + m_CurrentDifficulty=app.GetGameSettings(m_iPad,eGameSetting_Difficulty); + m_SliderDifficulty.SetValue(m_CurrentDifficulty); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[m_CurrentDifficulty])); + m_SliderDifficulty.SetText(TempString); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, FALSE ); + + // restrict the keyboard - don't want languages that are not supported, like cyrillic, etc. + switch(XGetLanguage()) + { + case XC_LANGUAGE_ENGLISH: + case XC_LANGUAGE_GERMAN: + case XC_LANGUAGE_FRENCH: + case XC_LANGUAGE_SPANISH: + case XC_LANGUAGE_ITALIAN: + case XC_LANGUAGE_PORTUGUESE: + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + case XC_LANGUAGE_KOREAN: + m_EditWorldName.SetKeyboardType(C_4JInput::EKeyboardMode_Default); + m_EditWorldName.SetKeyboardType(C_4JInput::EKeyboardMode_Default); + break; + default: + m_EditWorldName.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + m_EditWorldName.SetKeyboardType(C_4JInput::EKeyboardMode_Full); + break; + } + + m_NewWorld.SetEnable(true); + + m_EditWorldName.SetTextLimit(XCONTENT_MAX_DISPLAYNAME_LENGTH); + + wstring wWorldName = m_EditWorldName.GetText(); + + // set the caret to the end of the default text + m_EditWorldName.SetCaretPosition((int)wWorldName.length()); + // In the dashboard, there's room for about 30 W characters on two lines before they go over the top of things + m_EditWorldName.SetTextLimit(25); + + m_EditWorldName.SetTitleAndText(IDS_NAME_WORLD,IDS_NAME_WORLD_TEXT); + m_EditSeed.SetTitleAndText(IDS_CREATE_NEW_WORLD,IDS_CREATE_NEW_WORLD_SEEDTEXT); + + XuiSetTimer(m_hObj,GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME); + XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_CreateWorldMenu, 0); + + // 4J-PB - Load up any texture pack data we have locally in the XZP + for(int i=0;iSetSelectionChangedHandle(m_hObj); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + HRESULT hr; + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + ListInfo.fEnabled = TRUE; + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tp; + if(pDLCTexPack) + { + int id=pDLCTexPack->getDLCParentPackId(); + + if(id==0) + { + // default texture pack - should come first + ListInfo.iSortIndex=0x0FFFFFFF; + } + else + { + ListInfo.iSortIndex=id; + ListInfo.iData=id; + } + } + +#ifdef _DEBUG + app.DebugPrintf("TP - "); + OutputDebugStringW(tp->getName().c_str()); + app.DebugPrintf(", sort index - %d\n",ListInfo.iSortIndex); +#endif + + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + } + } + + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(0); + UpdateTexturePackDescription(m_currentTexturePackIndex); + + m_bSetup = true; + } + return S_OK; +} + + +HRESULT CScene_MultiGameCreate::OnDestroy() +{ + // clear out the texture pack data + for(int i=0;iUserIndex, VK_PAD_A); + + if(hObjPressed==m_NewWorld) + { + // Check if we need to upsell the texture pack + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { + // They've selected a texture pack they don't have yet + // upsell + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + // DLC might have been corrupt + if(ullOfferID_Full!=0LL) + { + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + // Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_MultiGameCreate::TexturePackDialogReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_MultiGameCreate::TexturePackDialogReturned,this,app.GetStringTable()); + } + + return S_OK; + } + } + } + + m_bIgnoreInput = true; + SetShow( FALSE ); + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && m_MoreOptionsParams.bOnlineGame; + + // if the profile data has been changed, then force a profile write (we save the online/invite/friends of friends settings) + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + // check the checkboxes + + // Only save the online setting if the user changed it - we may change it because we're offline, but don't want that saved + if(!m_MoreOptionsParams.bOnlineSettingChangedBySystem) + { + app.SetGameSettings(m_iPad,eGameSetting_Online,m_MoreOptionsParams.bOnlineGame?1:0); + } + app.SetGameSettings(m_iPad,eGameSetting_InviteOnly,m_MoreOptionsParams.bInviteOnly?1:0); + app.SetGameSettings(m_iPad,eGameSetting_FriendsOfFriends,m_MoreOptionsParams.bAllowFriendsOfFriends?1:0); + + app.CheckGameSettingsChanged(true,pNotifyPressData->UserIndex); + + // Check that we have the rights to use a texture pack we have selected. + if(m_MoreOptionsParams.dwTexturePack!=0) + { + // texture pack hasn't been set yet, so check what it will be + TexturePack *pTexturePack = pMinecraft->skins->getTexturePackById(m_MoreOptionsParams.dwTexturePack); + + if(pTexturePack==NULL) + { + // corrupt DLC so set it to the default textures + m_MoreOptionsParams.dwTexturePack=0; + } + else + { + m_pDLCPack=pTexturePack->getDLCPack(); + // do we have a license? + if(m_pDLCPack && !m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // no + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + if(!ProfileManager.IsSignedInLive(pNotifyPressData->UserIndex)) + { + // need to be signed in to live + StorageManager.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + return S_OK; + } + else + { + // upsell + + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_pDLCPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=pTexturePack->getDLCPack()->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(pNotifyPressData->UserIndex, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + // Give the player a warning about the trial version of the texture pack + StorageManager.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 1, pNotifyPressData->UserIndex,&CScene_MultiGameCreate::WarningTrialTexturePackReturned,this,app.GetStringTable()); + return S_OK; + } + } + } + } + + if(m_bGameModeSurvival != true || m_MoreOptionsParams.bHostPrivileges == TRUE) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + if(m_bGameModeSurvival != true) + { + StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_CREATIVE, uiIDA, 2, m_iPad,&CScene_MultiGameCreate::ConfirmCreateReturned,this,app.GetStringTable()); + } + else + { + StorageManager.RequestMessageBox(IDS_TITLE_START_GAME, IDS_CONFIRM_START_HOST_PRIVILEGES, uiIDA, 2, m_iPad,&CScene_MultiGameCreate::ConfirmCreateReturned,this,app.GetStringTable()); + } + } + else + { + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(isClientSide && connectedControllers > 1 && RenderManager.IsHiDef()) + { + ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameCreate::StartGame_SignInReturned, this,ProfileManager.GetPrimaryPad()); + } + else + { + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + //bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && m_MoreOptionsParams.bOnlineGame; + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + m_bIgnoreInput = false; + SetShow( TRUE ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + CreateGame(this, 0); + } + } + } + } + else if(hObjPressed==m_MoreOptions) + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_LaunchMoreOptionsMenu,&m_MoreOptionsParams); + } + else if(hObjPressed == m_ButtonGameMode) + { + if(m_bGameModeSurvival) + { + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_CREATIVE)); + m_bGameModeSurvival=false; + } + else + { + m_ButtonGameMode.SetText(app.GetString(IDS_GAMEMODE_SURVIVAL)); + m_bGameModeSurvival=true; + } + } + else if(hObjPressed == m_pTexturePacksList->m_hObj) + { + UpdateCurrentTexturePack(); + } + + return S_OK; +} + + +int CScene_MultiGameCreate::UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameCreate* pScene = (CScene_MultiGameCreate*)pParam; +#ifdef _XBOX + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + ULONGLONG ullIndexA[1]; + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(pScene->m_pDLCPack->getPurchaseOfferId()); + + if(pDLCInfo!=NULL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Full; + } + else + { + ullIndexA[0]=pScene->m_pDLCPack->getPurchaseOfferId(); + } + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( pScene->m_pDLCPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } +#endif + pScene->m_bIgnoreInput = false; + pScene->SetShow( TRUE ); + + return 0; +} + +int CScene_MultiGameCreate::WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameCreate* pScene = (CScene_MultiGameCreate*)pParam; + pScene->m_bIgnoreInput = false; + pScene->SetShow( TRUE ); + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pScene->m_MoreOptionsParams.bOnlineGame; + + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(isClientSide && connectedControllers > 1 && RenderManager.IsHiDef()) + { + ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameCreate::StartGame_SignInReturned, pScene,ProfileManager.GetPrimaryPad()); + } + else + { + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + // This is called from a storage manager thread... need to set up thread storage for IntCache as CreateGame requires this to search for a suitable seed if we haven't set a seed. + IntCache::CreateNewThreadStorage(); + CreateGame(pScene, 0); + IntCache::ReleaseThreadStorage(); + } + } + + return 0; +} + +HRESULT CScene_MultiGameCreate::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled) +{ + WCHAR TempString[256]; + + if(hObjSource == m_EditWorldName) + { + // Enable the done button when we have all of the necessary information + wstring wWorldName = m_EditWorldName.GetText(); + BOOL bHasWorldName = ( wWorldName.length()!=0); + m_NewWorld.SetEnable(bHasWorldName); + } + else if(hObjSource==m_SliderDifficulty.GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,pValueChangedData->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[pValueChangedData->nValue])); + m_SliderDifficulty.SetText(TempString); + } + + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest==NULL) + { + pControlNavigateData->hObjDest=pControlNavigateData->hObjSource; + } + + bHandled=TRUE; + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly + switch(pTimer->nId) + { + + + case GAME_CREATE_ONLINE_TIMER_ID: + { + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + bool bGameSetting_Online=(app.GetGameSettings(m_iPad,eGameSetting_Online)!=0); + m_MoreOptionsParams.bOnlineGame = bGameSetting_Online?TRUE:FALSE; + if(bGameSetting_Online) + { + m_MoreOptionsParams.bInviteOnly = (app.GetGameSettings(m_iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = (app.GetGameSettings(m_iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE; + } + else + { + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + } + else + { + m_MoreOptionsParams.bOnlineGame = FALSE; + m_MoreOptionsParams.bInviteOnly = FALSE; + m_MoreOptionsParams.bAllowFriendsOfFriends = FALSE; + } + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + // also check for any new texture packs info being available + // for each item in the mem list, check it's in the data list + + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + // for each iConfig, check if the data is available, and add it to the List, then remove it from the viConfig + for(int i=0;i 0 && pbData) + { + DWORD dwImageBytes=0; + PBYTE pbImageData=NULL; + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes ); + ListInfo.fEnabled = TRUE; + ListInfo.iData = m_iConfigA[i]; + HRESULT hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + app.DebugPrintf("Adding texturepack %d from TPD\n",m_iConfigA[i]); + + ListInfo.iSortIndex=m_iConfigA[i]; + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + + m_iConfigA[i]=-1; + } + } + } + + bool bAllDone=true; + for(int i=0;im_MoreOptionsParams.bOnlineGame; + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(isClientSide && connectedControllers > 1 && RenderManager.IsHiDef()) + { + ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameCreate::StartGame_SignInReturned, pClass,ProfileManager.GetPrimaryPad()); + } + else + { + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && noUGC ) + { + pClass->m_bIgnoreInput = false; + pClass->SetShow( TRUE ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + // This is called from a storage manager thread... need to set up thread storage for IntCache as CreateGame requires this to search for a suitable seed if we haven't set a seed. + IntCache::CreateNewThreadStorage(); + CreateGame(pClass, 0); + IntCache::ReleaseThreadStorage(); + } + } + } + else + { + pClass->m_bIgnoreInput = false; + pClass->SetShow( TRUE ); + } + return 0; +} + +int CScene_MultiGameCreate::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + CScene_MultiGameCreate* pClass = (CScene_MultiGameCreate*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad)) + { + DWORD dwLocalUsersMask = 0; + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool noPrivileges = false; + + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index); + } + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(isClientSide && (noPrivileges || noUGC) ) + { + if( noUGC ) + { + pClass->m_bIgnoreInput = false; + pClass->SetShow( TRUE ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_CREATE, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + pClass->m_bIgnoreInput = false; + pClass->SetShow( TRUE ); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_HOST_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + } + else + { + // This is NOT called from a storage manager thread, and is in fact called from the main thread in the Profile library tick. Therefore we use the main threads IntCache. + CreateGame(pClass, dwLocalUsersMask); + } + } + } + else + { + pClass->m_bIgnoreInput = false; + pClass->SetShow( TRUE ); + } + return 0; +} + +// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not +void CScene_MultiGameCreate::CreateGame(CScene_MultiGameCreate* pClass, DWORD dwLocalUsersMask) +{ + // stop the timer running that causes a check for new texture packs in TMS but not installed, since this will run all through the create game, and will crash if it tries to create an hbrush + XuiKillTimer(pClass->m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID); + + bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_MoreOptionsParams.bOnlineGame; + bool isPrivate = pClass->m_MoreOptionsParams.bInviteOnly?true:false; + + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + // create the world and launch + wstring wWorldName = pClass->m_EditWorldName.GetText(); + + StorageManager.ResetSaveData(); + // Make our next save default to the name of the level + StorageManager.SetSaveTitle((wchar_t *)wWorldName.c_str()); + + BOOL bHasSeed = (pClass->m_EditSeed.GetText() != NULL); + + wstring wSeed; + if(bHasSeed) + { + wSeed=pClass->m_EditSeed.GetText(); + } + else + { + // random + wSeed=L""; + } + + // start the game + bool isFlat = (pClass->m_MoreOptionsParams.bFlatWorld==TRUE); + __int64 seedValue = 0; //BiomeSource::findSeed(isFlat?LevelType::lvl_flat:LevelType::lvl_normal); // 4J - was (new Random())->nextLong() - now trying to actually find a seed to suit our requirements + + if (wSeed.length() != 0) + { + __int64 value = 0; + unsigned int len = (unsigned int)wSeed.length(); + + //Check if the input string contains a numerical value + bool isNumber = true; + for( unsigned int i = 0 ; i < len ; ++i ) + if( wSeed.at(i) < L'0' || wSeed.at(i) > L'9' ) + if( !(i==0 && wSeed.at(i) == L'-' ) ) + { + isNumber = false; + break; + } + + //If the input string is a numerical value, convert it to a number + if( isNumber ) + value = _fromString<__int64>(wSeed); + + //If the value is not 0 use it, otherwise use the algorithm from the java String.hashCode() function to hash it + if( value != 0 ) + seedValue = value; + else + { + int hashValue = 0; + for( unsigned int i = 0 ; i < len ; ++i ) + hashValue = 31 * hashValue + wSeed.at(i); + seedValue = hashValue; + } + + } + else + { + seedValue = BiomeSource::findSeed(isFlat?LevelType::lvl_flat:LevelType::lvl_normal); // 4J - was (new Random())->nextLong() - now trying to actually find a seed to suit our requirements + } + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = seedValue; + param->saveData = NULL; + param->texturePackId = pClass->m_MoreOptionsParams.dwTexturePack; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(pClass->m_MoreOptionsParams.dwTexturePack); + //pMinecraft->skins->updateUI(); + + app.SetGameHostOption(eGameHostOption_Difficulty,Minecraft::GetInstance()->options->difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,pClass->m_MoreOptionsParams.bAllowFriendsOfFriends); + app.SetGameHostOption(eGameHostOption_Gamertags,app.GetGameSettings(pClass->m_iPad,eGameSetting_GamertagsVisible)?1:0); + + app.SetGameHostOption(eGameHostOption_BedrockFog,app.GetGameSettings(pClass->m_iPad,eGameSetting_BedrockFog)?1:0); + +// CXuiList listObject; +// listObject.Attach( pClass->m_GameMode.GetListObject() ); + app.SetGameHostOption(eGameHostOption_GameType,pClass->m_bGameModeSurvival?GameType::SURVIVAL->getId():GameType::CREATIVE->getId()); + app.SetGameHostOption(eGameHostOption_LevelType,pClass->m_MoreOptionsParams.bFlatWorld ); + app.SetGameHostOption(eGameHostOption_Structures,pClass->m_MoreOptionsParams.bStructures ); + app.SetGameHostOption(eGameHostOption_BonusChest,pClass->m_MoreOptionsParams.bBonusChest ); + + app.SetGameHostOption(eGameHostOption_PvP,pClass->m_MoreOptionsParams.bPVP); + app.SetGameHostOption(eGameHostOption_TrustPlayers,pClass->m_MoreOptionsParams.bTrust ); + app.SetGameHostOption(eGameHostOption_FireSpreads,pClass->m_MoreOptionsParams.bFireSpreads ); + app.SetGameHostOption(eGameHostOption_TNT,pClass->m_MoreOptionsParams.bTNT ); + app.SetGameHostOption(eGameHostOption_HostCanFly,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger,pClass->m_MoreOptionsParams.bHostPrivileges); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible,pClass->m_MoreOptionsParams.bHostPrivileges ); + + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave time + app.SetAutosaveTimerTime(); + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +HRESULT CScene_MultiGameCreate::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) + { + m_SliderDifficulty.SetValueDisplay(FALSE); + } + + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY || + pTransition->dwTransType == XUI_TRANSITION_FROM || pTransition->dwTransType == XUI_TRANSITION_BACKFROM) + { + } + else if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + if(m_bSetup && m_texturePackDescDisplayed) + { + XUITimeline *timeline; + XUINamedFrame *startFrame, *endFrame; + GetTimeline( &timeline ); + startFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + endFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + m_texturePackDescDisplayed = true; + } + // 4J-PB - Need to check for installed DLC, which might have happened while you were on the info scene + if(pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // Can't call this here because if you back out of the load info screen and then go back in and load a game, it will attempt to use the dlc as it's running a mount of the dlc + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_pTexturePacksList->RemoveAllData(); + } + } + + } + + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ) +{ + if(hObjSource == m_pTexturePacksList->m_hObj) + { + UpdateTexturePackDescription(pNotifySelChangedData->iItem); + + // 4J-JEV: Removed expand description check, taken care of elsewhere. + } + + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + HXUIOBJ hSourceParent, hDestParent; + XuiElementGetParent(hObjSource,&hSourceParent); + XuiElementGetParent(pNotifyFocusData->hObjOther,&hDestParent); + if(hSourceParent != hDestParent && pNotifyFocusData->hObjOther != m_pTexturePacksList->m_hObj && hSourceParent == m_pTexturePacksList->m_hObj) + { + m_pTexturePacksList->SetCurSel(m_currentTexturePackIndex); + m_pTexturePacksList->SetTopItem(m_currentTexturePackIndex); // scroll the item into view if it's not visible + } + else if(!m_texturePackDescDisplayed && pNotifyFocusData->hObjOther == m_pTexturePacksList->m_hObj) + { + // 4J-JEV: Shouldn't we always do this? + //int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount(); + //if(texturePacksCount == 1) + //{ + XUITimeline *timeline; + XUINamedFrame *startFrame, *endFrame; + GetTimeline( &timeline ); + startFrame = timeline->FindNamedFrame( L"SlideOut" ); + endFrame = timeline->FindNamedFrame( L"SlideOutEnd" ); + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + m_texturePackDescDisplayed = true; + //} + } + + return S_OK; +} + +void CScene_MultiGameCreate::UpdateTexturePackDescription(int index) +{ + int iTexPackId=m_pTexturePacksList->GetData(index).iData; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(iTexPackId); + + if(tp==NULL) + { + // this is probably a texture pack icon added from TMS + + DWORD dwBytes=0,dwFileBytes=0; + PBYTE pbData=NULL,pbFileData=NULL; + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(index); + + app.GetTPD(ListItem.iData,&pbData,&dwBytes); + + app.GetFileFromTPD(eTPDFileType_Loc,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes > 0 && pbFileData) + { + StringTable *pStringTable = new StringTable(pbFileData, dwFileBytes); + m_texturePackTitle.SetText(pStringTable->getString(L"IDS_DISPLAY_NAME")); + m_texturePackDescription.SetText(pStringTable->getString(L"IDS_TP_DESCRIPTION")); + } + + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackIconBrush); + m_texturePackIcon->UseBrush(m_hTexturePackIconBrush); + } + app.GetFileFromTPD(eTPDFileType_Comparison,pbData,dwBytes,&pbFileData,&dwFileBytes ); + if(dwFileBytes >= 0 && pbFileData) + { + XuiCreateTextureBrushFromMemory(pbFileData,dwFileBytes,&m_hTexturePackComparisonBrush); + m_texturePackComparison->UseBrush(m_hTexturePackComparisonBrush); + } + else + { + m_texturePackComparison->UseBrush(NULL); + } + } + else + { + m_texturePackTitle.SetText(tp->getName().c_str()); + m_texturePackDescription.SetText(tp->getDesc1().c_str()); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hTexturePackIconBrush); + m_texturePackIcon->UseBrush(m_hTexturePackIconBrush); + } + + pbImageData = tp->getPackComparison(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&m_hTexturePackComparisonBrush); + m_texturePackComparison->UseBrush(m_hTexturePackComparisonBrush); + } + else + { + m_texturePackComparison->UseBrush(NULL); + } + } +} + +void CScene_MultiGameCreate::UpdateCurrentTexturePack() +{ + m_currentTexturePackIndex = m_pTexturePacksList->GetCurSel(); + int iTexPackId=m_pTexturePacksList->GetData(m_currentTexturePackIndex).iData; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(iTexPackId); + + // if the texture pack is null, you don't have it yet + if(tp==NULL) + { + // Upsell + + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=m_pTexturePacksList->GetData(m_currentTexturePackIndex); + + + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(ProfileManager.GetPrimaryPad(), eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + // Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_MultiGameCreate::TexturePackDialogReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_MultiGameCreate::TexturePackDialogReturned,this,app.GetStringTable()); + } + + // do set the texture pack id, and on the user pressing create world, check they have it + m_MoreOptionsParams.dwTexturePack = ListItem.iData; + return ; + } + else + { + m_MoreOptionsParams.dwTexturePack = tp->getId(); + } +} + +int CScene_MultiGameCreate::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameCreate *pClass = (CScene_MultiGameCreate *)pParam; + pClass->m_currentTexturePackIndex = pClass->m_pTexturePacksList->GetCurSel(); + // Exit with or without saving + // Decline means install full version of the texture pack in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultAccept) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + CXuiCtrl4JList::LIST_ITEM_INFO ListItem; + // get the current index of the list, and then get the data + ListItem=pClass->m_pTexturePacksList->GetData(pClass->m_currentTexturePackIndex); + app.GetDLCFullOfferIDForPackID(ListItem.iData,&ullOfferID_Full); + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + } + else // trial version + { + // if there is no trial version, this is a Cancel + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + } + pClass->m_bIgnoreInput=false; + return 0; +} + +HRESULT CScene_MultiGameCreate::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + // clear out the texture pack list + m_pTexturePacksList->RemoveAllData(); + ClearTexturePackDescription(); + } + // this will send a CustomMessage_DLCMountingComplete when done + return S_OK; +} + +HRESULT CScene_MultiGameCreate::OnCustomMessage_DLCMountingComplete() +{ + // refill the texture pack list + m_pTexturePacksList->SetSelectionChangedHandle(m_hObj); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + int texturePacksCount = pMinecraft->skins->getTexturePackCount(); + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + HRESULT hr; + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + { + ListInfo.fEnabled = TRUE; + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&ListInfo.hXuiBrush); + + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tp; + if(pDLCTexPack) + { + int id=pDLCTexPack->getDLCParentPackId(); + + if(id==0) + { + // default texture pack - should come first + ListInfo.iSortIndex=0x0FFFFFFF; + } + else + { + ListInfo.iSortIndex=id; + ListInfo.iData=id; + } + } + m_pTexturePacksList->AddData(ListInfo,0,CXuiCtrl4JList::eSortList_Index); + } + } + + m_iTexturePacksNotInstalled=0; + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + // REMOVE UNTIL WORKING + DLC_INFO *pDLCInfo=NULL; + + // first pass - look to see if there are any that are not in the list + bool bTexturePackAlreadyListed; + bool bNeedToGetTPD=false; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + if(m_iConfigA!=NULL) + { + delete m_iConfigA; + } + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + + m_currentTexturePackIndex = pMinecraft->skins->getTexturePackIndex(0); + UpdateTexturePackDescription(m_currentTexturePackIndex); + + m_bSetup = true; + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + return S_OK; +} + +void CScene_MultiGameCreate::ClearTexturePackDescription() +{ + m_texturePackTitle.SetText(L" "); + m_texturePackDescription.SetText(L" "); + m_texturePackComparison->UseBrush(NULL); + m_texturePackIcon->UseBrush(NULL); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.h b/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.h new file mode 100644 index 0000000..5b4e6ef --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameCreate.h @@ -0,0 +1,118 @@ +#pragma once +#include "..\Media\xuiscene_multi_create.h" +#include "XUI_Ctrl_4JEdit.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_MultiGameLaunchMoreOptions.h" + +class CXuiCtrl4JList; +class CXuiCtrl4JIcon; + +class CScene_MultiGameCreate : public CXuiSceneImpl +{ +protected: + CXuiScene m_MainScene; + CXuiScene m_TexturePackDetails; + CXuiControl m_NewWorld; + CXuiControl m_labelWorldName; + CXuiControl m_labelSeed; + CXuiControl m_labelRandomSeed; + CXuiControl m_MoreOptions; + CXuiCtrl4JEdit m_EditSeed; + CXuiCtrl4JEdit m_EditWorldName; + CXuiControl m_ButtonGameMode; + CXuiCtrlSliderWrapper m_SliderDifficulty; + CXuiCtrl4JList *m_pTexturePacksList; + CXuiControl m_texturePackTitle, m_texturePackDescription; + CXuiCtrl4JIcon *m_texturePackIcon, *m_texturePackComparison; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_ON_XM_CONTROL_NAVIGATE(OnControlNavigate) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_TRANSITION_END(OnTransitionEnd) + XUI_ON_XM_NOTIFY_SELCHANGED( OnNotifySelChanged ) + XUI_ON_XM_NOTIFY_KILL_FOCUS( OnNotifyKillFocus ) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_MainScene, m_MainScene) + BEGIN_MAP_CHILD_CONTROLS(m_MainScene) + MAP_CONTROL(IDC_XuiLabelWorldName, m_labelWorldName) + MAP_CONTROL(IDC_XuiLabelSeed, m_labelSeed) + MAP_CONTROL(IDC_XuiLabelRandomSeed, m_labelRandomSeed) + MAP_CONTROL(IDC_XuiGameModeToggle, m_ButtonGameMode) + MAP_CONTROL(IDC_XuiNewWorld, m_NewWorld) + MAP_CONTROL(IDC_XuiMoreOptions, m_MoreOptions) + MAP_CONTROL(IDC_XuiEditSeed, m_EditSeed) + MAP_CONTROL(IDC_XuiEditWorldName, m_EditWorldName) + MAP_CONTROL(IDC_XuiSliderDifficulty, m_SliderDifficulty) + MAP_OVERRIDE(IDC_TexturePacksList, m_pTexturePacksList) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TexturePackDetails, m_TexturePackDetails) + BEGIN_MAP_CHILD_CONTROLS(m_TexturePackDetails) + MAP_CONTROL(IDC_TexturePackName, m_texturePackTitle) + MAP_CONTROL(IDC_TexturePackDescription, m_texturePackDescription) + MAP_OVERRIDE(IDC_Icon, m_texturePackIcon) + MAP_OVERRIDE(IDC_ComparisonPic, m_texturePackComparison) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled); + HRESULT OnControlNavigate( XUIMessageControlNavigate *pControlNavigateData, BOOL &bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled ); + HRESULT OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); + HRESULT OnDestroy(); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_MultiGameCreate, L"CScene_MultiGameCreate", XUI_CLASS_SCENE ) + +private: + static int LoadSaveDataReturned(void *pParam,bool bContinue); + static int StartGame_SignInReturned(void *pParam,bool bContinue, int iPad); + static void CreateGame(CScene_MultiGameCreate* pClass, DWORD dwLocalUsersMask); + static int ConfirmCreateReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int UnlockTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + + void ToggleShowSaveList(); + void UpdateTexturePackDescription(int index); + void ClearTexturePackDescription(); + void UpdateCurrentTexturePack(); + + bool m_bMultiplayerAllowed; + int m_iPad; + int m_CurrentDifficulty; + static int m_iDifficultyTitleSettingA[4]; + LaunchMoreOptionsMenuInitData m_MoreOptionsParams; + bool m_bGameModeSurvival; + bool m_bIgnoreInput; + unsigned int m_currentTexturePackIndex; + DLCPack * m_pDLCPack; + bool m_bSetup; + bool m_texturePackDescDisplayed; + HXUIBRUSH m_hTexturePackIconBrush; + HXUIBRUSH m_hTexturePackComparisonBrush; + int *m_iConfigA; // track the texture packs that we don't have installed + int m_iTexturePacksNotInstalled; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.cpp b/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.cpp new file mode 100644 index 0000000..bbfa243 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.cpp @@ -0,0 +1,392 @@ +#include "stdafx.h" +#include +#include +#include +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "XUI_MultiGameInfo.h" +#include "XUI_MultiGameJoinLoad.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" +#include "..\..\..\Minecraft.World\Difficulty.h" + +#define UPDATE_PLAYERS_TIMER_ID 0 +#define UPDATE_PLAYERS_TIMER_TIME 30000 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameInfo::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(playersList,app.GetString(IDS_PLAYERS)); + XuiControlSetText(m_JoinGame,app.GetString(IDS_JOIN_GAME)); + XuiControlSetText(m_labelDifficulty,app.GetString(IDS_LABEL_DIFFICULTY)); + XuiControlSetText(m_labelGameType,app.GetString(IDS_LABEL_GAME_TYPE)); + XuiControlSetText(m_labelGamertagsOn,app.GetString(IDS_LABEL_GAMERTAGS)); + XuiControlSetText(m_labelStructuresOn,app.GetString(IDS_LABEL_STRUCTURES)); + XuiControlSetText(m_labelLevelType,app.GetString(IDS_LABEL_LEVEL_TYPE)); + XuiControlSetText(m_labelPvP,app.GetString(IDS_LABEL_PvP)); + XuiControlSetText(m_labelTrust,app.GetString(IDS_LABEL_TRUST)); + XuiControlSetText(m_labelTNTOn,app.GetString(IDS_LABEL_TNT)); + XuiControlSetText(m_labelFireOn,app.GetString(IDS_LABEL_FIRE_SPREADS)); + + JoinMenuInitData *initData = (JoinMenuInitData *)pInitData->pvInitData; + m_selectedSession = initData->selectedSession; + m_iPad = initData->iPad; + // 4J-PB - don't delete this - it's part of the joinload structure + //delete initData; + + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if( m_selectedSession->data.players[i] != NULL ) + { + playersList.InsertItems(i,1); +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<data.szPlayers[i] ).c_str() ); + } + } + else + { + // Leave the loop when we hit the first NULL player + break; + } + } + + unsigned int uiGameHostSettings = m_selectedSession->data.m_uiGameHostSettings; + switch(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Difficulty)) + { + case Difficulty::EASY: + m_difficulty.SetText( app.GetString(IDS_DIFFICULTY_TITLE_EASY) ); + break; + case Difficulty::NORMAL: + m_difficulty.SetText( app.GetString(IDS_DIFFICULTY_TITLE_NORMAL) ); + break; + case Difficulty::HARD: + m_difficulty.SetText( app.GetString(IDS_DIFFICULTY_TITLE_HARD) ); + break; + case Difficulty::PEACEFUL: + default: + m_difficulty.SetText( app.GetString(IDS_DIFFICULTY_TITLE_PEACEFUL) ); + break; + } + + unsigned int hostOption = app.GetGameHostOption(uiGameHostSettings,eGameHostOption_GameType); + + if (hostOption == GameType::CREATIVE->getId()) + { + m_GameType.SetText( app.GetString(IDS_CREATIVE) ); + } + else if (hostOption == GameType::SURVIVAL->getId()) + { + m_GameType.SetText( app.GetString(IDS_SURVIVAL) ); + } + else + { + m_GameType.SetText( app.GetString(IDS_SURVIVAL) ); + } + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Gamertags)) m_gamertagsOn.SetText( app.GetString(IDS_ON) ); + else m_gamertagsOn.SetText( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_Structures)) m_structuresOn.SetText( app.GetString(IDS_ON) ); + else m_structuresOn.SetText( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_LevelType)) m_levelType.SetText( app.GetString(IDS_LEVELTYPE_SUPERFLAT) ); + else m_levelType.SetText( app.GetString(IDS_LEVELTYPE_NORMAL) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_PvP)) m_pvpOn.SetText( app.GetString(IDS_ON) ); + else m_pvpOn.SetText( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TrustPlayers)) m_trustPlayers.SetText( app.GetString(IDS_ON) ); + else m_trustPlayers.SetText( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_TNT)) m_tntOn.SetText( app.GetString(IDS_ON) ); + else m_tntOn.SetText( app.GetString(IDS_OFF) ); + + if(app.GetGameHostOption(uiGameHostSettings,eGameHostOption_FireSpreads)) m_fireOn.SetText( app.GetString(IDS_ON) ); + else m_fireOn.SetText( app.GetString(IDS_OFF) ); + + m_bIgnoreInput = false; + + // Alert the app the we want to be informed of ethernet connections + app.SetLiveLinkRequired( true ); + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_JoinMenu, 0); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); + + XuiSetTimer(m_hObj,UPDATE_PLAYERS_TIMER_ID,UPDATE_PLAYERS_TIMER_TIME); + + return S_OK; +} + + + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameInfo::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if ( hObjPressed == m_JoinGame ) + { + // check we have the texture pack required for the game + + + SetShow( FALSE ); + m_bIgnoreInput=true; + + // 4J Stu - If we only have one controller connected, then don't show the sign-in UI again + DWORD connectedControllers = 0; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers; + } + + if(connectedControllers == 1 || !RenderManager.IsHiDef()) + { + JoinGame( this ); + } + else + { + ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameInfo::StartGame_SignInReturned, this,ProfileManager.GetPrimaryPad()); + } + } + + return S_OK; +} + +HRESULT CScene_MultiGameInfo::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr = S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + case VK_PAD_Y: + if(m_selectedSession != NULL && playersList.TreeHasFocus() && playersList.GetItemCount() > 0) + { + PlayerUID xuid = m_selectedSession->data.players[playersList.GetCurSel()]; + if( xuid != INVALID_XUID ) + hr = XShowGamerCardUI(ProfileManager.GetLockedProfile(), xuid); + } + break; + } + + return hr; +} + +HRESULT CScene_MultiGameInfo::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK); + + return S_OK; +} + +HRESULT CScene_MultiGameInfo::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + if(pNotifySelChangedData->iOldItem!=-1) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + bHandled = TRUE; + + return S_OK; +} + +HRESULT CScene_MultiGameInfo::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + if( playersList.TreeHasFocus() && playersList.GetItemCount() > 0 ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, IDS_TOOLTIPS_BACK, -1, IDS_TOOLTIPS_VIEW_GAMERCARD ); + bHandled = TRUE; + } + return S_OK; +} + +HRESULT CScene_MultiGameInfo::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + if( pNotifyFocusData->hObjOther == m_JoinGame ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); + bHandled = TRUE; + } + return S_OK; +} + +int CScene_MultiGameInfo::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + CScene_MultiGameInfo* pClass = (CScene_MultiGameInfo*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad)) + { + JoinGame(pClass); + } + } + else + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + } + return 0; +} + +// Shared function to join the game that is the same whether we used the sign-in UI or not +void CScene_MultiGameInfo::JoinGame(CScene_MultiGameInfo* pClass) +{ + DWORD dwLocalUsersMask = 0; + bool noPrivileges = false; + DWORD dwSignedInUsers = 0; + + // if we're in SD mode, then only the primary player gets to play + if(RenderManager.IsHiDef()) + { + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + ++dwSignedInUsers; + if( !ProfileManager.AllowedToPlayMultiplayer(index) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index); + } + } + } + else + { + if(ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad()) ) + { + ++dwSignedInUsers; + if( !ProfileManager.AllowedToPlayMultiplayer(ProfileManager.GetPrimaryPad()) ) noPrivileges = true; + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad()); + } + } + + // Check if user-created content is allowed, as we cannot play multiplayer if it's not + bool noUGC = false; + BOOL pccAllowed = TRUE; + BOOL pccFriendsAllowed = TRUE; + ProfileManager.AllowedPlayerCreatedContent(ProfileManager.GetPrimaryPad(),false,&pccAllowed,&pccFriendsAllowed); + if(!pccAllowed && !pccFriendsAllowed) noUGC = true; + + if(noUGC) + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + + int messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + if(dwSignedInUsers > 1) messageText = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + + StorageManager.RequestMessageBox( IDS_CONNECTION_FAILED, messageText, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + + } + else if(noPrivileges) + { + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + } + else + { + CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( pClass->m_selectedSession, dwLocalUsersMask ); + + // Alert the app the we no longer want to be informed of ethernet connections + app.SetLiveLinkRequired( false ); + + if( result != CGameNetworkManager::JOINGAME_SUCCESS ) + { + int exitReasonStringId = -1; + switch(result) + { + case CGameNetworkManager::JOINGAME_FAIL_SERVER_FULL: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + break; + } + + if( exitReasonStringId == -1 ) + { + app.NavigateBack(ProfileManager.GetPrimaryPad()); + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox( IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + + app.NavigateToHomeMenu(); + } + } + } +} + +HRESULT CScene_MultiGameInfo::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + if ( pTimer->nId == UPDATE_PLAYERS_TIMER_ID) + { + PlayerUID selectedPlayerXUID = m_selectedSession->data.players[playersList.GetCurSel()]; + + bool success = g_NetworkManager.GetGameSessionInfo(m_iPad, m_selectedSession->sessionId,m_selectedSession); + + if( success ) + { + playersList.DeleteItems(0, playersList.GetItemCount()); + int selectedIndex = 0; + for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if( m_selectedSession->data.players[i] != NULL ) + { + if(m_selectedSession->data.players[i] == selectedPlayerXUID) selectedIndex = i; + playersList.InsertItems(i,1); +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<data.szPlayers[i] ).c_str() ); + } + } + else + { + // Leave the loop when we hit the first NULL player + break; + } + } + playersList.SetCurSel(selectedIndex); + } + } + + return S_OK; +} + + diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.h b/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.h new file mode 100644 index 0000000..9664640 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameInfo.h @@ -0,0 +1,85 @@ +#pragma once +using namespace std; +#include +#include +#include "..\Media\xuiscene_multi_gameinfo.h" + +class FriendSessionInfo; + +class CScene_MultiGameInfo : public CXuiSceneImpl +{ +protected: + CXuiList playersList; + CXuiControl m_JoinGame; + CXuiControl m_GameSettingsGroup; + CXuiControl m_difficulty, m_GameType, m_gamertagsOn, m_structuresOn, m_levelType, m_pvpOn, m_trustPlayers, m_tntOn, m_fireOn; + CXuiControl m_labelDifficulty, m_labelGameType, m_labelGamertagsOn, m_labelStructuresOn, m_labelLevelType, m_labelPvP, m_labelTrust, m_labelTNTOn, m_labelFireOn; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_NOTIFY_SELCHANGED(OnNotifySelChanged) + XUI_ON_XM_NOTIFY_SET_FOCUS(OnNotifySetFocus) + XUI_ON_XM_NOTIFY_KILL_FOCUS(OnNotifyKillFocus) + XUI_ON_XM_TIMER(OnTimer) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_JoinGame, m_JoinGame) + MAP_CONTROL(IDC_GamePlayers, playersList) + + MAP_CONTROL(IDC_GameSettings, m_GameSettingsGroup) + BEGIN_MAP_CHILD_CONTROLS(m_GameSettingsGroup) + MAP_CONTROL(IDC_GamertagsOn, m_gamertagsOn) + MAP_CONTROL(IDC_StructuresOn, m_structuresOn) + MAP_CONTROL(IDC_Difficulty, m_difficulty) + MAP_CONTROL(IDC_GameType, m_GameType) + MAP_CONTROL(IDC_LevelType, m_levelType) + MAP_CONTROL(IDC_PvP, m_pvpOn) + MAP_CONTROL(IDC_Trust, m_trustPlayers) + MAP_CONTROL(IDC_TNTOn, m_tntOn) + MAP_CONTROL(IDC_FireOn, m_fireOn) + + MAP_CONTROL(IDC_LabelGamertagsOn, m_labelGamertagsOn) + MAP_CONTROL(IDC_LabelStructuresOn, m_labelStructuresOn) + MAP_CONTROL(IDC_LabelDifficulty, m_labelDifficulty) + MAP_CONTROL(IDC_LabelGameType, m_labelGameType) + MAP_CONTROL(IDC_LabelLevelType, m_labelLevelType) + MAP_CONTROL(IDC_LabelPvP, m_labelPvP) + MAP_CONTROL(IDC_LabelTrust, m_labelTrust) + MAP_CONTROL(IDC_LabelTNTOn, m_labelTNTOn) + MAP_CONTROL(IDC_LabelFireOn, m_labelFireOn) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled); + HRESULT OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_MultiGameInfo, L"CScene_MultiGameInfo", XUI_CLASS_SCENE ) + + + +protected: + FriendSessionInfo *m_selectedSession; + unsigned char m_localPlayers; + bool m_bIgnoreInput; + int m_iPad; + + static int StartGame_SignInReturned(void *pParam,bool bContinue, int iPad); + static void JoinGame(CScene_MultiGameInfo* pClass); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.cpp b/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.cpp new file mode 100644 index 0000000..73f25da --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.cpp @@ -0,0 +1,2767 @@ +#include "stdafx.h" +#include +#include +#include +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h" +#include "..\..\LocalPlayer.h" +#include "..\..\Minecraft.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\ArrayWithLength.h" +#include "..\..\..\Minecraft.World\File.h" +#include "..\..\..\Minecraft.World\InputOutputStream.h" +#include "XUI_Ctrl_4JList.h" +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_LoadSettings.h" +#include "XUI_MultiGameInfo.h" +#include "XUI_MultiGameJoinLoad.h" +#include "XUI_MultiGameCreate.h" +#include "..\..\MinecraftServer.h" +#include "..\..\Options.h" + +#include "..\GameRules\LevelGenerationOptions.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\..\Minecraft.World\LevelSettings.h" + +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 3 +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 100 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameJoinLoad::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + + m_iPad=*(int *)pInitData->pvInitData; + m_bReady=false; + MapChildControls(); + + m_iTexturePacksNotInstalled=0; + m_iConfigA=NULL; + + XuiControlSetText(m_LabelNoGames,app.GetString(IDS_NO_GAMES_FOUND)); + XuiControlSetText(m_GamesList,app.GetString(IDS_JOIN_GAME)); + XuiControlSetText(m_SavesList,app.GetString(IDS_START_GAME)); + + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/TexturePackIcon.png"); + + m_DefaultMinecraftIconSize = 0; + HRESULT hr = XuiResourceLoadAllNoLoc(szResourceLocator, &m_DefaultMinecraftIconData, &m_DefaultMinecraftIconSize); + + m_localPlayers = 1; + m_bKillSaveInfoEnumerate=false; + + m_bShowingPartyGamesOnly = false; + + m_bRetrievingSaveInfo=false; + m_bSaveTransferInProgress=false; + + // check for a default custom cloak in the global storage + // 4J-PB - changed to a config file +// if(ProfileManager.IsSignedInLive( m_iPad )) +// { +// app.InstallDefaultCape(); +// } + + m_initData= new JoinMenuInitData(); + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } + + int iLB = -1; + if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES; + + XuiSetTimer(m_hObj,JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME); + + m_iSaveInfoC=0; + + VOID *pObj; + XuiObjectFromHandle( m_SavesList, &pObj ); + m_pSavesList = (CXuiCtrl4JList *)pObj; + + XuiObjectFromHandle( m_GamesList, &pObj ); + m_pGamesList = (CXuiCtrl4JList *)pObj; + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true) + { + // not doing a mount, so enable input + m_bIgnoreInput=true; + } + else + { + // if we're waiting for DLC to mount, don't fill the save list. The custom message on end of dlc mounting will do that + m_bIgnoreInput=false; + + + + m_iChangingSaveGameInfoIndex = 0; + + m_generators = app.getLevelGenerators(); + m_iDefaultButtonsC = 0; + m_iMashUpButtonsC=0; + + // check if we're in the trial version + if(ProfileManager.IsFullVersion()==false) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB); + + AddDefaultButtons(); + + m_pSavesList->SetCurSelVisible(0); + } + else if(StorageManager.GetSaveDisabled()) + { + if(StorageManager.GetSaveDeviceSelected(m_iPad)) + { + // saving is disabled, but we should still be able to load from a selected save device + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,-1,-1,-1,iLB,IDS_TOOLTIPS_DELETESAVE); + + GetSaveInfo(); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,-1,-1,-1,iLB); + + AddDefaultButtons(); + m_SavesListTimer.SetShow( FALSE ); + + m_pSavesList->SetCurSelVisible(0); + } + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame(); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,-1,-1,-1,-1,bCanRename?IDS_TOOLTIPS_SAVEOPTIONS:IDS_TOOLTIPS_DELETESAVE); + + GetSaveInfo(); + } + } + //XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE); + + UpdateGamesList(); + + g_NetworkManager.SetSessionsUpdatedCallback( &CScene_MultiGameJoinLoad::UpdateGamesListCallback, this ); + + // 4J Stu - Fix for #12530 -TCR 001 BAS Game Stability: Title will crash if the player disconnects while starting a new world and then opts to play the tutorial once they have been returned to the Main Menu. + MinecraftServer::resetFlags(); + + // If we're not ignoring input, then we aren't still waiting for the DLC to mount, and can now check for corrupt dlc. Otherwise this will happen when the dlc has finished mounting. + if( !m_bIgnoreInput) + { + app.m_dlcManager.checkForCorruptDLCAndAlert(); + } + + + // 4J-PB - Load up any texture pack data we have locally in the XZP + for(int i=0;iskins->getTexturePackCount(); + //CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + //HRESULT hr; + + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + // some missing + bNeedToGetTPD=true; + + m_iTexturePacksNotInstalled++; + } + } + + if(bNeedToGetTPD==true) + { + // add a TMS request for them + app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n"); + app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData); + m_iConfigA= new int [m_iTexturePacksNotInstalled]; + m_iTexturePacksNotInstalled=0; + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + { + bTexturePackAlreadyListed=false; + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + for(unsigned int i = 0; i < texturePacksCount; ++i) + { + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + if(pDLCInfo->iConfig==tp->getDLCParentPackId()) + { + bTexturePackAlreadyListed=true; + } + } + if(bTexturePackAlreadyListed==false) + { + m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig; + } + } + } + + XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); + + return S_OK; +} + +void CScene_MultiGameJoinLoad::AddDefaultButtons() +{ + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + + // Add two for New Game and Tutorial + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + ListInfo.pwszText = app.GetString(IDS_CREATE_NEW_WORLD); + ListInfo.fEnabled = TRUE; + ListInfo.iData = -1; + m_pSavesList->AddData(ListInfo); + + int iSavesListIndex = 0; + int iGeneratorIndex = 0; + m_iMashUpButtonsC=0; + + for(AUTO_VAR(it, m_generators->begin()); it != m_generators->end(); ++it) + { + LevelGenerationOptions *levelGen = *it; + ListInfo.pwszText = levelGen->getWorldName(); + ListInfo.fEnabled = TRUE; + ListInfo.iData = iGeneratorIndex++; // used to index into the list of generators + + // need to check if the user has disabled this pack in the save display list + unsigned int uiTexturePackID=levelGen->getRequiredTexturePackId(); + + if(uiTexturePackID!=0) + { + unsigned int uiMashUpWorldsBitmask=app.GetMashupPackWorlds(m_iPad); + + if((uiMashUpWorldsBitmask & (1<<(uiTexturePackID-1024)))==0) + { + // this world is hidden, so skip + continue; + } + } + m_pSavesList->AddData(ListInfo); + + // retrieve the save icon from the texture pack, if there is one + if(uiTexturePackID!=0) + { + // increment the count of the mash-up pack worlds in the save list + m_iMashUpButtonsC++; + TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(levelGen->getRequiredTexturePackId()); + DWORD dwImageBytes; + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + HXUIBRUSH hXuiBrush; + + if(dwImageBytes > 0 && pbImageData) + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush); + // the index inside the list item for this will be i+1 because they start at m_vListData.size(), so the first etry (tutorial) is 1 + m_pSavesList->UpdateGraphic(iSavesListIndex+1,hXuiBrush); + } + } + + ++iSavesListIndex; + } + + m_iDefaultButtonsC = iSavesListIndex + 1; +} + + +HRESULT CScene_MultiGameJoinLoad::GetSaveInfo( ) +{ + unsigned int uiSaveC=0; + + // This will return with the number retrieved in uiSaveC + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + uiSaveC = 0; + File savesDir(L"GAME:\\Saves"); + if( savesDir.exists() ) + { + m_saves = savesDir.listFiles(); + uiSaveC = (unsigned int)m_saves->size(); + } + // add the New Game and Tutorial after the saves list is retrieved, if there are any saves + + // Add two for New Game and Tutorial + unsigned int listItems = uiSaveC; + + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + AddDefaultButtons(); + + for(unsigned int i=0;iat(i)->getName(); + wchar_t *name = new wchar_t[wName.size()+1]; + for(unsigned int j = 0; j < wName.size(); ++j) + { + name[j] = wName[j]; + } + name[wName.size()] = 0; + ListInfo.pwszText = name; + ListInfo.fEnabled=TRUE; + ListInfo.iData = -1; + m_pSavesList->AddData(ListInfo); + } + m_pSavesList->SetCurSelVisible(0); + } + else + { + m_bRetrievingSaveInfo=true; // we're blocking the exit from this scene until complete + + // clear the saves list + m_pSavesList->RemoveAllData(); + + m_iSaveInfoC=0; +#ifdef _XBOX + C4JStorage::ESGIStatus eSGIStatus=StorageManager.GetSavesInfo(ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::GetSavesInfoCallback,this,"savegame.dat"); + + if(eSGIStatus==C4JStorage::ESGIStatus_NoSaves) + { + uiSaveC=0; + m_SavesListTimer.SetShow( FALSE ); + m_SavesList.SetEnable(TRUE); + } +#else + + //C4JStorage::ESaveGameState eStatus=StorageManager.GetSavesInfo(ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::GetSavesInfoCallback,this,"savegame.dat"); + +#endif + } + + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnDestroy() +{ + g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL ); + + for(AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it) + { + delete (*it); + } + + if(m_bSaveTransferInProgress) + { + CancelSaveUploadCallback(this); + } + + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + + // clear out the texture pack data + for(int i=0;im_bIgnoreInput=false; + return 0; +} +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameJoinLoad::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // if we're retrieving save info, ignore key presses + if(m_bRetrievingSaveInfo) + { + return S_OK; + } + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if ( hObjPressed == m_GamesList ) + { + m_bIgnoreInput=true; + + DWORD nIndex = m_pGamesList->GetCurSel(); + + if( m_pGamesList->GetItemCount() > 0 && nIndex < currentSessions.size() ) + { + //CScene_MultiGameInfo::JoinMenuInitData *initData = new CScene_MultiGameInfo::JoinMenuInitData(); + m_initData->iPad = m_iPad; + m_initData->selectedSession = currentSessions.at( nIndex ); + + // check that we have the texture pack available + // If it's not the default texture pack + if(m_initData->selectedSession->data.texturePackParentId!=0) + { + int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount(); + bool bHasTexturePackInstalled=false; + + for(int i=0;iskins->getTexturePackByIndex(i); + if(tp->getDLCParentPackId()==m_initData->selectedSession->data.texturePackParentId) + { + bHasTexturePackInstalled=true; + break; + } + } + + if(bHasTexturePackInstalled==false) + { + // upsell the texture pack + // tell sentient about the upsell of the full version of the skin pack + ULONGLONG ullOfferID_Full; + app.GetDLCFullOfferIDForPackID(m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full); + + TelemetryManager->RecordUpsellPresented(pNotifyPressData->UserIndex, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[3]; + + // Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + uiIDA[2]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::TexturePackDialogReturned,this,app.GetStringTable()); + } + else + { + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + uiIDA[1]=IDS_CONFIRM_CANCEL; + // Give the player a warning about the texture pack missing + StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::TexturePackDialogReturned,this,app.GetStringTable()); + } + + return S_OK; + } + } + + m_NetGamesListTimer.SetShow( FALSE ); + + // Reset the background downloading, in case we changed it by attempting to download a texture pack + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + + // kill the texture pack check timer + XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID); + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_JoinMenu,m_initData); + } + } + else if(hObjPressed==m_SavesList) + { + m_bIgnoreInput=true; + + CXuiControl pItem; + int iIndex; + // get the selected item + iIndex=m_SavesList.GetCurSel(&pItem); + + CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesList->GetData(iIndex); + + if(iIndex == JOIN_LOAD_CREATE_BUTTON_INDEX) + { + app.SetTutorialMode( false ); + m_NetGamesListTimer.SetShow( FALSE ); + + app.SetCorruptSaveDeleted(false); + + CreateWorldMenuInitData *params = new CreateWorldMenuInitData(); + params->iPad = m_iPad; + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_CreateWorldMenu,(void *)params); + } + else if(info.iData >= 0) + { + LevelGenerationOptions *levelGen = m_generators->at(info.iData); + app.SetTutorialMode( levelGen->isTutorial() ); + // Reset the autosave time + app.SetAutosaveTimerTime(); + + if(levelGen->isTutorial()) + { + LoadLevelGen(levelGen); + } + else + { + LoadMenuInitData *params = new LoadMenuInitData(); + params->iPad = m_iPad; + // need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting + params->iSaveGameInfoIndex=-1; + //params->pbSaveRenamed=&m_bSaveRenamed; + params->levelGen = levelGen; + + // navigate to the settings scene + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params); + } + } + else + { + // check if this is a damaged save + if(m_pSavesList->GetData(iIndex).bIsDamaged) + { + // give the option to delete the save + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable()); + } + else + { + app.SetTutorialMode( false ); + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + LoadSaveFromDisk(m_saves->at(iIndex-m_iDefaultButtonsC)); + } + else + { + LoadMenuInitData *params = new LoadMenuInitData(); + params->iPad = m_iPad; + // need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting + params->iSaveGameInfoIndex=m_pSavesList->GetData(iIndex).iIndex-m_iDefaultButtonsC; + //params->pbSaveRenamed=&m_bSaveRenamed; + params->levelGen = NULL; + + // kill the texture pack timer + XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID); + // navigate to the settings scene + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params); + } + } + } + } + + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // if we're retrieving save info, ignore key presses + if(m_bRetrievingSaveInfo) + { + return S_OK; + } + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr = S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + m_NetGamesListTimer.SetShow( FALSE ); + + app.NavigateBack(XUSER_INDEX_ANY); + rfHandled = TRUE; + break; + case VK_PAD_X: + + // Change device + // Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage + // device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes. + m_bIgnoreInput=true; + StorageManager.SetSaveDevice(&CScene_MultiGameJoinLoad::DeviceSelectReturned,this,true); + CXuiSceneBase::PlayUISFX(eSFX_Press); + break; + case VK_PAD_Y: + if(m_pGamesList->TreeHasFocus() && m_pGamesList->GetItemCount() > 0) + { + DWORD nIndex = m_pGamesList->GetCurSel(); + FriendSessionInfo *pSelectedSession = currentSessions.at( nIndex ); + + PlayerUID xuid = pSelectedSession->data.hostPlayerUID; + if( xuid != INVALID_XUID ) + hr = XShowGamerCardUI(ProfileManager.GetLockedProfile(), xuid); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + else if(DoesSavesListHaveFocus()) + { + // save transfer - make sure they want to overwrite a save that is up there + if(ProfileManager.IsSignedInLive( m_iPad )) + { + // 4J-PB - required for a delete of the save if it's found to be a corrupted save + DWORD nIndex = m_pSavesList->GetCurSel(); + m_iChangingSaveGameInfoIndex=m_pSavesList->GetData(nIndex).iIndex; + + UINT uiIDA[2]; + uiIDA[0]=IDS_UPLOAD_SAVE; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + ui.RequestMessageBox(IDS_SAVE_TRANSFER_TITLE, IDS_SAVE_TRANSFER_TEXT, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::SaveTransferDialogReturned,this, app.GetStringTable()); + } + } + break; + case VK_PAD_RSHOULDER: + if(DoesSavesListHaveFocus()) + { + m_bIgnoreInput = true; + + int iIndex=m_SavesList.GetCurSel(); + m_iChangingSaveGameInfoIndex=m_pSavesList->GetData(iIndex).iIndex; + + // Could be delete save or Save Options + if(StorageManager.GetSaveDisabled()) + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable()); + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + UINT uiIDA[3]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_TITLE_RENAMESAVE; + uiIDA[2]=IDS_TOOLTIPS_DELETESAVE; + StorageManager.RequestMessageBox(IDS_TOOLTIPS_SAVEOPTIONS, IDS_TEXT_SAVEOPTIONS, uiIDA, 3, pInputData->UserIndex,&CScene_MultiGameJoinLoad::SaveOptionsDialogReturned,this, app.GetStringTable()); + } + else + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable()); + } + } + CXuiSceneBase::PlayUISFX(eSFX_Press); + + } + else if(DoesMashUpWorldHaveFocus()) + { + // hiding a mash-up world + // get the mash-up pack id + CXuiControl pItem; + int iIndex; + iIndex=m_SavesList.GetCurSel(&pItem); + + CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesList->GetData(iIndex); + if((iIndex != JOIN_LOAD_CREATE_BUTTON_INDEX) && (info.iData >= 0)) + { + LevelGenerationOptions *levelGen = m_generators->at(info.iData); + + if(!levelGen->isTutorial()) + { + if(levelGen->requiresTexturePack()) + { + unsigned int uiPackID=levelGen->getRequiredTexturePackId(); + + m_bIgnoreInput = true; + app.HideMashupPackWorld(m_iPad,uiPackID); + + // update the saves list + m_pSavesList->RemoveAllData(); + m_iSaveInfoC=0; + GetSaveInfo(); + m_bIgnoreInput = false; + } + } + } + + CXuiSceneBase::PlayUISFX(eSFX_Press); + + } + break; + case VK_PAD_LSHOULDER: + if( m_bInParty ) + { + m_bShowingPartyGamesOnly = !m_bShowingPartyGamesOnly; + UpdateGamesList(); + CXuiSceneBase::PlayUISFX(eSFX_Press); + } + break; + } + + return hr; +} + +HRESULT CScene_MultiGameJoinLoad::OnNavReturn(HXUIOBJ hSceneFrom,BOOL& rfHandled) +{ + + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + // start the texture pack timer again + XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + // re-enable button presses + m_bIgnoreInput=false; + + if( m_bMultiplayerAllowed ) + { + HXUICLASS hClassFullscreenProgress = XuiFindClass( L"CScene_FullscreenProgress" ); + HXUICLASS hClassConnectingProgress = XuiFindClass( L"CScene_ConnectingProgress" ); + + // If we are navigating back from a full screen progress scene, then that means a connection attempt failed + if( XuiIsInstanceOf( hSceneFrom, hClassFullscreenProgress ) || XuiIsInstanceOf( hSceneFrom, hClassConnectingProgress ) ) + { + UpdateGamesList(); + } + } + else + { + m_pGamesList->RemoveAllData(); + //m_GamesList.DeleteItems(0, m_GamesList.GetItemCount() ); + m_pGamesList->SetEnable(FALSE); + //XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE); + m_NetGamesListTimer.SetShow( TRUE ); + m_LabelNoGames.SetShow( FALSE ); + m_SavesList.InitFocus(m_iPad); + } + + // are we back here because of a delete of a corrupt save? + + if(app.GetCorruptSaveDeleted()) + { + // need to re-get the saves list and update the display + // clear the saves list + m_pSavesList->RemoveAllData(); + m_iSaveInfoC=0; + GetSaveInfo(); + app.SetCorruptSaveDeleted(false); + } + + int iY = -1; + int iRB=-1; + if( DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, -1,-1,-1,iLB); + } + else if(StorageManager.GetSaveDisabled()) + { + // clear out the saves list, since the disable save may have happened in the load screen because of a device removal + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB); + } + + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + + if(m_bReady) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + + return S_OK; +} + + +HRESULT CScene_MultiGameJoinLoad::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY || + pTransition->dwTransType == XUI_TRANSITION_FROM || pTransition->dwTransType == XUI_TRANSITION_BACKFROM) + { + // 4J Stu - We may have had to unload our font renderer in this scene if one of the save files + // uses characters not in our font (eg asian chars) so restore our font renderer + // This will not do anything if our font renderer is already loaded + app.OverrideFontRenderer(true,true); + + KillTimer(JOIN_LOAD_ONLINE_TIMER_ID); + } + else if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + SetTimer(JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME); + // 4J-PB - Need to check for installed DLC, which might have happened while you were on the info scene + if(pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // Can't call this here because if you back out of the load info screen and then go back in and load a game, it will attempt to use the dlc as it's running a mount of the dlc + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_pSavesList->RemoveAllData(); + m_SavesListTimer.SetShow( TRUE ); + } + } + } + + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnFontRendererChange() +{ + // update the tooltips + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should the the View Gamercard tooltip + int iRB=-1; + int iY = -1; + if( DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1,-1,true); + } + else if(StorageManager.GetSaveDisabled()) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB,-1,true); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB,-1,true); + } + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + // update the tooltips + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should the the View Gamercard tooltip + int iRB=-1; + int iY = -1; + if( DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1); + } + else if(StorageManager.GetSaveDisabled()) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB); + } + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + return S_OK; +} + +bool CScene_MultiGameJoinLoad::DoesSavesListHaveFocus() +{ + HXUIOBJ hParentObj,hObj=TreeGetFocus(); + + if(hObj!=NULL) + { + // get the parent and see if it's the saves list + XuiElementGetParent(hObj,&hParentObj); + if(hParentObj==m_SavesList.m_hObj) + { + // check it's not the first or second element (new world or tutorial) + if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC-1)) + { + return true; + } + } + } + return false; +} + +bool CScene_MultiGameJoinLoad::DoesMashUpWorldHaveFocus() +{ + HXUIOBJ hParentObj,hObj=TreeGetFocus(); + + if(hObj!=NULL) + { + // get the parent and see if it's the saves list + XuiElementGetParent(hObj,&hParentObj); + if(hParentObj==m_SavesList.m_hObj) + { + // check it's not the first or second element (new world or tutorial) + if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC-1)) + { + return false; + } + + if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC - 1 - m_iMashUpButtonsC)) + { + return true; + } + else return false; + } + else return false; + } + return false; +} + +bool CScene_MultiGameJoinLoad::DoesGamesListHaveFocus() +{ + HXUIOBJ hParentObj,hObj=TreeGetFocus(); + + if(hObj!=NULL) + { + // get the parent and see if it's the saves list + XuiElementGetParent(hObj,&hParentObj); + if(hParentObj==m_pGamesList->m_hObj) + { + return true; + } + } + return false; +} + +void CScene_MultiGameJoinLoad::UpdateGamesListCallback(LPVOID lpParam) +{ + if(lpParam != NULL) + { + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam; + // check this there's no save transfer in progress + if(!pClass->m_bSaveTransferInProgress) + { + pClass->UpdateGamesList(); + } + } +} + +void CScene_MultiGameJoinLoad::UpdateGamesList() +{ + if( m_bIgnoreInput ) return; + + // if we're retrieving save info, don't show the list yet as we will be ignoring press events + if(m_bRetrievingSaveInfo) + { + return; + } + + DWORD nIndex = -1; + FriendSessionInfo *pSelectedSession = NULL; + if(m_pGamesList->TreeHasFocus() && m_pGamesList->GetItemCount() > 0) + { + nIndex = m_pGamesList->GetCurSel(); + pSelectedSession = currentSessions.at( nIndex ); + } + + SessionID selectedSessionId; + if( pSelectedSession != NULL )selectedSessionId = pSelectedSession->sessionId; + pSelectedSession = NULL; + + for(AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it) + { + delete (*it); + } + currentSessions.clear(); + + m_NetGamesListTimer.SetShow( FALSE ); + + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should show the View Gamercard tooltip + int iRB=-1; + int iY = -1; + + if( DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1); + } + else if(StorageManager.GetSaveDisabled()) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB); + } + + currentSessions = *g_NetworkManager.GetSessionList( m_iPad, m_localPlayers, m_bShowingPartyGamesOnly ); + + // Update the xui list displayed + unsigned int xuiListSize = m_pGamesList->GetItemCount(); + unsigned int filteredListSize = (unsigned int)currentSessions.size(); + + BOOL gamesListHasFocus = m_pGamesList->TreeHasFocus(); + + if(filteredListSize > 0) + { + if( !m_pGamesList->IsEnabled() ) + { + m_pGamesList->SetEnable(TRUE); + //XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, FALSE); + m_pGamesList->SetCurSel( 0 ); + } + m_LabelNoGames.SetShow( FALSE ); + m_NetGamesListTimer.SetShow( FALSE ); + } + else + { + m_pGamesList->SetEnable(FALSE); + //XuiElementSetDisableFocusRecursion(m_pGamesList->m_hObj, TRUE); + m_NetGamesListTimer.SetShow( FALSE ); + m_LabelNoGames.SetShow( TRUE ); + + if( gamesListHasFocus ) m_pGamesList->InitFocus(m_iPad); + } + + // clear out the games list and re-fill + m_pGamesList->RemoveAllData(); + + if( filteredListSize > 0 ) + { + // Reset the focus to the selected session if it still exists + unsigned int sessionIndex = 0; + m_pGamesList->SetCurSel(0); + + for( AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it) + { + FriendSessionInfo *sessionInfo = *it; + HXUIBRUSH hXuiBrush; + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + ListInfo.pwszText = sessionInfo->displayLabel; + ListInfo.fEnabled = TRUE; + ListInfo.iData = sessionIndex; + m_pGamesList->AddData(ListInfo); + // display an icon too + + // Is this a default game or a texture pack game? + if(sessionInfo->data.texturePackParentId!=0) + { + // Do we have the texture pack + Minecraft *pMinecraft = Minecraft::GetInstance(); + TexturePack *tp = pMinecraft->skins->getTexturePackById(sessionInfo->data.texturePackParentId); + HRESULT hr; + + DWORD dwImageBytes=0; + PBYTE pbImageData=NULL; + + if(tp==NULL) + { + DWORD dwBytes=0; + PBYTE pbData=NULL; + app.GetTPD(sessionInfo->data.texturePackParentId,&pbData,&dwBytes); + + // is it in the tpd data ? + app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes ); + if(dwImageBytes > 0 && pbImageData) + { + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush); + m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush); + } + } + else + { + pbImageData = tp->getPackIcon(dwImageBytes); + if(dwImageBytes > 0 && pbImageData) + { + hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush); + m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush); + } + } + } + else + { + // default texture pack + XuiCreateTextureBrushFromMemory(m_DefaultMinecraftIconData,m_DefaultMinecraftIconSize,&hXuiBrush); + m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush); + } + + + if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0) + { + m_pGamesList->SetCurSel(sessionIndex); + break; + } + ++sessionIndex; + } + } +} + +void CScene_MultiGameJoinLoad::UpdateGamesList(DWORD dwNumResults, IQNetGameSearch *pGameSearch) +{ + // We don't use the QNet callback, but could resurrect this if we ever do normal matchmaking, but updated to work as the function above +#if 0 + const XSESSION_SEARCHRESULT *pSearchResult; + const XNQOSINFO * pxnqi; + + if(m_searches>0) + --m_searches; + + if(m_searches==0) + { + m_NetGamesListTimer.SetShow( FALSE ); + + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should show the View Gamercard tooltip + int iRB=-1; + int iY = -1; + + if( DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(DoesSavesListHaveFocus()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,iRB); + } + else if(StorageManager.GetSaveDisabled()) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB); + } + } + + if( dwNumResults == 0 ) + { + if(m_searches==0 && m_GamesList.GetItemCount() == 0) + { + m_LabelNoGames.SetShow( TRUE ); + } + return; + } + + unsigned int startOffset = m_GamesList.GetItemCount(); + //m_GamesList.InsertItems(startOffset,dwNumResults); + //m_GamesList.SetEnable(TRUE); + //XuiElementSetDisableFocusRecursion( m_GamesList.m_hObj, FALSE); + + // Loop through all the results. + for( DWORD dwResult = 0; dwResult < pGameSearch->GetNumResults(); dwResult++ ) + { + + pSearchResult = pGameSearch->GetSearchResultAtIndex( dwResult ); + + // No room for us, so ignore it + if(pSearchResult->dwOpenPublicSlots < m_localPlayers) + continue; + + FriendSessionInfo *sessionInfo = NULL; + bool foundSession = false; + for(AUTO_VAR(it, friendsSessions.begin()); it < friendsSessions.end(); ++it) + { + sessionInfo = *it; + if(memcmp( &pSearchResult->info.sessionID, &sessionInfo->sessionId, sizeof(SessionID) ) == 0) + { + sessionInfo->searchResult = *pSearchResult; + sessionInfo->displayLabel = new wchar_t[100]; + foundSession = true; + break; + } + } + + // We received a search result for a session no longer in our list of friends sessions + if(!foundSession) + continue; + + // Print some info about this result. + //printf( "Search result %u:\n", dwResult ); + //printf( " public slots open = %u, filled = %u\n", pSearchResult->dwOpenPublicSlots, pSearchResult->dwFilledPublicSlots ); + //printf( " private slots open = %u, filled = %u\n", pSearchResult->dwOpenPrivateSlots, pSearchResult->dwFilledPrivateSlots ); + + // See if this result was contacted successfully via QoS probes. + pxnqi = pGameSearch->GetQosInfoAtIndex( dwResult ); + if( pxnqi->bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) + { + // Print the round trip time and the rough estimation of + // bandwidth. + app.DebugPrintf( " RTT min = %u, med = %u\n", pxnqi->wRttMinInMsecs, pxnqi->wRttMedInMsecs ); + app.DebugPrintf( " bps up = %u, down = %u\n", pxnqi->dwUpBitsPerSec, pxnqi->dwDnBitsPerSec ); + + if(pxnqi->cbData > 0) + { + sessionInfo->data = *(GameSessionData *)pxnqi->pbData; + + wstring gamerName = convStringToWstring(sessionInfo->data.hostName); + swprintf(sessionInfo->displayLabel,L"%ls's Game", gamerName.c_str() ); + } + else + { + swprintf(sessionInfo->displayLabel,L"Unknown host Game"); + } + + // If this host wasn't disabled use this one. + if( !( pxnqi->bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) && sessionInfo->data.netVersion == MINECRAFT_NET_VERSION ) + { + //printf("This game is valid\n"); + filteredResults.push_back(sessionInfo); + m_GamesList.InsertItems(startOffset,1); + m_GamesList.SetText(startOffset,sessionInfo->displayLabel); + startOffset++; + } +#ifndef _CONTENT_PACKAGE + if( sessionInfo->data.netVersion != MINECRAFT_NET_VERSION ) + { + wprintf(L"%ls version of %d does not match our version of %d\n", sessionInfo->displayLabel, sessionInfo->data.netVersion, MINECRAFT_NET_VERSION); + } +#endif + } + } + + if( m_GamesList.GetItemCount() == 0) + { + m_LabelNoGames.SetShow( TRUE ); + } + else + { + m_GamesList.SetEnable(TRUE); + XuiElementSetDisableFocusRecursion( m_GamesList.m_hObj, FALSE); + if( DoesGamesListHaveFocus() ) + { + m_GamesList.SetCurSel(0); + } + } +#endif +} + +/*void CScene_MultiGameJoinLoad::UpdateGamesListLabels() +{ + for( unsigned int i = 0; i < currentSessions.size(); ++i ) + { + FriendSessionInfo *sessionInfo = currentSessions.at(i); + m_GamesList.SetText(i,sessionInfo->displayLabel); + HXUIBRUSH hBrush; + CXuiCtrl4JList::LIST_ITEM_INFO info = m_pGamesList->GetData(i); + + // display an icon too + XuiCreateTextureBrushFromMemory(m_DefaultMinecraftIconData,m_DefaultMinecraftIconSize,&hBrush); + m_pGamesList->UpdateGraphic(i,hBrush); + } +#if 0 + XUIRect xuiRect; + HXUIOBJ item = XuiListGetItemControl(m_GamesList,0); + + HXUIOBJ hObj=NULL; + HXUIOBJ hTextPres=NULL; + HRESULT hr=XuiControlGetVisual(item,&hObj); + hr=XuiElementGetChildById(hObj,L"text_Label",&hTextPres); + + unsigned char displayLabelViewableStartIndex = 0; + for( unsigned int i = 0; i < currentSessions.size(); ++i ) + { + FriendSessionInfo *sessionInfo = currentSessions.at(i); + + if(hTextPres != NULL ) + { + hr=XuiTextPresenterMeasureText(hTextPres, sessionInfo->displayLabel, &xuiRect); + + float fWidth, fHeight; + XuiElementGetBounds(hTextPres,&fWidth,&fHeight); + int characters = (fWidth/xuiRect.right) * sessionInfo->displayLabelLength; + + if( characters < sessionInfo->displayLabelLength ) + { + static wchar_t temp[100]; + ZeroMemory(temp, (100)*sizeof(wchar_t)); + wcsncpy_s( temp, sessionInfo->displayLabel+sessionInfo->displayLabelViewableStartIndex, characters ); + m_GamesList.SetText(i,temp); + sessionInfo->displayLabelViewableStartIndex++; + if( sessionInfo->displayLabelViewableStartIndex >= sessionInfo->displayLabelLength ) sessionInfo->displayLabelViewableStartIndex = 0; + } + } + } +#endif +}*/ + +void CScene_MultiGameJoinLoad::SearchForGameCallback(void *param, DWORD dwNumResults, IQNetGameSearch *pGameSearch) +{ +#if 0 + HXUIOBJ hObj = (HXUIOBJ)param; + + void *pObj; + XuiObjectFromHandle( hObj, &pObj); + CScene_MultiGameJoinLoad *MultiGameJoinLoad = (CScene_MultiGameJoinLoad *)pObj; + + MultiGameJoinLoad->UpdateGamesList(dwNumResults, pGameSearch); +#endif +} + +int CScene_MultiGameJoinLoad::DeviceSelectReturned(void *pParam,bool bContinue) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + //HRESULT hr; + + if(bContinue==true) + { + // if the saves list has focus, then we should show the Delete Save tooltip + // if the games list has focus, then we should show the View Gamercard tooltip + int iRB=-1; + int iY = -1; + if( pClass->DoesGamesListHaveFocus() ) + { + iY = IDS_TOOLTIPS_VIEW_GAMERCARD; + } + else if(pClass->DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( pClass->m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + else if(pClass->DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + int iLB = -1; + if(pClass->m_bInParty) + { + if( pClass->m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + + //BOOL bOnlineGame=pClass->m_CheckboxOnline.IsChecked(); + + // refresh the saves list (if there is a device selected) + + // clear out the list first + + if(StorageManager.GetSaveDisabled()) + { + if(StorageManager.GetSaveDeviceSelected(pClass->m_iPad)) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB); + // saving is disabled, but we should still be able to load from a selected save device + pClass->GetSaveInfo(); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB); + // clear the saves list + pClass->m_pSavesList->RemoveAllData(); + + pClass->m_iSaveInfoC=0; + //pClass->m_iThumbnailsLoadedC=0; + + pClass->AddDefaultButtons(); + + pClass->m_SavesListTimer.SetShow( FALSE ); + + pClass->m_pSavesList->SetCurSelVisible(0); + } + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB); + pClass->GetSaveInfo(); + } + } + + // enable input again + pClass->m_bIgnoreInput=false; + + return 0; +} + +HRESULT CScene_MultiGameJoinLoad::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly + switch(pTimer->nId) + { + + + case JOIN_LOAD_ONLINE_TIMER_ID: + { + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } + + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { +// m_CheckboxOnline.SetEnable(TRUE); +// m_CheckboxPrivate.SetEnable(TRUE); + } + else + { + m_bInParty = false; + m_pGamesList->RemoveAllData(); + //m_GamesList.DeleteItems(0, m_GamesList.GetItemCount() ); + m_pGamesList->SetEnable(FALSE); + //XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE); + m_NetGamesListTimer.SetShow( TRUE ); + m_LabelNoGames.SetShow( FALSE ); + } + + int iLB = -1; + if(m_bInParty) + { + if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES; + else iLB = IDS_TOOLTIPS_PARTY_GAMES; + } + int iRB=-1; + int iY=-1; + + if( DoesGamesListHaveFocus() ) + { + } + else if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + + if(StorageManager.GetSaveDisabled()) + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + } + else if(DoesMashUpWorldHaveFocus()) + { + // If it's a mash-up pack world, give the Hide option + iRB=IDS_TOOLTIPS_HIDE; + } + + if(ProfileManager.IsFullVersion()==false ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, -1,-1,-1,iLB); + } + else if(StorageManager.GetSaveDisabled()) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,-1,-1,-1,iLB,iRB); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB); + } + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + break; + case JOIN_LOAD_SEARCH_MINIMUM_TIMER_ID: + { + XuiKillTimer( m_hObj, JOIN_LOAD_SEARCH_MINIMUM_TIMER_ID ); + m_NetGamesListTimer.SetShow( FALSE ); + m_LabelNoGames.SetShow( TRUE ); + } + break; + case JOIN_LOAD_SCROLL_GAME_NAMES_TIMER_ID: + { + // This is called by the gameslist callback function, so isn't needed on a timer + //UpdateGamesListLabels(); + } + break; + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + { + // also check for any new texture packs info being available + // for each item in the mem list, check it's in the data list + + //CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + // for each iConfig, check if the data is available, and add it to the List, then remove it from the viConfig + + for(int i=0;i 0 && pbData) + { + //update the games list + UpdateGamesList(); + + m_iConfigA[i]=-1; + } + } + } + bool bAllDone=true; + for(int i=0;im_bIgnoreInput=false; + } + return 0; +} +*/ + +int CScene_MultiGameJoinLoad::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + if(bContinue==true) + { + // It's possible that the player has not signed in - they can back out + if(ProfileManager.IsSignedIn(iPad)) + { + DWORD dwLocalUsersMask = 0; + + for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index) + { + if(ProfileManager.IsSignedIn(index) ) + { + dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index); + } + } + StartGameFromSave(pClass, dwLocalUsersMask); + } + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not +void CScene_MultiGameJoinLoad::StartGameFromSave(CScene_MultiGameJoinLoad* pClass, DWORD dwLocalUsersMask) +{ + /*bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_CheckboxOnline.IsChecked() == TRUE; + //bool isPrivate = pClass->m_CheckboxPrivate.IsChecked() == TRUE; + + SenStatGameEvent(ProfileManager.GetPrimaryPad(),eTelemetryGameEvent_Load,Minecraft::GetInstance()->options->difficulty, isClientSide, ProfileManager.IsFullVersion(), 1,0 ); + + g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = NULL; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);*/ +} + +int CScene_MultiGameJoinLoad::DeleteSaveDataReturned(void *pParam,bool bSuccess) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + if(bSuccess==true) + { + // need to re-get the saves list and update the display + // clear the saves list + pClass->m_pSavesList->RemoveAllData(); + pClass->m_iSaveInfoC=0; + pClass->GetSaveInfo(); + } + + pClass->m_bIgnoreInput=false; + + return 0; +} + +void CScene_MultiGameJoinLoad::LoadLevelGen(LevelGenerationOptions *levelGen) +{ + // Load data from disc + //File saveFile( L"Tutorial\\Tutorial" ); + //LoadSaveFromDisk(&saveFile); + + // clear out the app's terrain features list + app.ClearTerrainFeaturePosition(); + + StorageManager.ResetSaveData(); + // Make our next save default to the name of the level + StorageManager.SetSaveTitle(levelGen->getDefaultSaveName().c_str()); + + bool isClientSide = false; + bool isPrivate = false; + int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + + if( app.GetTutorialMode() ) + { + isClientSide = false; + maxPlayers = 4; + } + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = NULL; + param->settings = app.GetGameHostOption( eGameHostOption_Tutorial ); + param->levelGen = levelGen; + + if(levelGen->requiresTexturePack()) + { + param->texturePackId = levelGen->getRequiredTexturePackId(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->skins->selectTexturePackById(param->texturePackId); + //pMinecraft->skins->updateUI(); + } + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +void CScene_MultiGameJoinLoad::LoadSaveFromDisk(File *saveFile) +{ + // we'll only be coming in here when the tutorial is loaded now + + StorageManager.ResetSaveData(); + + // Make our next save default to the name of the level + StorageManager.SetSaveTitle(saveFile->getName().c_str()); + + __int64 fileSize = saveFile->length(); + FileInputStream fis(*saveFile); + byteArray ba(fileSize); + fis.read(ba); + fis.close(); + + bool isClientSide = false; + bool isPrivate = false; + int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + + if( app.GetTutorialMode() ) + { + isClientSide = false; + maxPlayers = 4; + } + + app.SetGameHostOption(eGameHostOption_GameType,GameType::CREATIVE->getId()); + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + LoadSaveDataThreadParam *saveData = new LoadSaveDataThreadParam(ba.data, ba.length, saveFile->getName()); + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = 0; + param->saveData = saveData; + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes; + completionData->iPad = DEFAULT_XUI_MENU_USER; + loadingParams->completionData = completionData; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); +} + +int CScene_MultiGameJoinLoad::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + { + pClass->m_bIgnoreInput=false; + } + else + { + XCONTENT_DATA XContentData; + StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData); + StorageManager.DeleteSaveData(&XContentData,CScene_MultiGameJoinLoad::DeleteSaveDataReturned,pClass); + pClass->m_SavesListTimer.SetShow( TRUE ); + } + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int CScene_MultiGameJoinLoad::SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultAccept) + { + // upload the save + + // first load the save + int iIndex=pClass->m_pSavesList->GetData(pClass->m_pSavesList->GetCurSel()).iIndex-pClass->m_iDefaultButtonsC; + XCONTENT_DATA ContentData; + + // 4J-PB - ensure we've switched to the right title group id for uploading to + app.TMSPP_SetTitleGroupID(SAVETRANSFER_GROUP_ID); + StorageManager.GetSaveCacheFileInfo(iIndex,ContentData); + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&ContentData,CScene_MultiGameJoinLoad::LoadSaveDataReturned,pClass); + + pClass->m_bIgnoreInput=false; + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int CScene_MultiGameJoinLoad::UploadSaveForXboxOneThreadProc( LPVOID lpParameter ) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParameter; + Minecraft *pMinecraft = Minecraft::GetInstance(); + + pMinecraft->progressRenderer->progressStart(IDS_SAVE_TRANSFER_TITLE); + pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADING ); + + // Delete the marker file + DeleteFile(pClass, "completemarker"); + if(!WaitForTransferComplete(pClass)) return 0; + + // Upload the save data + { + unsigned int uiSaveBytes; + uiSaveBytes=StorageManager.GetSaveSize(); + pClass->m_pbSaveTransferData=new BYTE [uiSaveBytes]; + + StorageManager.GetSaveData(pClass->m_pbSaveTransferData,&uiSaveBytes); + + app.DebugPrintf("Uploading save data (%d bytes)\n", uiSaveBytes); + UploadFile(pClass, "savedata", pClass->m_pbSaveTransferData, uiSaveBytes); + } + + if(!WaitForTransferComplete(pClass)) return 0; + if(pClass->m_bTransferFail) + { + // something went wrong, user has been informed + pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED ); + return 0; + } + + // Upload the metadata and thumbnail + { + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + LPCWSTR title = StorageManager.GetSaveTitle(); + dos.writeUTF(title); + + char szUniqueMapName[14]; + StorageManager.GetSaveUniqueFilename(szUniqueMapName); + dos.writeUTF(convStringToWstring(szUniqueMapName)); + + { + // set the save icon + PBYTE pbImageData=NULL; + DWORD dwImageBytes=0; + XCONTENT_DATA XContentData; + int iIndex=pClass->m_pSavesList->GetData(pClass->m_pSavesList->GetCurSel()).iIndex-pClass->m_iDefaultButtonsC; + StorageManager.GetSaveCacheFileInfo(iIndex,XContentData); + StorageManager.GetSaveCacheFileInfo(iIndex,&pbImageData,&dwImageBytes); + + // if there is no thumbnail, retrieve the default one from the file. + // Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save + if(pbImageData==NULL) + { + DWORD dwResult=XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,NULL,&dwImageBytes,NULL); + if(dwResult==ERROR_SUCCESS) + { + pClass->m_pbSaveTransferData = new BYTE[dwImageBytes]; + pbImageData = pClass->m_pbSaveTransferData; // Copy pointer so that we can use the same name as the library owned one, but m_pbSaveTransferData will get deleted when done + XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,pbImageData,&dwImageBytes,NULL); + } + } + + dos.writeInt(dwImageBytes); + + byteArray ba(pbImageData, dwImageBytes); + dos.write(ba); + } + + pClass->m_pbSaveTransferData=new BYTE [baos.size()]; + memcpy(pClass->m_pbSaveTransferData,baos.buf.data,baos.size()); + + app.DebugPrintf("Uploading meta data (%d bytes)\n", baos.size()); + UploadFile(pClass, "metadata", pClass->m_pbSaveTransferData, baos.size()); + } + + // Wait for metadata and thumbnail + if(!WaitForTransferComplete(pClass)) return 0; + if(pClass->m_bTransferFail) + { + // something went wrong, user has been informed + pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED ); + return 0; + } + + // Upload the marker file + { + char singleByteData[1] = {1}; + app.DebugPrintf("Uploading marker (%d bytes)\n", 1); + UploadFile(pClass, "completemarker", &singleByteData, 1); + } + + // Wait for marker + if(!WaitForTransferComplete(pClass)) return 0; + if(pClass->m_bTransferFail) + { + // something went wrong, user has been informed + pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED ); + + return 0; + } + // change text for completion confirmation + pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADCOMPLETE ); + + // done + return 0; +} + +void CScene_MultiGameJoinLoad::DeleteFile(CScene_MultiGameJoinLoad *pClass, char *filename) +{ + pClass->m_fProgress=0.0f; + pClass->m_bTransferComplete=false; + + C4JStorage::ETMSStatus result = StorageManager.TMSPP_DeleteFile( + ProfileManager.GetPrimaryPad(), + filename, + C4JStorage::TMS_FILETYPE_BINARY, + &CScene_MultiGameJoinLoad::DeleteComplete, + pClass, + NULL); + + if(result != C4JStorage::ETMSStatus_DeleteInProgress) + { + DeleteComplete(pClass,ProfileManager.GetPrimaryPad(), -1); + } +} + +void CScene_MultiGameJoinLoad::UploadFile(CScene_MultiGameJoinLoad *pClass, char *filename, LPVOID data, DWORD size) +{ + pClass->m_fProgress=0.0f; + pClass->m_bTransferComplete=false; + + C4JStorage::ETMSStatus result = StorageManager.TMSPP_WriteFileWithProgress( + ProfileManager.GetPrimaryPad(), + C4JStorage::eGlobalStorage_TitleUser, + C4JStorage::TMS_FILETYPE_BINARY, + C4JStorage::TMS_UGCTYPE_NONE, + filename, + (CHAR *)data, + size, + &CScene_MultiGameJoinLoad::TransferComplete,pClass, 0, + &CScene_MultiGameJoinLoad::Progress,pClass); + +#ifdef _DEBUG_MENUS_ENABLED + if(app.GetWriteSavesToFolderEnabled()) + { + File targetFileDir(L"GAME:\\FakeTMSPP"); + if(!targetFileDir.exists()) targetFileDir.mkdir(); + string path = string( wstringtofilename( targetFileDir.getPath() ) ).append("\\").append(filename); + HANDLE hSaveFile = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL); + + DWORD numberOfBytesWritten = 0; + WriteFile( hSaveFile,data,size,&numberOfBytesWritten,NULL); + assert(numberOfBytesWritten == size); + + CloseHandle(hSaveFile); + } +#endif + + if(result != C4JStorage::ETMSStatus_WriteInProgress) + { + TransferComplete(pClass,ProfileManager.GetPrimaryPad(), -1); + } +} + +bool CScene_MultiGameJoinLoad::WaitForTransferComplete( CScene_MultiGameJoinLoad *pClass ) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + // loop until complete + while(pClass->m_bTransferComplete==false) + { + // check for a cancel + if(pClass->m_bSaveTransferInProgress==false) + { + // cancelled + return false; + } + Sleep(50); + // update the progress + pMinecraft->progressRenderer->progressStagePercentage((unsigned int)(pClass->m_fProgress*100.0f)); + } + + // was there a transfer error? + + return true; +} + +int CScene_MultiGameJoinLoad::SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + // results switched for this dialog + // EMessage_ResultAccept means cancel + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) + { + if(result==C4JStorage::EMessage_ResultDecline) // rename + { + ZeroMemory(pClass->m_wchNewName,sizeof(WCHAR)*XCONTENT_MAX_DISPLAYNAME_LENGTH); + // bring up a keyboard + InputManager.RequestKeyboard(IDS_RENAME_WORLD_TITLE,L"",IDS_RENAME_WORLD_TEXT,iPad,pClass->m_wchNewName,XCONTENT_MAX_DISPLAYNAME_LENGTH,&CScene_MultiGameJoinLoad::KeyboardReturned,pClass,C_4JInput::EKeyboardMode_Default,app.GetStringTable()); + } + else // delete + { + // delete the save game + // Have to ask the player if they are sure they want to delete this game + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,pClass, app.GetStringTable()); + //pClass->m_bIgnoreInput=false; + } + } + else + { + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int CScene_MultiGameJoinLoad::LoadSaveDataReturned(void *pParam,bool bContinue) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + if(bContinue==true) + { + pClass->m_bSaveTransferInProgress=true; + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CScene_MultiGameJoinLoad::UploadSaveForXboxOneThreadProc; + loadingParams->lpParam = (LPVOID)pParam; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->type = e_ProgressCompletion_NavigateBack; + completionData->iPad = DEFAULT_XUI_MENU_USER; + completionData->bRequiresUserAction=TRUE; + loadingParams->completionData = completionData; + + loadingParams->cancelFunc=&CScene_MultiGameJoinLoad::CancelSaveUploadCallback; + loadingParams->completeFunc=&CScene_MultiGameJoinLoad::SaveUploadCompleteCallback; + loadingParams->m_cancelFuncParam=pClass; + loadingParams->m_completeFuncParam=pClass; + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + } + else + { + // switch back to the normal title group id + app.TMSPP_SetTitleGroupID(GROUP_ID); + + // the save is corrupt! + + pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + + // give the option to delete the save + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, + pClass->m_iPad,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,pClass, app.GetStringTable()); + + } + + return 0; +} + +int CScene_MultiGameJoinLoad::Progress(void *pParam,float fProgress) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + app.DebugPrintf("Progress - %f\n",fProgress); + pClass->m_fProgress=fProgress; + return 0; +} + +int CScene_MultiGameJoinLoad::TransferComplete(void *pParam,int iPad, int iResult) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + + delete [] pClass->m_pbSaveTransferData; + pClass->m_pbSaveTransferData = NULL; + if(iResult!=0) + { + // There was a transfer fail + // Display a dialog + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_SAVE_TRANSFER_TITLE, IDS_SAVE_TRANSFER_UPLOADFAILED, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL,app.GetStringTable()); + pClass->m_bTransferFail=true; + } + else + { + pClass->m_bTransferFail=false; + } + pClass->m_bTransferComplete=true; + //pClass->m_bSaveTransferInProgress=false; + return 0; +} + +int CScene_MultiGameJoinLoad::DeleteComplete(void *pParam,int iPad, int iResult) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + pClass->m_bTransferComplete=true; + return 0; +} + +int CScene_MultiGameJoinLoad::KeyboardReturned(void *pParam,bool bSet) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; + HRESULT hr = S_OK; + + // if the user has left the name empty, treat this as backing out + if((pClass->m_wchNewName[0]!=0) && bSet) + { +#ifdef _XBOX + XCONTENT_DATA XContentData; + StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData); + + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&XContentData,CScene_MultiGameJoinLoad::LoadSaveDataForRenameReturned,pClass); + + if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved) + { + // disable saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(ProfileManager.GetPrimaryPad(),false); + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::DeviceRemovedDialogReturned,pClass); + } +#else + // rename the save + +#endif + } + else + { + pClass->m_bIgnoreInput=false; + } + + return hr; +} + +int CScene_MultiGameJoinLoad::LoadSaveDataForRenameReturned(void *pParam,bool bContinue) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; +#ifdef _XBOX + if(bContinue==true) + { + // set the save icon + PBYTE pbImageData=NULL; + DWORD dwImageBytes=0; + HXUIBRUSH hXuiBrush; + XCONTENT_DATA XContentData; + StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData); + StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,&pbImageData,&dwImageBytes); + + // if there is no thumbnail, retrieve the default one from the file. + // Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save + if(pbImageData==NULL) + { + DWORD dwResult=XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,NULL,&dwImageBytes,NULL); + if(dwResult==ERROR_SUCCESS) + { + pbImageData = new BYTE[dwImageBytes]; + XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,pbImageData,&dwImageBytes,NULL); + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush); + } + } + else + { + XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush); + } + // save the data with this icon + StorageManager.CopySaveDataToNewSave( pbImageData,dwImageBytes,pClass->m_wchNewName,&CScene_MultiGameJoinLoad::CopySaveReturned,pClass); + } + else +#endif + { + //pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + } + return 0; +} + +int CScene_MultiGameJoinLoad::CopySaveReturned(void *pParam,bool bResult) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam; +#ifdef _XBOX + if(bResult) + { + // and delete the old save + XCONTENT_DATA XContentData; + StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData); + StorageManager.DeleteSaveData(&XContentData,CScene_MultiGameJoinLoad::DeleteSaveDataReturned,pClass); + pClass->m_SavesListTimer.SetShow( TRUE ); + } + else +#endif + { + //pClass->SetShow( TRUE ); + pClass->m_bIgnoreInput=false; + } + + return 0; +} + +int CScene_MultiGameJoinLoad::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_MultiGameJoinLoad *pClass = (CScene_MultiGameJoinLoad *)pParam; + + // Exit with or without saving + // Decline means install full version of the texture pack in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultAccept) + { + // we need to enable background downloading for the DLC + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + ULONGLONG ullOfferID_Full; + ULONGLONG ullIndexA[1]; + app.GetDLCFullOfferIDForPackID(pClass->m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full); + + if( result==C4JStorage::EMessage_ResultAccept ) // Full version + { + ullIndexA[0]=ullOfferID_Full; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + } + else // trial version + { + // if there is no trial version, this is a Cancel + DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full); + if(pDLCInfo->ullOfferID_Trial!=0LL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Trial; + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + } + pClass->m_bIgnoreInput=false; + return 0; +} + +HRESULT CScene_MultiGameJoinLoad::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + // clear out the saves list and re-fill + + m_pSavesList->RemoveAllData(); + m_SavesListTimer.SetShow( TRUE ); + } + // this will send a CustomMessage_DLCMountingComplete when done + return S_OK; +} + +HRESULT CScene_MultiGameJoinLoad::OnCustomMessage_DLCMountingComplete() +{ + + VOID *pObj; + XuiObjectFromHandle( m_SavesList, &pObj ); + m_pSavesList = (CXuiCtrl4JList *)pObj; + + m_iChangingSaveGameInfoIndex = 0; + + m_generators = app.getLevelGenerators(); + m_iDefaultButtonsC = 0; + m_iMashUpButtonsC = 0; + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } + + int iLB = -1; + + int iY=-1; + if(DoesSavesListHaveFocus()) + { + if(ProfileManager.IsSignedInLive( m_iPad )) + { + iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE; + } + } + if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES; + // check if we're in the trial version + if(ProfileManager.IsFullVersion()==false) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB); + + AddDefaultButtons(); + + m_pSavesList->SetCurSelVisible(0); + } + else if(StorageManager.GetSaveDisabled()) + { + if(StorageManager.GetSaveDeviceSelected(m_iPad)) + { + // saving is disabled, but we should still be able to load from a selected save device + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,IDS_TOOLTIPS_DELETESAVE); + + GetSaveInfo(); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB); + + AddDefaultButtons(); + m_SavesListTimer.SetShow( FALSE ); + + m_pSavesList->SetCurSelVisible(0); + } + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame(); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,-1,bCanRename?IDS_TOOLTIPS_SAVEOPTIONS:IDS_TOOLTIPS_DELETESAVE); + + GetSaveInfo(); + } + + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + return S_OK; +} + +/* +void CScene_MultiGameJoinLoad::UpdateTooltips() +{ + int iA=IDS_TOOLTIPS_SELECT; + int iB=IDS_TOOLTIPS_BACK; + int iX=-1; + int iY=-1 + int iLB = -1; + XPARTY_USER_LIST partyList; + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + { + m_bInParty=true; + } + else + { + m_bInParty=false; + } + + if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES; + + if(ProfileManager.IsFullVersion()==false) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB); + } + else if(StorageManager.GetSaveDisabled()) + { + if(StorageManager.GetSaveDeviceSelected(m_iPad)) + { + // saving is disabled, but we should still be able to load from a selected save device + iX=IDS_TOOLTIPS_CHANGEDEVICE; + iRB=IDS_TOOLTIPS_DELETESAVE; + } + else + { + iX=IDS_TOOLTIPS_SELECTDEVICE; + } + } + else + { + // 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename) + bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame(); + + if(bCanRename) + { + iRB=IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iRB=IDS_TOOLTIPS_DELETESAVE; + } + } + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, iA,iB, iX, iY, iLT, iRT,iLB, iRB); +} +*/ + + + +#ifdef _XBOX +bool CScene_MultiGameJoinLoad::GetSavesInfoCallback(LPVOID pParam,int iTotalSaveInfoC, C4JStorage::CACHEINFOSTRUCT *InfoA, int iPad, HRESULT hResult) +{ + CScene_MultiGameJoinLoad *pClass=(CScene_MultiGameJoinLoad *)pParam; + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + PBYTE pbImageData=(PBYTE)InfoA; + PBYTE pbCurrentImagePtr; + HXUIBRUSH hXuiBrush; + HRESULT hr; + + // move the image data pointer to the right place + if(iTotalSaveInfoC!=0) + { + pbImageData+=sizeof(C4JStorage::CACHEINFOSTRUCT)*iTotalSaveInfoC; + } + + pClass->m_SavesListTimer.SetShow( FALSE ); + pClass->m_SavesList.SetEnable(TRUE); + + pClass->AddDefaultButtons(); + + for(int i=0;im_pSavesList->AddData(ListInfo,-1); + + // update the graphic on the list item + + // if there is no thumbnail, this is a corrupt file + if(InfoA[i].dwImageBytes!=0) + { + pbCurrentImagePtr=pbImageData+InfoA[i].dwImageOffset; + hr=XuiCreateTextureBrushFromMemory(pbCurrentImagePtr,InfoA[i].dwImageBytes,&hXuiBrush); + pClass->m_pSavesList->UpdateGraphic(i+pClass->m_iDefaultButtonsC,hXuiBrush ); + } + else + { + // we could put in a damaged save icon here + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + + swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/MinecraftBrokenIcon.png"); + + XuiCreateTextureBrush(szResourceLocator,&hXuiBrush); + pClass->m_pSavesList->UpdateGraphic(i+pClass->m_iDefaultButtonsC,hXuiBrush ); + } + } + } + + pClass->m_iSaveInfoC=iTotalSaveInfoC; + + // If there are some saves, then set the focus to be on the most recent one, which will be the first one after the create and tutorial + if(iTotalSaveInfoC>0) + { + pClass->m_pSavesList->SetCurSelVisible(pClass->m_iDefaultButtonsC); + pClass->m_bReady=true; + } + + pClass->m_bRetrievingSaveInfo=false; + + // It's possible that the games list is updated but we haven't displayed it yet as we were still waiting on saves list to load + // This is to fix a bug where joining a game before the saves list has loaded causes a crash when this callback is called + // as the scene no longer exists + pClass->UpdateGamesList(); + + // Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + + return false; +} +#else +int CScene_MultiGameJoinLoad::GetSavesInfoCallback(LPVOID lpParam,const bool) +{ + return true; +} +#endif + +void CScene_MultiGameJoinLoad::CancelSaveUploadCallback(LPVOID lpParam) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam; + + StorageManager.TMSPP_CancelWriteFileWithProgress(pClass->m_iPad); + + pClass->m_bSaveTransferInProgress=false; + + // change back to the normal title group id + app.TMSPP_SetTitleGroupID(GROUP_ID); +// app.getRemoteStorage()->abort(); +// pClass->m_eSaveUploadState = eSaveUpload_Idle; + + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + ui.RequestMessageBox(IDS_XBONE_CANCEL_UPLOAD_TITLE, IDS_XBONE_CANCEL_UPLOAD_TEXT, uiIDA, 1, pClass->m_iPad, NULL, NULL, app.GetStringTable()); +} + +void CScene_MultiGameJoinLoad::SaveUploadCompleteCallback(LPVOID lpParam) +{ + CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam; + + pClass->m_bSaveTransferInProgress=false; + // change back to the normal title group id + app.TMSPP_SetTitleGroupID(GROUP_ID); + // app.getRemoteStorage()->abort(); + // pClass->m_eSaveUploadState = eSaveUpload_Idle; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.h b/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.h new file mode 100644 index 0000000..e91fbac --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameJoinLoad.h @@ -0,0 +1,168 @@ +#pragma once +using namespace std; +#include +#include "..\Media\xuiscene_multi_joinload.h" +#include "XUI_CustomMessages.h" + + +#define JOIN_LOAD_CREATE_BUTTON_INDEX 0 + +#define JOIN_LOAD_ONLINE_TIMER_ID 0 +#define JOIN_LOAD_ONLINE_TIMER_TIME 100 +#define JOIN_LOAD_SEARCH_MINIMUM_TIMER_ID 1 +#define JOIN_LOAD_SEARCH_MINIMUM_TIMER_TIME 2000 +#define JOIN_LOAD_SCROLL_GAME_NAMES_TIMER_ID 2 +#define JOIN_LOAD_SCROLL_GAME_NAMES_TIMER_TIME 1000 + +class CXuiCtrl4JList; +class LevelGenerationOptions; +class CScene_MultiGameInfo; + +class CScene_MultiGameJoinLoad : public CXuiSceneImpl +{ +protected: + CXuiCtrl4JList *m_pSavesList; + CXuiCtrl4JList *m_pGamesList; + CXuiList m_SavesList; + CXuiList m_GamesList; + CXuiControl m_SavesListTimer; + CXuiControl m_NetGamesListTimer; + CXuiControl m_LabelNoGames; + int m_iPad; + + bool m_bShowingPartyGamesOnly; + bool m_bInParty; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_NOTIFY_SELCHANGED(OnNotifySelChanged) + XUI_ON_XM_NOTIFY_SET_FOCUS(OnNotifySetFocus) + XUI_ON_XM_NOTIFY_KILL_FOCUS(OnNotifyKillFocus) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_FONTRENDERERCHANGE_MESSAGE(OnFontRendererChange) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_GamesList, m_GamesList) + MAP_CONTROL(IDC_SavesTimer, m_SavesListTimer) + MAP_CONTROL(IDC_Timer, m_NetGamesListTimer) + MAP_CONTROL(IDC_LabelNoGames, m_LabelNoGames) + MAP_CONTROL(IDC_SavesList, m_SavesList) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled); + HRESULT OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnFontRendererChange(); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); + + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_MultiGameJoinLoad, L"CScene_MultiGameJoinLoad", XUI_CLASS_SCENE ) + +private: + bool DoesSavesListHaveFocus(); + bool DoesGamesListHaveFocus(); + bool DoesMashUpWorldHaveFocus(); + +public: + static void UpdateGamesListCallback(LPVOID pParam); + +private: + void AddDefaultButtons(); + void UpdateGamesList(); + void UpdateGamesList(DWORD dwNumResults, IQNetGameSearch *pGameSearch); + //void UpdateGamesListLabels(); + static void SearchForGameCallback(void *hObj, DWORD dwNumResults, IQNetGameSearch *pGameSearch); + static int DeviceSelectReturned(void *pParam,bool bContinue); + + unsigned char m_localPlayers; + + HRESULT GetSaveInfo( ); + static int LoadSaveDataReturned(void *pParam,bool bContinue); + static int DeleteSaveDataReturned(void *pParam,bool bSuccess); + + unsigned int m_uiSaveC; + void LoadLevelGen(LevelGenerationOptions *levelGen); + void LoadSaveFromDisk(File *saveFile); + + // callback +#ifdef _XBOX + static bool GetSavesInfoCallback(LPVOID pParam,int iInstalledC, C4JStorage::CACHEINFOSTRUCT *InfoA, int iPad, HRESULT hRes); +#else + static int GetSavesInfoCallback(LPVOID lpParam,const bool); +#endif + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int StartGame_SignInReturned(void *pParam,bool bContinue, int iPad); + static int CopySaveReturned(void *pParam,bool bResult); + static int LoadSaveDataForRenameReturned(void *pParam,bool bContinue); + static int KeyboardReturned(void *pParam,bool bSet); + static void StartGameFromSave(CScene_MultiGameJoinLoad* pClass, DWORD dwLocalUsersMask); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + static int Progress(void *pParam,float fProgress); + static int TransferComplete(void *pParam,int i1, int i2); + static int DeleteComplete(void *pParam,int i1, int i2); + static int UploadSaveForXboxOneThreadProc( LPVOID lpParameter ); + static void DeleteFile(CScene_MultiGameJoinLoad *pClass, char *filename); + static void UploadFile(CScene_MultiGameJoinLoad *pClass, char *filename, LPVOID data, DWORD size); + static bool WaitForTransferComplete( CScene_MultiGameJoinLoad *pClass ); + static void CancelSaveUploadCallback(LPVOID lpParam); + static void SaveUploadCompleteCallback(LPVOID lpParam); + + + bool m_bIgnoreInput; + vector *m_saves; + + int m_iSaveInfoC; + int m_iDefaultButtonsC; + int m_iMashUpButtonsC; + int m_iChangingSaveGameInfoIndex; + + bool m_bMultiplayerAllowed; + bool m_bKillSaveInfoEnumerate; + + vector currentSessions; + bool m_bReady; + bool m_bRetrievingSaveInfo; + //bool m_bSaveRenamed; + WCHAR m_wchNewName[XCONTENT_MAX_DISPLAYNAME_LENGTH]; + unsigned char m_szSeed[50]; + + vector *m_generators; + JoinMenuInitData *m_initData; + + UINT m_DefaultMinecraftIconSize; + PBYTE m_DefaultMinecraftIconData; + int *m_iConfigA; // track the texture packs that we don't have installed + int m_iTexturePacksNotInstalled; + PBYTE m_pbSaveTransferData; + + float m_fProgress; + bool m_bTransferComplete; + bool m_bTransferFail; + bool m_bSaveTransferInProgress; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.cpp b/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.cpp new file mode 100644 index 0000000..0865611 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "XUI_MultiGameCreate.h" +#include "XUI_MultiGameLaunchMoreOptions.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\Minecraft.h" + +#define GAME_CREATE_ONLINE_TIMER_ID 0 +#define GAME_CREATE_ONLINE_TIMER_TIME 100 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameLaunchMoreOptions::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_CheckboxOnline,app.GetString(IDS_ONLINE_GAME)); + XuiControlSetText(m_CheckboxInviteOnly,app.GetString(IDS_INVITE_ONLY)); + XuiControlSetText(m_CheckboxAllowFoF,app.GetString(IDS_ALLOWFRIENDSOFFRIENDS)); + XuiControlSetText(m_CheckboxPVP,app.GetString(IDS_PLAYER_VS_PLAYER)); + XuiControlSetText(m_CheckboxTrustPlayers,app.GetString(IDS_TRUST_PLAYERS)); + XuiControlSetText(m_CheckboxFireSpreads,app.GetString(IDS_FIRE_SPREADS)); + XuiControlSetText(m_CheckboxTNTExplodes,app.GetString(IDS_TNT_EXPLODES)); + XuiControlSetText(m_CheckboxHostPrivileges,app.GetString(IDS_HOST_PRIVILEGES)); + XuiControlSetText(m_CheckboxResetNether,app.GetString(IDS_RESET_NETHER)); + XuiControlSetText(m_LabelWorldOptions,app.GetString(IDS_WORLD_OPTIONS)); + XuiControlSetText(m_CheckboxStructures,app.GetString(IDS_GENERATE_STRUCTURES)); + XuiControlSetText(m_CheckboxFlatWorld,app.GetString(IDS_SUPERFLAT_WORLD)); + XuiControlSetText(m_CheckboxBonusChest,app.GetString(IDS_BONUS_CHEST)); + + m_params = (LaunchMoreOptionsMenuInitData *)pInitData->pvInitData; + + if(m_params->bGenerateOptions) + { + m_CheckboxStructures.SetEnable(TRUE); + m_CheckboxFlatWorld.SetEnable(TRUE); + m_CheckboxBonusChest.SetEnable(TRUE); + m_CheckboxStructures.SetCheck(m_params->bStructures); + m_CheckboxFlatWorld.SetCheck (m_params->bFlatWorld); + m_CheckboxBonusChest.SetCheck(m_params->bBonusChest); + + // This is a create world, so don't need Reset Nether + float fHeight, fCheckboxHeight, fWidth; + m_CheckboxResetNether.GetBounds(&fWidth, &fCheckboxHeight); + + m_HostOptionGroup.GetBounds(&fWidth, &fHeight); + m_HostOptionGroup.SetBounds(fWidth, fHeight - fCheckboxHeight); + + GetBounds(&fWidth,&fHeight); + SetBounds(fWidth, fHeight - fCheckboxHeight); + + D3DXVECTOR3 pos; + GetPosition(&pos); + pos.y += (fCheckboxHeight/2); + SetPosition(&pos); + + m_GenerationGroup.GetPosition(&pos); + pos.y -= fCheckboxHeight; + m_GenerationGroup.SetPosition(&pos); + + m_CheckboxResetNether.SetShow(FALSE); + } + else + { + float fHeight, fGroupHeight, fWidth; + m_GenerationGroup.GetBounds(&fWidth, &fGroupHeight); + m_GenerationGroup.SetShow(FALSE); + m_GenerationGroup.SetEnable(FALSE); + m_CheckboxStructures.SetShow(FALSE); + m_CheckboxFlatWorld.SetShow(FALSE); + m_CheckboxBonusChest.SetShow(FALSE); + + GetBounds(&fWidth,&fHeight); + SetBounds(fWidth, fHeight +10 - fGroupHeight); + + D3DXVECTOR3 pos; + GetPosition(&pos); + pos.y += (fGroupHeight/2); + SetPosition(&pos); + + m_CheckboxResetNether.SetEnable(TRUE); + m_CheckboxResetNether.SetCheck(m_params->bResetNether); + } + + m_CheckboxPVP.SetEnable(TRUE); + m_CheckboxTrustPlayers.SetEnable(TRUE); + m_CheckboxFireSpreads.SetEnable(TRUE); + m_CheckboxTNTExplodes.SetEnable(TRUE); + m_CheckboxHostPrivileges.SetEnable(TRUE); + + m_CheckboxPVP.SetCheck(m_params->bPVP); + m_CheckboxTrustPlayers.SetCheck (m_params->bTrust); + m_CheckboxFireSpreads.SetCheck(m_params->bFireSpreads); + m_CheckboxTNTExplodes.SetCheck(m_params->bTNT); + m_CheckboxHostPrivileges.SetCheck(m_params->bHostPrivileges); + + + m_CheckboxOnline.SetCheck(m_params->bOnlineGame); + m_CheckboxInviteOnly.SetCheck(m_params->bInviteOnly); + m_CheckboxAllowFoF.SetCheck(m_params->bAllowFriendsOfFriends); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_params->iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); + if(m_params->bOnlineSettingChangedBySystem) + { + m_CheckboxOnline.SetCheck(FALSE); + m_CheckboxOnline.SetEnable(FALSE); + m_CheckboxInviteOnly.SetCheck(FALSE); + m_CheckboxInviteOnly.SetEnable(FALSE); + m_CheckboxAllowFoF.SetCheck(FALSE); + m_CheckboxAllowFoF.SetEnable(FALSE); + } + else if(!m_params->bOnlineGame) + { + m_CheckboxInviteOnly.SetEnable(FALSE); + m_CheckboxAllowFoF.SetEnable(FALSE); + } + + XuiSetTimer(m_hObj,GAME_CREATE_ONLINE_TIMER_ID,GAME_CREATE_ONLINE_TIMER_TIME); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, FALSE ); + + + //SentientManager.RecordMenuShown(m_params->iPad, eUIScene_CreateWorldMenu, 0); + + return S_OK; +} + + +HRESULT CScene_MultiGameLaunchMoreOptions::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + m_params->bOnlineGame = m_CheckboxOnline.IsChecked(); + m_params->bInviteOnly = m_CheckboxInviteOnly.IsChecked(); + m_params->bAllowFriendsOfFriends = m_CheckboxAllowFoF.IsChecked(); + + m_params->bStructures = m_CheckboxStructures.IsChecked(); + m_params->bFlatWorld = m_CheckboxFlatWorld.IsChecked(); + m_params->bBonusChest = m_CheckboxBonusChest.IsChecked(); + m_params->bPVP = m_CheckboxPVP.IsChecked(); + m_params->bTrust = m_CheckboxTrustPlayers.IsChecked(); + m_params->bFireSpreads = m_CheckboxFireSpreads.IsChecked(); + m_params->bTNT = m_CheckboxTNTExplodes.IsChecked(); + + m_params->bHostPrivileges = m_CheckboxHostPrivileges.IsChecked(); + + m_params->bResetNether = m_CheckboxResetNether.IsChecked(); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + return S_OK; +} + + +HRESULT CScene_MultiGameLaunchMoreOptions::OnNotifySetFocus( HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled ) +{ + int stringId = 0; + if(hObjSource == m_CheckboxStructures) + { + stringId = IDS_GAMEOPTION_STRUCTURES; + } + else if(hObjSource == m_CheckboxOnline) + { + stringId = IDS_GAMEOPTION_ONLINE; + } + else if(hObjSource == m_CheckboxInviteOnly) + { + stringId = IDS_GAMEOPTION_INVITEONLY; + } + else if(hObjSource == m_CheckboxAllowFoF) + { + stringId = IDS_GAMEOPTION_ALLOWFOF; + } + else if(hObjSource == m_CheckboxFlatWorld) + { + stringId = IDS_GAMEOPTION_SUPERFLAT; + } + else if(hObjSource == m_CheckboxBonusChest) + { + stringId = IDS_GAMEOPTION_BONUS_CHEST; + } + else if(hObjSource == m_CheckboxPVP) + { + stringId = IDS_GAMEOPTION_PVP; + } + else if(hObjSource == m_CheckboxTrustPlayers) + { + stringId = IDS_GAMEOPTION_TRUST; + } + else if(hObjSource == m_CheckboxFireSpreads) + { + stringId = IDS_GAMEOPTION_FIRE_SPREADS; + } + else if(hObjSource == m_CheckboxTNTExplodes) + { + stringId = IDS_GAMEOPTION_TNT_EXPLODES; + } + else if(hObjSource == m_CheckboxHostPrivileges) + { + stringId = IDS_GAMEOPTION_HOST_PRIVILEGES; + } + else if(hObjSource == m_CheckboxResetNether) + { + stringId = IDS_GAMEOPTION_RESET_NETHER; + } + + wstring wsText=app.GetString(stringId); + int size = 14; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = 12; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White), size); + wsText= startTags + wsText; + + m_Description.SetText(wsText.c_str()); + return S_OK; +} + +HRESULT CScene_MultiGameLaunchMoreOptions::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_MultiGameLaunchMoreOptions::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_CheckboxOnline) + { + if(m_CheckboxOnline.IsChecked()) + { + m_CheckboxInviteOnly.SetEnable(TRUE); + m_CheckboxAllowFoF.SetEnable(TRUE); + } + else + { + m_CheckboxInviteOnly.SetCheck(FALSE); + m_CheckboxInviteOnly.SetEnable(FALSE); + m_CheckboxAllowFoF.SetCheck(FALSE); + m_CheckboxAllowFoF.SetEnable(FALSE); + } + } + return S_OK; +} + +HRESULT CScene_MultiGameLaunchMoreOptions::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + // 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly + if( pTimer->nId == GAME_CREATE_ONLINE_TIMER_ID) + { + bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_params->iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_params->iPad); + + if(bMultiplayerAllowed != m_bMultiplayerAllowed) + { + if( bMultiplayerAllowed ) + { + bool bGameSetting_Online=(app.GetGameSettings(m_params->iPad,eGameSetting_Online)!=0); + m_CheckboxOnline.SetCheck(bGameSetting_Online?TRUE:FALSE); + if(bGameSetting_Online) + { + m_CheckboxInviteOnly.SetCheck((app.GetGameSettings(m_params->iPad,eGameSetting_InviteOnly)!=0)?TRUE:FALSE); + m_CheckboxAllowFoF.SetCheck((app.GetGameSettings(m_params->iPad,eGameSetting_FriendsOfFriends)!=0)?TRUE:FALSE); + } + else + { + m_CheckboxInviteOnly.SetCheck(FALSE); + m_CheckboxAllowFoF.SetCheck(FALSE); + } + } + else + { + m_CheckboxOnline.SetCheck(FALSE); + m_CheckboxOnline.SetEnable(FALSE); + m_CheckboxInviteOnly.SetCheck(FALSE); + m_CheckboxInviteOnly.SetEnable(FALSE); + m_CheckboxAllowFoF.SetCheck(FALSE); + } + + m_bMultiplayerAllowed = bMultiplayerAllowed; + } + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.h b/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.h new file mode 100644 index 0000000..9287624 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_MultiGameLaunchMoreOptions.h @@ -0,0 +1,75 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_multi_launch_more_options.h" + +class CScene_MultiGameLaunchMoreOptions : public CXuiSceneImpl +{ +protected: + CXuiScene m_GenerationGroup, m_HostOptionGroup; + CXuiCheckbox m_CheckboxOnline; + CXuiCheckbox m_CheckboxInviteOnly; + CXuiCheckbox m_CheckboxAllowFoF; + CXuiCheckbox m_CheckboxStructures; + CXuiCheckbox m_CheckboxFlatWorld; + CXuiCheckbox m_CheckboxBonusChest; + CXuiCheckbox m_CheckboxPVP; + CXuiCheckbox m_CheckboxTrustPlayers; + CXuiCheckbox m_CheckboxFireSpreads; + CXuiCheckbox m_CheckboxTNTExplodes; + CXuiCheckbox m_CheckboxHostPrivileges; + CXuiCheckbox m_CheckboxResetNether; + CXuiControl m_Description; + CXuiControl m_LabelWorldOptions; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_SET_FOCUS( OnNotifySetFocus ) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_GenerationOptions, m_GenerationGroup) + BEGIN_MAP_CHILD_CONTROLS(m_GenerationGroup) + MAP_CONTROL(IDC_WorldOptions, m_LabelWorldOptions) + MAP_CONTROL(IDC_CheckboxStructures, m_CheckboxStructures) + MAP_CONTROL(IDC_CheckboxFlatWorld, m_CheckboxFlatWorld) + MAP_CONTROL(IDC_CheckboxBonusChest, m_CheckboxBonusChest) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_HostOptions, m_HostOptionGroup) + BEGIN_MAP_CHILD_CONTROLS(m_HostOptionGroup) + MAP_CONTROL(IDC_CheckboxOnline, m_CheckboxOnline) + MAP_CONTROL(IDC_CheckboxInviteOnly, m_CheckboxInviteOnly) + MAP_CONTROL(IDC_CheckboxAllowFoF, m_CheckboxAllowFoF) + MAP_CONTROL(IDC_CheckboxPVP, m_CheckboxPVP) + MAP_CONTROL(IDC_CheckboxTrustSystem, m_CheckboxTrustPlayers) + MAP_CONTROL(IDC_CheckboxFireSpreads, m_CheckboxFireSpreads) + MAP_CONTROL(IDC_CheckboxTNT, m_CheckboxTNTExplodes) + MAP_CONTROL(IDC_CheckboxHostPrivileges, m_CheckboxHostPrivileges) + MAP_CONTROL(IDC_CheckboxResetNether, m_CheckboxResetNether) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_Description, m_Description) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifySetFocus( HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_MultiGameLaunchMoreOptions, L"CScene_MultiGameLaunchMoreOptions", XUI_CLASS_SCENE ) + +private: + LaunchMoreOptionsMenuInitData *m_params; + bool m_bMultiplayerAllowed; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.cpp b/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.cpp new file mode 100644 index 0000000..49b524f --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.cpp @@ -0,0 +1,90 @@ + + +#include "stdafx.h" + // #include "XUI_Ctrl_4JIcon.h" +#include "XUI_NewUpdateMessage.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + + +HRESULT CScene_NewUpdateMessage::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *) pInitData->pvInitData; + m_bIsSD=!RenderManager.IsHiDef() && !RenderManager.IsWidescreen(); + + MapChildControls(); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, IDS_TOOLTIPS_BACK ); + + // set the text in the XuiHTMLMessage + wchar_t formatting[40]; + wstring wstrTemp = app.GetString(IDS_TITLEUPDATE); + swprintf(formatting, 40, L"", m_bIsSD?12:14); + wstrTemp = formatting + wstrTemp; + + wstring wsText=app.FormatHTMLString(m_iPad,wstrTemp); + m_HTMLText.SetText(wsText.c_str()); + m_HTMLText.SetShow(TRUE); + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_NewUpdateMessage, 0); + + return S_OK; +} + + + +HRESULT CScene_NewUpdateMessage::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + { + int iVal=app.GetGameSettings(m_iPad,eGameSetting_DisplayUpdateMessage); + if(iVal>0) iVal--; + + // set the update text as seen, by clearing the flag + app.SetGameSettings(m_iPad,eGameSetting_DisplayUpdateMessage,iVal); + // force a profile write + app.CheckGameSettingsChanged(true,m_iPad); + app.NavigateBack(XUSER_INDEX_ANY); + rfHandled = TRUE; + } + + break; + case VK_PAD_RTHUMB_DOWN: + case VK_PAD_LTHUMB_DOWN: + { + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_HTMLText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_HTMLText.m_hObj,1); + } + } + break; + case VK_PAD_RTHUMB_UP: + case VK_PAD_LTHUMB_UP: + { + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_HTMLText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_HTMLText.m_hObj,-1); + } + } + break; } + + return S_OK; +} + +HRESULT CScene_NewUpdateMessage::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK ); + + return S_OK; +} + diff --git a/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.h b/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.h new file mode 100644 index 0000000..8c4fbb3 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_NewUpdateMessage.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../media\xuiscene_NewUpdateMessage.h" + +class CScene_NewUpdateMessage : public CXuiSceneImpl +{ + // Xui Elements + CXuiHtmlControl m_HTMLText; + // Misc + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiHTMLMessage, m_HTMLText) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + +public: + XUI_IMPLEMENT_CLASS( CScene_NewUpdateMessage, L"CScene_NewUpdateMessage", XUI_CLASS_SCENE ) + +private: + int m_iPad; + bool m_bIsSD; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.cpp b/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.cpp new file mode 100644 index 0000000..76b820d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "XUI_PartnernetPassword.h" +#include "..\XUI\XUI_Ctrl_4JList.h" + +#ifdef _CONTENT_PACKAGE +#ifndef _FINAL_BUILD + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_PartnernetPassword::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT); + + m_PartnernetPassword.SetEnable(true); + + m_PartnernetPassword.SetTextLimit(XCONTENT_MAX_DISPLAYNAME_LENGTH); + + // set the caret to the end of the default text + m_PartnernetPassword.SetCaretPosition(0); + m_PartnernetPassword.SetKeyboardType(C_4JInput::EKeyboardMode_Phone); + + m_PartnernetPassword.SetTitleAndText(IDS_NAME_WORLD,IDS_NAME_WORLD_TEXT); + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_PartnernetPassword::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_OK) + { + // create the world and launch + wstring wPassword = m_PartnernetPassword.GetText(); + if(wPassword==L"5183") + { + app.NavigateBack(pNotifyPressData->UserIndex); + app.SetPartnernetPasswordRunning(false); + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1); + } + rfHandled = TRUE; + } + + return S_OK; +} + +HRESULT CScene_PartnernetPassword::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_A: + case VK_PAD_B: + case VK_ESCAPE: + rfHandled = TRUE; + break; + } + return S_OK; +} + +#endif +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.h b/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.h new file mode 100644 index 0000000..b05206c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_PartnernetPassword.h @@ -0,0 +1,40 @@ +#pragma once +#include "..\Media\xuiscene_partnernetpassword.h" +#include "XUI_Ctrl_4JEdit.h" + +#ifdef _CONTENT_PACKAGE +#ifndef _FINAL_BUILD + +class CScene_PartnernetPassword : public CXuiSceneImpl +{ + +protected: + CXuiCtrl4JEdit m_PartnernetPassword; + CXuiControl m_OK; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiEditPartnernetPassword, m_PartnernetPassword) + MAP_CONTROL(IDC_XuiOK, m_OK) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_PartnernetPassword, L"CScene_PartnernetPassword", XUI_CLASS_SCENE ) + +}; + +#endif +#endif diff --git a/Minecraft.Client/Common/XUI/XUI_PauseMenu.cpp b/Minecraft.Client/Common/XUI/XUI_PauseMenu.cpp new file mode 100644 index 0000000..4de74d1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_PauseMenu.cpp @@ -0,0 +1,1219 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\..\Minecraft.Client\StatsCounter.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.Client\MultiplayerLocalPlayer.h" +#include "..\..\MinecraftServer.h" +#include "..\..\MultiPlayerLevel.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" +#include "..\..\..\Minecraft.World\compression.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\DLCTexturePack.h" + +#define IGNORE_KEYPRESS_TIMERID 0 +#define IGNORE_KEYPRESS_TIME 100 + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT UIScene_PauseMenu::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_bIgnoreInput=true; + m_iPad = *(int *)pInitData->pvInitData; + bool bUserisClientSide = ProfileManager.IsSignedInLive(m_iPad); + + app.DebugPrintf("PAUSE PRESS PROCESSING - ipad = %d, UIScene_PauseMenu::OnInit\n",m_iPad); + + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(m_iPad); + + MapChildControls(); + + XuiControlSetText(m_Buttons[BUTTON_PAUSE_RESUMEGAME],app.GetString(IDS_RESUME_GAME)); + XuiControlSetText(m_Buttons[BUTTON_PAUSE_HELPANDOPTIONS],app.GetString(IDS_HELP_AND_OPTIONS)); + XuiControlSetText(m_Buttons[BUTTON_PAUSE_LEADERBOARDS],app.GetString(IDS_LEADERBOARDS)); + XuiControlSetText(m_Buttons[BUTTON_PAUSE_ACHIEVEMENTS],app.GetString(IDS_ACHIEVEMENTS)); + XuiControlSetText(m_Buttons[BUTTON_PAUSE_SAVEGAME],app.GetString(IDS_SAVE_GAME)); + XuiControlSetText(m_Buttons[BUTTON_PAUSE_EXITGAME],app.GetString(IDS_EXIT_GAME)); + + if(app.GetLocalPlayerCount()>1) + { + m_bSplitscreen = true; + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + m_bSplitscreen = false; + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + + // test award the theme + //ProfileManager.Award( ProfileManager.GetPrimaryPad(), eAward_socialPost ); + // Display the tooltips, we are only allowed to display "SHARE" if we have the capability (TCR). + + if(!ProfileManager.IsFullVersion()) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + // hide the trial timer + CXuiSceneBase::ShowTrialTimer(FALSE); + } + else if(StorageManager.GetSaveDisabled()) + { + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide ) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_SELECTDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_SELECTDEVICE:-1,-1,-1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + } + else + { + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,-1, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + } + + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + + // are we the primary player? + // 4J-PB - fix for 7844 & 7845 - + // TCR # 128: XLA Pause Menu: When in a multiplayer game as a client the Pause Menu does not have a Leaderboards option. + // TCR # 128: XLA Pause Menu: When in a multiplayer game as a client the Pause Menu does not have an Achievements option. + if(ProfileManager.GetPrimaryPad()==m_iPad) // && g_NetworkManager.IsHost()) + { + // are we in splitscreen? + // how many local players do we have? + + D3DXVECTOR3 vPos; + if( app.GetLocalPlayerCount()>1 ) + { + m_Buttons[BUTTON_PAUSE_LEADERBOARDS].GetPosition(&vPos); + m_Buttons[BUTTON_PAUSE_SAVEGAME].SetPosition(&vPos); + m_Buttons[BUTTON_PAUSE_ACHIEVEMENTS].GetPosition(&vPos); + m_Buttons[BUTTON_PAUSE_EXITGAME].SetPosition(&vPos); + // Hide the BUTTON_PAUSE_LEADERBOARDS and BUTTON_PAUSE_ACHIEVEMENTS + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_LEADERBOARDS],FALSE); + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_ACHIEVEMENTS],FALSE); + } + + if( !g_NetworkManager.IsHost() ) + { + m_Buttons[BUTTON_PAUSE_SAVEGAME].GetPosition(&vPos); + m_Buttons[BUTTON_PAUSE_EXITGAME].SetPosition(&vPos); + // Hide the BUTTON_PAUSE_SAVEGAME + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_SAVEGAME],FALSE); + } + } + else + { + D3DXVECTOR3 vPos; + m_Buttons[BUTTON_PAUSE_LEADERBOARDS].GetPosition(&vPos); + m_Buttons[BUTTON_PAUSE_EXITGAME].SetPosition(&vPos); + // Hide the BUTTON_PAUSE_LEADERBOARDS, BUTTON_PAUSE_ACHIEVEMENTS and BUTTON_PAUSE_SAVEGAME + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_LEADERBOARDS],FALSE); + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_ACHIEVEMENTS],FALSE); + XuiElementSetShow(m_Buttons[BUTTON_PAUSE_SAVEGAME],FALSE); + } + + // is saving disabled? + if(StorageManager.GetSaveDisabled()) + { + // disable save button + m_Buttons[BUTTON_PAUSE_SAVEGAME].SetEnable(FALSE); + m_Buttons[BUTTON_PAUSE_SAVEGAME].EnableInput(FALSE); + } + + m_iLastButtonPressed=0; + + // get rid of the quadrant display if it's on + CXuiSceneBase::HidePressStart(); + + XuiSetTimer(m_hObj,IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); + + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)TRUE); + } + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_PauseMenu, 0); + TelemetryManager->RecordPauseOrInactive(m_iPad); + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT UIScene_PauseMenu::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + + while((uiButtonCounterUserIndex!=pMinecraft->player->GetXboxPad()) return S_OK; + + // Determine which button was pressed, + // and call the appropriate function. + + // store the last button pressed, so on a nav back we can set the focus properly + m_iLastButtonPressed=uiButtonCounter; + switch(uiButtonCounter) + { + case BUTTON_PAUSE_RESUMEGAME: + if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + app.CloseXuiScenes(pNotifyPressData->UserIndex); + break; + + case BUTTON_PAUSE_LEADERBOARDS: + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + //4J Gordon: Being used for the leaderboards proper now + // guests can't look at leaderboards + if(ProfileManager.IsGuest(pNotifyPressData->UserIndex)) + { + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else if(!ProfileManager.IsSignedInLive(pNotifyPressData->UserIndex)) + { + StorageManager.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + } + else + { + app.NavigateToScene(pNotifyPressData->UserIndex, eUIScene_LeaderboardsMenu); + } + } + break; + case BUTTON_PAUSE_ACHIEVEMENTS: + // guests can't look at achievements + if(ProfileManager.IsGuest(pNotifyPressData->UserIndex)) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + else + { + XShowAchievementsUI( pNotifyPressData->UserIndex ); + } + + break; + case BUTTON_PAUSE_HELPANDOPTIONS: + if(app.GetLocalPlayerCount()>1) + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_HelpAndOptionsMenu); + } + else + { + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_HelpAndOptionsMenu); + } + break; + + case BUTTON_PAUSE_SAVEGAME: + { + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + m_pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + + if(!m_pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + // upsell + ULONGLONG ullOfferID_Full; + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the texture pack + TelemetryManager->RecordUpsellPresented(pNotifyPressData->UserIndex, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + StorageManager.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::WarningTrialTexturePackReturned,this,app.GetStringTable()); + + return S_OK; + } + } + + // does the save exist? + bool bSaveExists; + C4JStorage::ELoadGameStatus result=StorageManager.DoesSaveExist(&bSaveExists); + + if(result == C4JStorage::ELoadGame_DeviceRemoved) + { + // this will be a tester trying to be clever + UINT uiIDA[2]; + uiIDA[0]=IDS_SELECTANEWDEVICE; + uiIDA[1]=IDS_NODEVICE_DECLINE; + + StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::DeviceRemovedDialogReturned,this); + } + else + { + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::SaveGameDialogReturned,this, app.GetStringTable()); + } + else + { + // flag a app action of save game + app.SetAction(pNotifyPressData->UserIndex,eAppAction_SaveGame); + } + } + } + + break; + case BUTTON_PAUSE_EXITGAME: + { + // Check if it's the trial version + if(ProfileManager.IsFullVersion()) + { + UINT uiIDA[3]; + + // is it the primary player exiting? + if(pNotifyPressData->UserIndex==ProfileManager.GetPrimaryPad()) + { + int playTime = -1; + if( pMinecraft->localplayers[pNotifyPressData->UserIndex] != NULL ) + { + playTime = (int)pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer(); + } + + if(StorageManager.GetSaveDisabled()) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + else + { + if( g_NetworkManager.IsHost() ) + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable()); + } + else + { + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameSaveDialogReturned,this, app.GetStringTable()); + } + } + else + { + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + } + } + else + { + int playTime = -1; + if( pMinecraft->localplayers[pNotifyPressData->UserIndex] != NULL ) + { + playTime = (int)pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer(); + } + + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Exited); + + + // just exit the player + app.SetAction(pNotifyPressData->UserIndex,eAppAction_ExitPlayer); + } + } + else + { + // is it the primary player exiting? + if(pNotifyPressData->UserIndex==ProfileManager.GetPrimaryPad()) + { + int playTime = -1; + if( pMinecraft->localplayers[pNotifyPressData->UserIndex] != NULL ) + { + playTime = (int)pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer(); + } + + // adjust the trial time played + CXuiSceneBase::ReduceTrialTimerValue(); + + // exit the level + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_PROGRESS_LOST, uiIDA, 2, pNotifyPressData->UserIndex,&UIScene_PauseMenu::ExitGameDialogReturned,this, app.GetStringTable()); + } + else + { + int playTime = -1; + if( pMinecraft->localplayers[pNotifyPressData->UserIndex] != NULL ) + { + playTime = (int)pMinecraft->localplayers[pNotifyPressData->UserIndex]->getSessionTimer(); + } + + TelemetryManager->RecordLevelExit(pNotifyPressData->UserIndex, eSen_LevelExitStatus_Exited); + + // just exit the player + app.SetAction(pNotifyPressData->UserIndex,eAppAction_ExitPlayer); + } + } + } + break; + default: + break; + } + + return S_OK; +} + +HRESULT UIScene_PauseMenu::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + // ignore repeated start presses to avoid the scene closing before it's opened + if((pInputData->dwKeyCode==VK_PAD_START) &&(pInputData->dwFlags&XUI_INPUT_FLAG_REPEAT )) + { + rfHandled = TRUE; + return S_OK; + } + + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(m_iPad); + bool bUserisClientSide = ProfileManager.IsSignedInLive(m_iPad); + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + app.DebugPrintf("PAUSE- Keydown in the xui %d flags = %d\n",pInputData->dwKeyCode,pInputData->dwFlags); + + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_ESCAPE: + app.DebugPrintf("PAUSE PRESS PROCESSING - ipad = %d, UIScene_PauseMenu::OnKeyDown - LEAVING PAUSE MENU\n",m_iPad); + + if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + + CXuiSceneBase::PlayUISFX(eSFX_Back); + app.CloseXuiScenes(pInputData->UserIndex); + if(!ProfileManager.IsFullVersion()) + { + CXuiSceneBase::ShowTrialTimer(TRUE); + } + rfHandled = TRUE; + + break; + + case VK_PAD_X: + // Change device + if(bIsisPrimaryHost) + { + // we need a function to deal with the return from this - if it changes, we need to update the pause menu and tooltips + // Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage + // device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes. + m_bIgnoreInput=true; + + StorageManager.SetSaveDevice(&UIScene_PauseMenu::DeviceSelectReturned,this,true); + } + rfHandled = TRUE; + break; + + + case VK_PAD_Y: + { + if(bUserisClientSide) + { + // 4J Stu - Added check in 1.8.2 bug fix (TU6) to stop repeat key presses + bool bCanScreenshot = true; + for(int j=0; j < XUSER_MAX_COUNT;++j) + { + if(app.GetXuiAction(j) == eAppAction_SocialPostScreenshot) + { + bCanScreenshot = false; + break; + } + } + if(bCanScreenshot) app.SetAction(pInputData->UserIndex,eAppAction_SocialPost); + } + rfHandled = TRUE; + } + break; + + case VK_PAD_RSHOULDER: + if( bDisplayBanTip ) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + StorageManager.RequestMessageBox(IDS_ACTION_BAN_LEVEL_TITLE, IDS_ACTION_BAN_LEVEL_DESCRIPTION, uiIDA, 2, pInputData->UserIndex,&UIScene_PauseMenu::BanGameDialogReturned,this, app.GetStringTable()); + + rfHandled = TRUE; + } + break; + + // handle a keyboard Return specifically, because we've turned off the VK_A_OR_START for the pause menu, since this should exit the menu, rather than cause the button to be activated + case VK_RETURN: + // call OnNotifyPressEx directly to trigger the button press action + + HXUIOBJ hObjPressed=TreeGetFocus(); + XUINotifyPress NotifyPressData; + BOOL rfHandled=FALSE; + + NotifyPressData.UserIndex=pInputData->UserIndex; + OnNotifyPressEx(hObjPressed,&NotifyPressData,rfHandled); + + break; + } + + return S_OK; +} + +HRESULT UIScene_PauseMenu::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(m_iPad); + bool bUserisClientSide = ProfileManager.IsSignedInLive(m_iPad); + + // Display the tooltips, we are only allowed to display "SHARE" if we have the capability (TCR). + if(StorageManager.GetSaveDisabled()) + { + if( ProfileManager.IsFullVersion() && CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_SELECTDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,-1,-1, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + } + else + { + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,-1, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + } + + // set the focus to the last button we were on + XuiElementSetUserFocus(m_Buttons[m_iLastButtonPressed],m_iPad); + + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + + bool isWrongSize = false; + if(app.GetLocalPlayerCount()==1) + { + // If we were created as a splitscreen scene, then it's now going to be in the wrong place. Get rid of this scene. + if(m_bSplitscreen) + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + isWrongSize = true; + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + if(!m_bSplitscreen) isWrongSize = true; + } + + if(isWrongSize) + { + if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + + app.CloseXuiScenes(m_iPad); + if(!ProfileManager.IsFullVersion()) + { + CXuiSceneBase::ShowTrialTimer(TRUE); + } + } + + return S_OK; +} + +HRESULT UIScene_PauseMenu::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +int UIScene_PauseMenu::BanGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // 4J Stu - Only do this if we are currently idle, don't want the (relatively) low priority ban task overriding something else + if(app.GetXuiAction(iPad) == eAppAction_Idle) app.SetAction(iPad,eAppAction_BanLevel); + } + return 0; +} + +int UIScene_PauseMenu::DeviceSelectReturned(void *pParam,bool bContinue) +{ + // Has someone pulled the ethernet cable and caused the pause menu to be closed before this callback returns? + if(!app.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + return 0; + } + + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==pClass->m_iPad); + bool bDisplayBanTip = !g_NetworkManager.IsLocalGame() && !bIsisPrimaryHost && !ProfileManager.IsGuest(pClass->m_iPad); + bool bUserisClientSide = ProfileManager.IsSignedInLive(pClass->m_iPad); + + //Whatever happen, we need to update the pause menu and the tooltips + // Display the tooltips, we are only allowed to display "SHARE" if we have the capability (TCR). + if(StorageManager.GetSaveDisabled()) + { + if( ProfileManager.IsFullVersion() && CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { + ui.SetTooltips( pClass->m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_SELECTDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + else + { + ui.SetTooltips( pClass->m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,-1,-1, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + // disable save button + // set the focus on to another button + pClass->m_Buttons[BUTTON_PAUSE_SAVEGAME].SetEnable(FALSE); + pClass->m_Buttons[BUTTON_PAUSE_SAVEGAME].EnableInput(FALSE); + + pClass->m_Buttons[BUTTON_PAUSE_RESUMEGAME].InitFocus(pClass->m_iPad); + } + else + { + if( CSocialManager::Instance()->IsTitleAllowedToPostImages() && CSocialManager::Instance()->AreAllUsersAllowedToPostImages() && bUserisClientSide) + { + ui.SetTooltips(pClass->m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,IDS_TOOLTIPS_SHARE, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + else + { + ui.SetTooltips( pClass->m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,bIsisPrimaryHost?IDS_TOOLTIPS_CHANGEDEVICE:-1,-1, -1,-1,-1,bDisplayBanTip?IDS_TOOLTIPS_BANLEVEL:-1); + } + // enable save button + pClass->m_Buttons[BUTTON_PAUSE_SAVEGAME].SetEnable(TRUE); + pClass->m_Buttons[BUTTON_PAUSE_SAVEGAME].EnableInput(TRUE); + + } + + pClass->m_bIgnoreInput=false; + return 0; +} + +HRESULT UIScene_PauseMenu::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining,false); +} + +HRESULT UIScene_PauseMenu::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==IGNORE_KEYPRESS_TIMERID) + { + XuiKillTimer(m_hObj,IGNORE_KEYPRESS_TIMERID); + + // block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again + if(app.StartInstallDLCProcess(m_iPad)==true) + { + // not doing a mount, so enable input + m_bIgnoreInput=true; + } + else + { + m_bIgnoreInput=false; + } + } + + return S_OK; +} + + +HRESULT UIScene_PauseMenu::OnDestroy() +{ + //XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + TelemetryManager->RecordUnpauseOrActive(m_iPad); + + if( m_iPad == ProfileManager.GetPrimaryPad() && g_NetworkManager.IsLocalGame() ) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_PauseServer,(void *)FALSE); + } + + return S_OK; +} + +int UIScene_PauseMenu::DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // continue without saving + StorageManager.SetSaveDisabled(true); + StorageManager.SetSaveDeviceSelected(ProfileManager.GetPrimaryPad(),false); + + // Has someone pulled the ethernet cable and caused the pause menu to be closed before this callback returns? + if(app.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + + // use the device select returned function to wipe the saves list and change the tooltip + pClass->DeviceSelectReturned(pClass,true); + } + } + else + { + // Change device + if(app.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + StorageManager.SetSaveDevice(&UIScene_PauseMenu::DeviceSelectReturned,pClass,true); + } + } + return 0; +} + +HRESULT UIScene_PauseMenu::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + } + // this will send a CustomMessage_DLCMountingComplete when done + return S_OK; +} + +HRESULT UIScene_PauseMenu::OnCustomMessage_DLCMountingComplete() +{ + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + return S_OK; +} + +void UIScene_PauseMenu::ShowScene(bool show) +{ + SetShow(show?TRUE:FALSE); +} + +int UIScene_PauseMenu::WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu* pScene = (UIScene_PauseMenu*)pParam; + + //pScene->m_bIgnoreInput = false; + pScene->ShowScene( true ); + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + ULONGLONG ullIndexA[1]; + + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + // Need to get the parent packs id, since this may be one of many child packs with their own ids + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullIndexA[0]); + + // need to allow downloads here, or the player would need to quit the game to let the download of a texture pack happen. This might affect the network traffic, since the download could take all the bandwidth... + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Texture_DLC, ( pScene->m_pDLCPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } + + + return 0; +} + +int UIScene_PauseMenu::ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + UIScene_PauseMenu *pClass = (UIScene_PauseMenu *)pParam; + // Exit with or without saving + // Decline means save in this dialog + if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) + { + if( result==C4JStorage::EMessage_ResultDecline ) // Save + { + // 4J-PB - Is the player trying to save but they are using a trial texturepack ? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack();//tPack->getDLCPack(); + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { +#ifdef _XBOX + // upsell + ULONGLONG ullOfferID_Full; + // get the dlc texture pack + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + app.GetDLCFullOfferIDForPackID(pDLCTexPack->getDLCParentPackId(),&ullOfferID_Full); + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); +#endif + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + // Give the player a warning about the trial version of the texture pack + ui.RequestMessageBox(IDS_WARNING_DLC_TRIALTEXTUREPACK_TITLE, IDS_WARNING_DLC_TRIALTEXTUREPACK_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad() ,&UIScene_PauseMenu::WarningTrialTexturePackReturned,pClass,app.GetStringTable()); + + return S_OK; + } + } + + // does the save exist? + bool bSaveExists; + StorageManager.DoesSaveExist(&bSaveExists); + // 4J-PB - we check if the save exists inside the libs + // we need to ask if they are sure they want to overwrite the existing game + if(bSaveExists) + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_SAVE_GAME, IDS_CONFIRM_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameAndSaveReturned,pClass, app.GetStringTable()); + return 0; + } + else + { + MinecraftServer::getInstance()->setSaveOnExit( true ); + } + } + else + { + // been a few requests for a confirm on exit without saving + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_TITLE_DECLINE_SAVE_GAME, IDS_CONFIRM_DECLINE_SAVE_GAME, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameDeclineSaveReturned, dynamic_cast(pClass), app.GetStringTable()); + return 0; + } + + app.SetAction(iPad,eAppAction_ExitWorld); + } + return 0; +} + +int UIScene_PauseMenu::ExitGameDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + MinecraftServer::getInstance()->setSaveOnExit( false ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + else + { + // has someone disconnected the ethernet here, causing the pause menu to shut? + if(ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + IUIScene_PauseMenu* pClass = (IUIScene_PauseMenu*)pParam; + UINT uiIDA[3]; + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameSaveDialogReturned,pClass, app.GetStringTable(), 0, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameSaveDialogReturned,pClass, app.GetStringTable(), 0, 0, false); + } + } + + } + return 0; +} + +int UIScene_PauseMenu::ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // 4J-PB - we won't come in here if we have a trial texture pack + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + MinecraftServer::getInstance()->setSaveOnExit( true ); + // flag a app action of exit game + app.SetAction(iPad,eAppAction_ExitWorld); + } + else + { + // has someone disconnected the ethernet here, causing the pause menu to shut? + if(ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + UIScene_PauseMenu* pClass = (UIScene_PauseMenu*)pParam; + UINT uiIDA[3]; + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_EXIT_GAME_SAVE; + uiIDA[2]=IDS_EXIT_GAME_NO_SAVE; + + if(g_NetworkManager.GetPlayerCount()>1) + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME_CONFIRM_DISCONNECT_SAVE, uiIDA, 3, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameSaveDialogReturned,pClass, app.GetStringTable(), 0, 0, false); + } + else + { + ui.RequestMessageBox(IDS_EXIT_GAME, IDS_CONFIRM_EXIT_GAME, uiIDA, 3, ProfileManager.GetPrimaryPad(),&UIScene_PauseMenu::ExitGameSaveDialogReturned,pClass, app.GetStringTable(), 0, 0, false); + } + } + + } + return 0; +} + +int UIScene_PauseMenu::SaveGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + // flag a app action of save game + app.SetAction(iPad,eAppAction_SaveGame); + } + return 0; +} + +int UIScene_PauseMenu::ExitGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + app.SetAction(iPad,eAppAction_ExitWorld); + } + return 0; +} + +int UIScene_PauseMenu::SaveWorldThreadProc( LPVOID lpParameter ) +{ + bool bAutosave=(bool)lpParameter; + if(bAutosave) + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_AutoSaveGame); + } + else + { + app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_SaveGame); + } + + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + //wprintf(L"Loading world on thread\n"); + + if(ProfileManager.IsFullVersion()) + { + app.SetGameStarted(false); + + while( app.GetXuiServerAction(ProfileManager.GetPrimaryPad() ) != eXuiServerAction_Idle && !MinecraftServer::serverHalted() ) + { + Sleep(10); + } + + if(!MinecraftServer::serverHalted() && !app.GetChangingSessionType() ) app.SetGameStarted(true); + } + + HRESULT hr = S_OK; + if(app.GetChangingSessionType()) + { + // 4J Stu - This causes the fullscreenprogress scene to ignore the action it was given + hr = ERROR_CANCELLED; + } + return hr; +} + +int UIScene_PauseMenu::ExitWorldThreadProc( void* lpParameter ) +{ + // Share AABB & Vec3 pools with default (main thread) - should be ok as long as we don't tick the main thread whilst this thread is running + AABB::UseDefaultThreadStorage(); + Vec3::UseDefaultThreadStorage(); + Compression::UseDefaultThreadStorage(); + + //app.SetGameStarted(false); + + _ExitWorld(lpParameter); + + return S_OK; +} + +// This function performs the meat of exiting from a level. It should be called from a thread other than the main thread. +void UIScene_PauseMenu::_ExitWorld(LPVOID lpParameter) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + int exitReasonStringId = pMinecraft->progressRenderer->getCurrentTitle(); + int exitReasonTitleId = IDS_CONNECTION_LOST; + + bool saveStats = true; + if (pMinecraft->isClientSide() || g_NetworkManager.IsInSession()) + { + if(lpParameter != NULL ) + { + // 4J-PB - check if we have lost connection to Live + if(ProfileManager.GetLiveConnectionStatus()!=XONLINE_S_LOGON_CONNECTION_ESTABLISHED ) + { + exitReasonStringId = IDS_CONNECTION_LOST_LIVE; + } + else + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + case DisconnectPacket::eDisconnect_NoFlying: + exitReasonStringId = IDS_DISCONNECTED_FLYING; + break; + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; + case DisconnectPacket::eDisconnect_NoFriendsInGame: + exitReasonStringId = IDS_DISCONNECTED_NO_FRIENDS_IN_GAME; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_Banned: + exitReasonStringId = IDS_DISCONNECTED_BANNED; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_NotFriendsWithHost: + exitReasonStringId = IDS_NOTALLOWED_FRIENDSOFFRIENDS; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + default: + exitReasonStringId = IDS_CONNECTION_LOST_SERVER; + } + } + //pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + // 4J Stu - Fix for #48669 - TU5: Code: Compliance: TCR #15: Incorrect/misleading messages after signing out a profile during online game session. + // If the primary player is signed out, then that is most likely the cause of the disconnection so don't display a message box. This will allow the message box requested by the libraries to be brought up + if( ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) ui.RequestMessageBox( exitReasonTitleId, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + + // 4J - Force a disconnection, this handles the situation that the server has already disconnected + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(false); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(false); + if( pMinecraft->levels[2] != NULL ) pMinecraft->levels[2]->disconnect(false); + } + else + { + exitReasonStringId = IDS_EXITING_GAME; + pMinecraft->progressRenderer->progressStartNoAbort( IDS_EXITING_GAME ); + if( pMinecraft->levels[0] != NULL ) pMinecraft->levels[0]->disconnect(); + if( pMinecraft->levels[1] != NULL ) pMinecraft->levels[1]->disconnect(); + if( pMinecraft->levels[2] != NULL ) pMinecraft->levels[2]->disconnect(); + } + + // 4J Stu - This only does something if we actually have a server, so don't need to do any other checks + MinecraftServer::HaltServer(); + + // We need to call the stats & leaderboards save before we exit the session + // 4J We need to do this in a QNet callback where it is safe + //pMinecraft->forceStatsSave(); + saveStats = false; + + // 4J Stu - Leave the session once the disconnect packet has been sent + g_NetworkManager.LeaveGame(FALSE); + } + else + { + if(lpParameter != NULL && ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad()) ) + { + switch( app.GetDisconnectReason() ) + { + case DisconnectPacket::eDisconnect_Kicked: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_ALL_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_SINGLE_LOCAL; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#ifdef _XBOX + case DisconnectPacket::eDisconnect_NoUGC_Remote: + exitReasonStringId = IDS_NO_USER_CREATED_CONTENT_PRIVILEGE_REMOTE; + exitReasonTitleId = IDS_CONNECTION_FAILED; + break; +#endif + case DisconnectPacket::eDisconnect_Quitting: + exitReasonStringId = IDS_DISCONNECTED_SERVER_QUIT; + break; + case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin: + exitReasonStringId = IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT; + break; + case DisconnectPacket::eDisconnect_OutdatedServer: + exitReasonStringId = IDS_DISCONNECTED_SERVER_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_OutdatedClient: + exitReasonStringId = IDS_DISCONNECTED_CLIENT_OLD; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + exitReasonTitleId = IDS_CANTJOIN_TITLE; + break; + default: + exitReasonStringId = IDS_DISCONNECTED; + } + //pMinecraft->progressRenderer->progressStartNoAbort( exitReasonStringId ); + + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( exitReasonTitleId, exitReasonStringId, uiIDA,1,ProfileManager.GetPrimaryPad(),NULL,NULL, app.GetStringTable()); + exitReasonStringId = -1; + } + } + // Fix for #93148 - TCR 001: BAS Game Stability: Title will crash for the multiplayer client if host of the game will exit during the clients loading to created world. + while( g_NetworkManager.IsNetworkThreadRunning() ) + { + Sleep(1); + } + pMinecraft->setLevel(NULL,exitReasonStringId,nullptr,saveStats); + + TelemetryManager->Flush(); + + app.m_gameRules.unloadCurrentGameRules(); + //app.m_Audio.unloadCurrentAudioDetails(); + + MinecraftServer::resetFlags(); + + // Fix for #48385 - BLACK OPS :TU5: Functional: Client becomes pseudo soft-locked when returned to the main menu after a remote disconnect + // Make sure there is text explaining why the player is waiting + pMinecraft->progressRenderer->progressStart(IDS_EXITING_GAME); + + // Fix for #13259 - CRASH: Gameplay: loading process is halted when player loads saved data + // We can't start/join a new game until the session is destroyed, so wait for it to be idle again + while( g_NetworkManager.IsInSession() ) + { + Sleep(1); + } + + app.SetChangingSessionType(false); + app.SetReallyChangingSessionType(false); +} + +void UIScene_PauseMenu::SetIgnoreInput(bool ignoreInput) +{ + m_bIgnoreInput = ignoreInput; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_PauseMenu.h b/Minecraft.Client/Common/XUI/XUI_PauseMenu.h new file mode 100644 index 0000000..9c0e001 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_PauseMenu.h @@ -0,0 +1,89 @@ +#pragma once +#include "../media/xuiscene_Pause.h" +#include "..\UI\IUIScene_PauseMenu.h" +#include "XUI_CustomMessages.h" + +#define BUTTON_PAUSE_RESUMEGAME 0 +#define BUTTON_PAUSE_HELPANDOPTIONS 1 +#define BUTTON_PAUSE_LEADERBOARDS 2 +#define BUTTON_PAUSE_ACHIEVEMENTS 3 +#define BUTTON_PAUSE_SAVEGAME 4 +#define BUTTON_PAUSE_EXITGAME 5 +#define BUTTONS_PAUSE_MAX BUTTON_PAUSE_EXITGAME + 1 + + + +class UIScene_PauseMenu : public CXuiSceneImpl, public IUIScene_PauseMenu +{ + protected: + // Control and Element wrapper objects. + CXuiScene m_Scene; + CXuiControl m_Buttons[BUTTONS_PAUSE_MAX]; + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiButton1, m_Buttons[BUTTON_PAUSE_RESUMEGAME]) + MAP_CONTROL(IDC_XuiButton2, m_Buttons[BUTTON_PAUSE_HELPANDOPTIONS ]) + MAP_CONTROL(IDC_XuiButton3, m_Buttons[BUTTON_PAUSE_LEADERBOARDS ]) + MAP_CONTROL(IDC_XuiButton4, m_Buttons[BUTTON_PAUSE_ACHIEVEMENTS]) + MAP_CONTROL(IDC_XuiButton5, m_Buttons[BUTTON_PAUSE_SAVEGAME]) + MAP_CONTROL(IDC_XuiButton6, m_Buttons[BUTTON_PAUSE_EXITGAME]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + HRESULT OnDestroy(); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( UIScene_PauseMenu, L"CScene_Pause", XUI_CLASS_SCENE ) + + static int BanGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeviceSelectReturned(void *pParam,bool bContinue); + static int DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int WarningTrialTexturePackReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameDeclineSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameAndSaveReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int ExitGameDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + static int SaveWorldThreadProc( void* lpParameter ); + static int ExitWorldThreadProc( void* lpParameter ); + static void _ExitWorld(LPVOID lpParameter); // Call only from a thread + + +protected: + virtual void ShowScene(bool show); + virtual void SetIgnoreInput(bool ignoreInput); + +private: + int m_iPad; + int m_iLastButtonPressed; + D3DXVECTOR3 m_OriginalPosition; + bool m_bIgnoreInput; + bool m_bSplitscreen; + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Reinstall.cpp b/Minecraft.Client/Common/XUI/XUI_Reinstall.cpp new file mode 100644 index 0000000..c1e06c0 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Reinstall.cpp @@ -0,0 +1,339 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\XUI\XUI_Reinstall.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\..\Minecraft.Client\StatsCounter.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.Client\LocalPlayer.h" +#include "..\..\MinecraftServer.h" +#include "..\..\ProgressRenderer.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" +#include "..\..\Minecraft.h" +#include "..\..\Options.h" + + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Reinstall::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + // We'll only be in this menu from the main menu, not in game + m_iPad = *(int *)pInitData->pvInitData; + + m_bIgnoreInput=false; + MapChildControls(); + + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_THEME],app.GetString(IDS_REINSTALL_THEME)); + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_GAMERPIC1],app.GetString(IDS_REINSTALL_GAMERPIC_1)); + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_GAMERPIC2],app.GetString(IDS_REINSTALL_GAMERPIC_2)); + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_AVATAR1],app.GetString(IDS_REINSTALL_AVATAR_ITEM_1)); + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_AVATAR2],app.GetString(IDS_REINSTALL_AVATAR_ITEM_2)); + XuiControlSetText(m_Buttons[BUTTON_REINSTALL_AVATAR3],app.GetString(IDS_REINSTALL_AVATAR_ITEM_3)); + + int iFirstEnabled=-1; + + // we can only come in here if we are the primary player, it's the full version, and we have some content to re-install + + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_mine100Blocks) ) + { + m_Buttons[BUTTON_REINSTALL_GAMERPIC1].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_GAMERPIC1].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_GAMERPIC1; + } + else + { + m_Buttons[BUTTON_REINSTALL_GAMERPIC1].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_GAMERPIC1].EnableInput(FALSE); + } + + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_kill10Creepers) ) + { + m_Buttons[BUTTON_REINSTALL_GAMERPIC2].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_GAMERPIC2].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_GAMERPIC2; + } + else + { + m_Buttons[BUTTON_REINSTALL_GAMERPIC2].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_GAMERPIC2].EnableInput(FALSE); + } + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_eatPorkChop) ) + { + m_Buttons[BUTTON_REINSTALL_AVATAR1].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_AVATAR1].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_AVATAR1; + } + else + { + m_Buttons[BUTTON_REINSTALL_AVATAR1].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_AVATAR1].EnableInput(FALSE); + } + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_play100Days) ) + { + m_Buttons[BUTTON_REINSTALL_AVATAR2].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_AVATAR2].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_AVATAR2; + } + else + { + m_Buttons[BUTTON_REINSTALL_AVATAR2].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_AVATAR2].EnableInput(FALSE); + } + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_arrowKillCreeper) ) + { + m_Buttons[BUTTON_REINSTALL_AVATAR3].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_AVATAR3].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_AVATAR3; + } + else + { + m_Buttons[BUTTON_REINSTALL_AVATAR3].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_AVATAR3].EnableInput(FALSE); + } + if(ProfileManager.IsAwardsFlagSet(m_iPad,eAward_socialPost) ) + { + m_Buttons[BUTTON_REINSTALL_THEME].SetEnable(TRUE); + m_Buttons[BUTTON_REINSTALL_THEME].EnableInput(TRUE); + if(iFirstEnabled==-1) iFirstEnabled=BUTTON_REINSTALL_THEME; + } + else + { + m_Buttons[BUTTON_REINSTALL_THEME].SetEnable(FALSE); + m_Buttons[BUTTON_REINSTALL_THEME].EnableInput(FALSE); + } + + // if iFirstEnabled is still -1 then we shouldn't set focus to a button + if(iFirstEnabled!=-1) + { + m_Buttons[iFirstEnabled].InitFocus(m_iPad); + } + + // allow the user to change the storage device + if(!StorageManager.GetSaveDeviceSelected(m_iPad)) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE); + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Reinstall::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + + while((uiButtonCounterUserIndex, eAward_socialPost, true ); + break; + case BUTTON_REINSTALL_GAMERPIC1: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_mine100Blocks, true); + break; + case BUTTON_REINSTALL_GAMERPIC2: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_kill10Creepers, true); + break; + case BUTTON_REINSTALL_AVATAR1: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_eatPorkChop, true ); + break; + case BUTTON_REINSTALL_AVATAR2: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_play100Days, true ); + break; + case BUTTON_REINSTALL_AVATAR3: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_arrowKillCreeper, true ); + break; + } + + return S_OK; +} + +HRESULT CScene_Reinstall::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_PAD_START: + case VK_ESCAPE: + app.NavigateBack(m_iPad); + rfHandled = TRUE; + + break; + case VK_PAD_X: + // Change device + // we need a function to deal with the return from this - if it changes, we need to update the tooltips + // Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage + // device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes. + m_bIgnoreInput=true; + StorageManager.SetSaveDevice(&CScene_Reinstall::DeviceSelectReturned,this,true); + + rfHandled = TRUE; + break; + } + + return S_OK; +} + +HRESULT CScene_Reinstall::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + + return S_OK; +} + +HRESULT CScene_Reinstall::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_Reinstall::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-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + XUIRect xuiRect; + HXUIOBJ visual=NULL; + HXUIOBJ text; + float fMaxTextLen=0.0f; + float fTextVisualLen; + float fMaxButton; + float fWidth,fHeight; + + HRESULT hr=XuiControlGetVisual(m_Buttons[0].m_hObj,&visual); + hr=XuiElementGetChildById(visual,L"text_Label",&text); + hr=XuiElementGetBounds(text,&fTextVisualLen,&fHeight); + m_Buttons[0].GetBounds(&fMaxButton,&fHeight); + + + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + } + + if(fTextVisualLenm_iPad)) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE); + } + else + { + ui.SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE); + } + + pClass->m_bIgnoreInput=false; + return 0; +} + +int CScene_Reinstall::DeviceSelectReturned_AndReinstall(void *pParam,bool bContinue) +{ + CScene_Reinstall* pClass = (CScene_Reinstall*)pParam; + + if(!StorageManager.GetSaveDeviceSelected(pClass->m_iPad)) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE); + // reinstall + switch(pClass->m_iLastButtonPressed) + { + case BUTTON_REINSTALL_THEME: + ProfileManager.Award( pClass->m_iPad, eAward_socialPost, true ); + break; + case BUTTON_REINSTALL_GAMERPIC1: + ProfileManager.Award( pClass->m_iPad, eAward_mine100Blocks, true); + break; + case BUTTON_REINSTALL_GAMERPIC2: + ProfileManager.Award( pClass->m_iPad, eAward_kill10Creepers, true); + break; + case BUTTON_REINSTALL_AVATAR1: + ProfileManager.Award( pClass->m_iPad, eAward_eatPorkChop, true ); + break; + case BUTTON_REINSTALL_AVATAR2: + ProfileManager.Award( pClass->m_iPad, eAward_play100Days, true ); + break; + case BUTTON_REINSTALL_AVATAR3: + ProfileManager.Award( pClass->m_iPad, eAward_arrowKillCreeper, true ); + break; + } + } + pClass->m_bIgnoreInput=false; + return 0; +} + + diff --git a/Minecraft.Client/Common/XUI/XUI_Reinstall.h b/Minecraft.Client/Common/XUI/XUI_Reinstall.h new file mode 100644 index 0000000..06b823c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Reinstall.h @@ -0,0 +1,63 @@ +#pragma once + +#include "../media/xuiscene_Reinstall.h" + +#define BUTTON_REINSTALL_THEME 0 +#define BUTTON_REINSTALL_GAMERPIC1 1 +#define BUTTON_REINSTALL_GAMERPIC2 2 +#define BUTTON_REINSTALL_AVATAR1 3 +#define BUTTON_REINSTALL_AVATAR2 4 +#define BUTTON_REINSTALL_AVATAR3 5 +#define BUTTONS_REINSTALL_MAX BUTTON_REINSTALL_AVATAR3 + 1 + + + +class CScene_Reinstall : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiControl m_Buttons[BUTTONS_REINSTALL_MAX]; + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiButton1, m_Buttons[BUTTON_REINSTALL_THEME ]) + MAP_CONTROL(IDC_XuiButton2, m_Buttons[BUTTON_REINSTALL_GAMERPIC1 ]) + MAP_CONTROL(IDC_XuiButton3, m_Buttons[BUTTON_REINSTALL_GAMERPIC2 ]) + MAP_CONTROL(IDC_XuiButton4, m_Buttons[BUTTON_REINSTALL_AVATAR1 ]) + MAP_CONTROL(IDC_XuiButton5, m_Buttons[BUTTON_REINSTALL_AVATAR2 ]) + MAP_CONTROL(IDC_XuiButton6, m_Buttons[BUTTON_REINSTALL_AVATAR3 ]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + + static int DeviceSelectReturned(void *pParam,bool bContinue); + static int DeviceSelectReturned_AndReinstall(void *pParam,bool bContinue); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Reinstall, L"CScene_Reinstall", XUI_CLASS_SCENE ) + + //static int InstallReturned(void *pParam,bool bContinue); + +private: + int m_iPad; + int m_iLastButtonPressed; + bool m_bIgnoreInput; + + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SaveMessage.cpp b/Minecraft.Client/Common/XUI/XUI_SaveMessage.cpp new file mode 100644 index 0000000..7c81603 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SaveMessage.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include +#include "..\XUI\XUI_SaveMessage.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SaveMessage::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + + m_button.SetText(app.GetString(IDS_CONFIRM_OK)); + + m_SaveMessage.SetText(app.GetString(IDS_SAVE_ICON_MESSAGE)); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT ); + + // 4J-PB - If we have a signed in user connected, let's get the DLC now + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if( (InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i)) ) + { + if(!app.DLCInstallProcessCompleted() && !app.DLCInstallPending()) + { + app.StartInstallDLCProcess(i); + break; + } + } + } + + // set a timer on the saving message screen, so we continue after 8 seconds + XuiSetTimer( m_hObj,0,8000); + m_bIgnoreInput=false; + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SaveMessage::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + XuiKillTimer(m_hObj,0); + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_MainMenu); + rfHandled = TRUE; + return S_OK; +} + +HRESULT CScene_SaveMessage::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + return S_OK; +} + +// 4J-PB - added for Compliance fail - +// Games must enter an interactive state that accepts player input within 20 seconds after the initial start-up sequence. +// If an animation or cinematic shown during the start-up sequence runs longer than 20 seconds, it must be skippable using the START button or natural input. +HRESULT CScene_SaveMessage::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + m_bIgnoreInput=true; + XuiKillTimer(m_hObj,0); + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_MainMenu); + + return S_OK; +} + + diff --git a/Minecraft.Client/Common/XUI/XUI_SaveMessage.h b/Minecraft.Client/Common/XUI/XUI_SaveMessage.h new file mode 100644 index 0000000..3e84541 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SaveMessage.h @@ -0,0 +1,36 @@ +#pragma once + +#include "../media/xuiscene_savemessage.h" + +class CScene_SaveMessage : public CXuiSceneImpl +{ + protected: + CXuiControl m_button; + CXuiControl m_SaveMessage; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_ConfirmButton, m_button) + MAP_CONTROL(IDC_Description, m_SaveMessage) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled); + + bool m_bIgnoreInput; +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SaveMessage, L"CScene_SaveMessage", XUI_CLASS_SCENE ) + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.cpp new file mode 100644 index 0000000..10369d5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.cpp @@ -0,0 +1,475 @@ +#include "stdafx.h" +#include +#include +#include + +#include "..\..\..\Minecraft.World\Player.h" +#include "..\..\..\Minecraft.Client\LocalPlayer.h" +#include "..\..\..\Minecraft.Client\Minecraft.h" +#include "..\..\..\Minecraft.Client\GameMode.h" +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\Tile.h" +#include "..\..\..\Minecraft.World\FurnaceRecipes.h" +#include "..\..\..\Minecraft.World\Recipy.h" +#include "..\..\..\Minecraft.World\Recipes.h" +#include "..\..\..\Minecraft.World\ArmorRecipes.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\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; + } +} + +shared_ptr 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 wstring &description, vector &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()); + wstring desc = L"(smallPointer ? 12 :14) + L"\">" + description + L""; + + 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); + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.h b/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.h new file mode 100644 index 0000000..ca191db --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_AbstractContainer.h @@ -0,0 +1,83 @@ +#pragma once +#include "..\UI\IUIScene_AbstractContainerMenu.h" +#include "XUI_CustomMessages.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" + +class CXuiCtrlSlotList; +class CXuiCtrlSlotItem; +class AbstractContainerMenu; +class Slot; + +class CXuiSceneAbstractContainer : public CXuiSceneImpl, public virtual IUIScene_AbstractContainerMenu +{ +public: + BOOL isPauseScreen() { + return FALSE; + }; + + D3DXVECTOR3 GetCursorScreenPosition(); + +protected: + virtual void PlatformInitialize(int iPad, int startIndex); + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + + CXuiCtrlSlotList* m_inventoryControl; + CXuiCtrlSlotList* m_useRowControl; + CXuiCtrlSlotItem* m_pointerControl; + CXuiControl m_InventoryText; + + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + // Timer function to poll stick input and update pointer position. + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + +protected: + // 4J JEV - Wanted to override onClick method in XUI_Scene_Inventory_Creative, + // so am making this protected. + + +protected: + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + bool m_bIgnoreKeyPresses; + +private: + int mapVKToAction(int vk); + +protected: + virtual HRESULT handleCustomTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + +public: + int getPad() { return m_iPad; } + +#ifdef USE_POINTER_ACCEL + float m_fPointerVelX; + float m_fPointerVelY; + + float m_fPointerAccelX; + float m_fPointerAccelY; +#endif + + + HXUIOBJ m_hPointerTextMeasurer; + HXUIOBJ m_hPointerText; + HXUIOBJ m_hPointerTextBkg; + HXUIOBJ m_hPointerImg; + + virtual int getSectionColumns(ESceneSection eSection); + virtual int getSectionRows(ESceneSection eSection); + virtual CXuiControl* GetSectionControl( ESceneSection eSection ) = 0; + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ) = 0; + virtual void GetPositionOfSection( ESceneSection eSection, UIVec2D* pPosition ); + virtual void GetItemScreenData( ESceneSection eSection, int iItemIndex, UIVec2D* pPosition, UIVec2D* pSize ); + void handleSectionClick(ESceneSection eSection); + virtual bool doesSectionTreeHaveFocus(ESceneSection eSection); + virtual void setSectionFocus(ESceneSection eSection, int iPad); + void setSectionSelectedSlot(ESceneSection eSection, int x, int y); + void setFocusToPointer(int iPad); + void SetPointerText(const wstring &description, vector &unformattedStrings, bool newSlot); + virtual shared_ptr getSlotItem(ESceneSection eSection, int iSlot); + virtual bool isSlotEmpty(ESceneSection eSection, int iSlot); + virtual void adjustPointerForSafeZone(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.cpp new file mode 100644 index 0000000..4a8617b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.cpp @@ -0,0 +1,242 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_Anvil.h" + + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneAnvil::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneAnvil::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_anvilText,app.GetString(IDS_REPAIR_AND_NAME)); + + m_cross.SetShow(FALSE); + m_editName.SetText(L""); + m_editName.SetTextLimit(30); + m_editName.SetTitleAndText(IDS_TITLE_RENAME,IDS_TITLE_RENAME); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + AnvilScreenInput* initData = (AnvilScreenInput*)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + m_inventory = initData->inventory; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Anvil_Menu, this); + } + + m_repairMenu = new RepairMenu( initData->inventory, initData->level, initData->x, initData->y, initData->z, pMinecraft->localplayers[m_iPad] ); + m_repairMenu->addSlotListener(this); + + InitDataAssociations(m_iPad, m_repairMenu); + + delete initData; + + CXuiSceneAbstractContainer::Initialize( m_iPad, m_repairMenu, true, RepairMenu::INV_SLOT_START, eSectionAnvilUsing, eSectionAnvilMax ); + + //ProfileManager.SetRichPresenceContextValue(m_iPad,CONTEXT_GAME_STATE,CONTEXT_GAME_STATE_FORGING); + + XuiSetTimer(m_hObj,ANVIL_UPDATE_TIMER_ID,ANVIL_UPDATE_TIMER_TIME); + + return S_OK; +} + +HRESULT CXuiSceneAnvil::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +HRESULT CXuiSceneAnvil::OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled) +{ + if(hObjSource == m_editName) + { + wstring newValue = m_editName.GetText(); + LPCWSTR szText=newValue.c_str(); + stripWhitespaceForHtml(newValue); + + // strip leading spaces + wstring b; + int start = (int)newValue.find_first_not_of(L" "); + int end = (int)newValue.find_last_not_of(L" "); + + if( start == wstring::npos ) + { + // the string is all space + newValue=L""; + } + else + { + if( end == wstring::npos ) end = (int)newValue.size()-1; + b = newValue.substr(start,(end-start)+1); + newValue=b; + } + + // 4J-PB - This was stopping the player deleting the last character in the name using the chatpad + //if(!newValue.empty() && !(newValue.size() == 1 && newValue.at(0) == L' ')) + { + newValue = escapeXML(newValue); + m_itemName = newValue; + updateItemName(); + } +// else +// { +// LPCWSTR szText=m_itemName.c_str(); +// m_editName.SetText(szText); +// m_editName.SetCaretPosition(m_itemName.length()); +// } + } + + return S_OK; +} + +HRESULT CXuiSceneAnvil::handleCustomTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + if(pTimer->nId == ANVIL_UPDATE_TIMER_ID) + { + handleTick(); + bHandled = TRUE; + } + return S_OK; +} + +CXuiControl* CXuiSceneAnvil::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionAnvilItem1: + return (CXuiControl *)m_ingredient1Control; + break; + case eSectionAnvilItem2: + return (CXuiControl *)m_ingredient2Control; + break; + case eSectionAnvilResult: + return (CXuiControl *)m_resultControl; + break; + case eSectionAnvilName: + return (CXuiControl *)&m_editName; + break; + case eSectionAnvilInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionAnvilUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneAnvil::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionAnvilItem1: + return m_ingredient1Control; + break; + case eSectionAnvilItem2: + return m_ingredient2Control; + break; + case eSectionAnvilResult: + return m_resultControl; + break; + case eSectionAnvilInventory: + return m_inventoryControl; + break; + case eSectionAnvilUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneAnvil::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + // TODO Inventory dimensions need defined as constants + m_resultControl->SetData( iPad, menu, 1, 1, RepairMenu::RESULT_SLOT ); + + m_ingredient1Control->SetData( iPad, menu, 1, 1, RepairMenu::INPUT_SLOT ); + m_ingredient2Control->SetData( iPad, menu, 1, 1, RepairMenu::ADDITIONAL_SLOT); + + //m_litProgressControl->SetUserData( initData->furnace.get() ); + + //m_burnProgress->SetUserData( initData->furnace.get() ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, RepairMenu::INV_SLOT_START); +} + +void CXuiSceneAnvil::handleEditNamePressed() +{ + // 4J Stu - The edit control already handles A/Start presses, so ignore anything else + //m_editName.RequestKeyboard(m_iPad); +} + +void CXuiSceneAnvil::setEditNameValue(const wstring &name) +{ + wstring parsedName = parseXMLSpecials(name); + m_editName.SetText(parsedName.c_str()); + m_editName.SetCaretPosition(parsedName.length()); +} + +void CXuiSceneAnvil::setEditNameEditable(bool enabled) +{ +} + +void CXuiSceneAnvil::setCostLabel(const wstring &label, bool canAfford) +{ + if(canAfford) + { + m_affordableText.SetText(label.c_str()); + m_affordableText.SetShow(TRUE); + m_expensiveText.SetShow(FALSE); + } + else + { + m_expensiveText.SetText(label.c_str()); + m_affordableText.SetShow(FALSE); + m_expensiveText.SetShow(TRUE); + } +} + +void CXuiSceneAnvil::showCross(bool show) +{ + m_cross.SetShow(show?TRUE:FALSE); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.h b/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.h new file mode 100644 index 0000000..2b16c8a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Anvil.h @@ -0,0 +1,88 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_anvil.h" +#include "XUI_Scene_AbstractContainer.h" +#include "..\UI\IUIScene_AnvilMenu.h" +#include "Common\XUI\XUI_Ctrl_4JEdit.h" + +#define ANVIL_UPDATE_TIMER_ID (10) +#define ANVIL_UPDATE_TIMER_TIME (1000) // 1 second + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneAnvil : public CXuiSceneAbstractContainer, public IUIScene_AnvilMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneAnvil, L"CXuiSceneAnvil", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Ingredient, m_ingredient1Control) + MAP_OVERRIDE(IDC_Ingredient2, m_ingredient2Control) + MAP_OVERRIDE(IDC_Result, m_resultControl) + + MAP_CONTROL(IDC_AnvilTextInput, m_editName) + + MAP_CONTROL(IDC_AnvilText,m_anvilText) + MAP_CONTROL(IDC_LabelAffordable,m_affordableText) + MAP_CONTROL(IDC_LabelExpensive,m_expensiveText) + MAP_CONTROL(IDC_AnvilCross,m_cross) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled); +// HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + virtual HRESULT handleCustomTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + +private: + CXuiCtrlSlotList *m_ingredient1Control; + CXuiCtrlSlotList *m_ingredient2Control; + CXuiCtrlSlotList *m_resultControl; + + CXuiCtrl4JEdit m_editName; + + CXuiControl m_anvilText; + CXuiControl m_affordableText; + CXuiControl m_expensiveText; + CXuiControl m_cross; + CXuiControl m_sceneGroup; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); + +protected: + virtual void handleEditNamePressed(); + virtual void setEditNameValue(const wstring &name); + virtual void setEditNameEditable(bool enabled); + virtual void setCostLabel(const wstring &label, bool canAfford); + virtual void showCross(bool show); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Base.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Base.cpp new file mode 100644 index 0000000..03782c7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Base.cpp @@ -0,0 +1,2243 @@ +#include "stdafx.h" + +#include +#include "..\..\MultiplayerLevel.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\StatsCounter.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\Minecraft.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" +#include "..\..\..\Minecraft.World\LevelData.h" +#include "XUI_CustomMessages.h" +#include "..\..\..\Minecraft.World\Dimension.h" +#include "..\..\..\Minecraft.World\SharedConstants.h" +#include "..\..\GameMode.h" +#include "..\..\EnderDragonRenderer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\..\TexturePackRepository.h" +#include "..\..\TexturePack.h" +#include "..\..\DLCTexturePack.h" + +#define PRESS_START_TIMER 0 + +CXuiSceneBase *CXuiSceneBase::Instance = NULL; +DWORD CXuiSceneBase::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CXuiSceneBase::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + ASSERT( CXuiSceneBase::Instance == NULL ); + CXuiSceneBase::Instance = this; + + m_iWrongTexturePackTickC=20*5; // default 5 seconds before bringing up the upsell for not having the texture pack + MapChildControls(); + + // Display the tooltips + HRESULT hr = S_OK; + CXuiElement xuiElement = m_hObj; + HXUIOBJ hTemp; + + + m_hEmptyQuadrantLogo=NULL; + XuiElementGetChildById(m_hObj,L"EmptyQuadrantLogo",&m_hEmptyQuadrantLogo); + + D3DXVECTOR3 lastPos; + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + for( unsigned int i = 0; i < BUTTONS_TOOLTIP_MAX; ++i ) + { + m_visible[idx][ i ] = FALSE; + m_iCurrentTooltipTextID[idx][i]=-1; + hTooltipText[idx][i]=NULL; + hTooltipTextSmall[idx][i]=NULL; + // set all tooltips to shown FALSE by default + m_Buttons[idx][i].SetShow( FALSE ); + m_ButtonsSmall[idx][i].SetShow( FALSE ); + } + + XuiElementGetPosition( m_bottomLeftAnchorPoint[idx].m_hObj, &lastPos); + lastPos.y-=110; + + m_bCrouching[idx]=false; + m_uiSelectedItemOpacityCountDown[idx] =0; + m_bossHealthVisible[idx] = FALSE; + + switch(idx) + { + case 0: + XuiElementGetChildById(m_hObj,L"BasePlayer0",&hTemp); + XuiElementGetChildById(hTemp,L"XuiGamertag",&m_hGamerTagA[0]); + break; + case 1: + XuiElementGetChildById(m_hObj,L"BasePlayer1",&hTemp); + XuiElementGetChildById(hTemp,L"XuiGamertag",&m_hGamerTagA[1]); + break; + case 2: + XuiElementGetChildById(m_hObj,L"BasePlayer2",&hTemp); + XuiElementGetChildById(hTemp,L"XuiGamertag",&m_hGamerTagA[2]); + break; + case 3: + XuiElementGetChildById(m_hObj,L"BasePlayer3",&hTemp); + XuiElementGetChildById(hTemp,L"XuiGamertag",&m_hGamerTagA[3]); + break; + } + } + + m_ticksWithNoBoss = 0; + + UpdatePlayerBasePositions(); + + m_iQuadrantsMask=0; + + return S_OK; +} + +HRESULT CXuiSceneBase::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==PRESS_START_TIMER) + { + XuiKillTimer(m_hObj,PRESS_START_TIMER); + XuiElementStopTimeline(m_hObj,TRUE); + m_PressStart.SetShow(FALSE); + + // clear the quadrants + m_iQuadrantsMask=0; + + HXUIOBJ hObj=NULL,hQuadrant; + + HRESULT hr=XuiControlGetVisual(m_PressStart.m_hObj,&hObj); + + for(int i=0;iskins->getSelected(); + + if(tPack->getId()!=app.GetRequiredTexturePackID()) + { + // we're not using the texture pack we need + + //Is it available? + TexturePack * pRequiredTPack=pMinecraft->skins->getTexturePackById(app.GetRequiredTexturePackID()); + if(pRequiredTPack!=NULL) + { + // we can switch to the required pack + // reset the timer + m_iWrongTexturePackTickC=20*60*5; // reset to 5 minutes + + pMinecraft->skins->selectTexturePackById(app.GetRequiredTexturePackID()); + + // probably had background downloads enabled, so turn them off + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + } + else + { + // decrement the counter + m_iWrongTexturePackTickC--; + + if(m_iWrongTexturePackTickC==0) + { + // action + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_TexturePackRequired); + + // reset the timer + m_iWrongTexturePackTickC=20*60*5; // reset to 5 minutes + } + } + } + } + + if (EnderDragonRenderer::bossInstance == NULL) + { + if(m_ticksWithNoBoss<=20) + { + ++m_ticksWithNoBoss; + } + } + else + { + shared_ptr boss = EnderDragonRenderer::bossInstance; + EnderDragonRenderer::bossInstance = nullptr; + m_ticksWithNoBoss = 0; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(pMinecraft->localplayers[i] != NULL && pMinecraft->localplayers[i]->dimension == 1 && !ui.GetMenuDisplayed(i) && app.GetGameSettings(i,eGameSetting_DisplayHUD)) + { + int iGuiScale; + + if(pMinecraft->localplayers[i]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + iGuiScale=app.GetGameSettings(i,eGameSetting_UISize); + } + else + { + iGuiScale=app.GetGameSettings(i,eGameSetting_UISizeSplitscreen); + } + m_BossHealthGroup[i].SetShow(TRUE); + m_BossHealthText[i].SetText( app.GetString( IDS_BOSS_ENDERDRAGON_HEALTH ) ); + + if(pMinecraft->localplayers[i]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + switch(iGuiScale) + { + case 0: + m_pBossHealthProgress = m_BossHealthProgress1; + m_BossHealthProgress1[i].SetShow(TRUE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(FALSE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + + + break; + case 1: + m_pBossHealthProgress = m_BossHealthProgress2; + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(TRUE); + m_BossHealthProgress3[i].SetShow(FALSE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + + break; + case 2: + m_pBossHealthProgress = m_BossHealthProgress3; + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(TRUE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + + break; + } + } + else + { + // if we have 2 player top & bottom, we can use the fullscreen bar + if((pMinecraft->localplayers[i]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP) || + (pMinecraft->localplayers[i]->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM)) + { + switch(iGuiScale) + { + case 0: + m_pBossHealthProgress = m_BossHealthProgress1; + m_BossHealthProgress1[i].SetShow(TRUE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(FALSE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + + break; + case 1: + m_pBossHealthProgress = m_BossHealthProgress2; + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(TRUE); + m_BossHealthProgress3[i].SetShow(FALSE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + break; + case 2: + m_pBossHealthProgress = m_BossHealthProgress3; + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(TRUE); + if(m_BossHealthProgress1_small[i]!=NULL) + { + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + } + break; + } + } + else + { + // use the small versions + switch(iGuiScale) + { + case 0: + m_pBossHealthProgress = m_BossHealthProgress1_small; + m_BossHealthProgress1_small[i].SetShow(TRUE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(FALSE); + + break; + case 1: + m_pBossHealthProgress = m_BossHealthProgress2_small; + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(TRUE); + m_BossHealthProgress3_small[i].SetShow(FALSE); + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(FALSE); + break; + case 2: + m_pBossHealthProgress = m_BossHealthProgress3_small; + m_BossHealthProgress1_small[i].SetShow(FALSE); + m_BossHealthProgress2_small[i].SetShow(FALSE); + m_BossHealthProgress3_small[i].SetShow(TRUE); + m_BossHealthProgress1[i].SetShow(FALSE); + m_BossHealthProgress2[i].SetShow(FALSE); + m_BossHealthProgress3[i].SetShow(FALSE); + break; + } + } + } + + m_pBossHealthProgress[i].SetRange(0, boss->getMaxHealth() ); + m_pBossHealthProgress[i].SetValue( boss->getSynchedHealth() ); + m_bossHealthVisible[i] = TRUE; + + _UpdateSelectedItemPos(i); + } + else if( m_bossHealthVisible[i] == TRUE) + { + m_BossHealthGroup[i].SetShow(FALSE); + m_bossHealthVisible[i] = FALSE; + + _UpdateSelectedItemPos(i); + } + } + } + + for(int i=0;i 0) --m_uiSelectedItemOpacityCountDown[i]; + if( m_ticksWithNoBoss > 20 ) + { + m_BossHealthGroup[i].SetShow(FALSE); + m_bossHealthVisible[i] = FALSE; + + _UpdateSelectedItemPos(i); + } + + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=m_uiSelectedItemOpacityCountDown[i]; + + if(uiOpacityTimer>0 && !ui.GetMenuDisplayed(i) && app.GetGameStarted()) + { + if(uiOpacityTimer < (SharedConstants::TICKS_PER_SECOND * 1) ) + { + float fStep=(80.0f)/10.0f; + float fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + + XuiElementSetOpacity(m_selectedItemA[i],fVal); + XuiElementSetOpacity(m_selectedItemSmallA[i],fVal); + } + + if( m_playerBaseScenePosition[i] == e_BaseScene_Fullscreen ) + { + m_selectedItemA[i].SetShow(TRUE); + m_selectedItemSmallA[i].SetShow(FALSE); + } + else + { + m_selectedItemA[i].SetShow(FALSE); + m_selectedItemSmallA[i].SetShow(TRUE); + } + } + else + { + m_selectedItemA[i].SetShow(FALSE); + m_selectedItemSmallA[i].SetShow(FALSE); + } + + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + float fVal; + + if(ucAlpha<80) + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(i) && (ucAlpha<15)) + { + ucAlpha=15; + } + + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(i); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=0.01f*80.0f; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + } + else + { + // if we are in a menu, set the minimum opacity for tooltips to 15% + if(ui.GetMenuDisplayed(i) && (ucAlpha<15)) + { + ucAlpha=15; + } + fVal=0.01f*(float)ucAlpha; + } + XuiElementSetOpacity(app.GetCurrentHUDScene(i),fVal); + + XUIMessage xuiMsg; + CustomMessage_TickScene( &xuiMsg ); + XuiSendMessage( app.GetCurrentHUDScene(i), &xuiMsg ); + + bool bDisplayGui=app.GetGameStarted() && !ui.GetMenuDisplayed(i) && !(app.GetXuiAction(i)==eAppAction_AutosaveSaveGameCapturedThumbnail) && app.GetGameSettings(i,eGameSetting_DisplayHUD)!=0; + if(bDisplayGui && pMinecraft->localplayers[i] != NULL) + { + XuiElementSetShow(app.GetCurrentHUDScene(i),TRUE); + } + else + { + XuiElementSetShow(app.GetCurrentHUDScene(i),FALSE); + } + } +} + +HRESULT CXuiSceneBase::_SetEnableTooltips( unsigned int iPad, BOOL bVal ) +{ + for(int i=0;i=0) + { + pString=app.GetString(iTextID); + } + + if(hTooltipText[iPad][uiTooltip]==NULL) + { + HXUIOBJ hObj=NULL; + hr=XuiControlGetVisual(m_Buttons[iPad][uiTooltip].m_hObj,&hObj); + hr=XuiElementGetChildById(hObj,L"text_ButtonText",&hTooltipText[iPad][uiTooltip]); + hr=XuiElementGetPosition(hTooltipText[iPad][uiTooltip],&m_vPosTextInTooltip[uiTooltip]); + } + + if(hTooltipTextSmall[iPad][uiTooltip]==NULL) + { + HXUIOBJ hObj=NULL; + hr=XuiControlGetVisual(m_ButtonsSmall[iPad][uiTooltip].m_hObj,&hObj); + hr=XuiElementGetChildById(hObj,L"text_ButtonText",&hTooltipTextSmall[iPad][uiTooltip]); + hr=XuiElementGetPosition(hTooltipTextSmall[iPad][uiTooltip],&m_vPosTextInTooltipSmall[uiTooltip]); + } + + if(iTextID>=0) + { + hr=XuiTextPresenterMeasureText(hTooltipText[iPad][uiTooltip], pString, &xuiRect); + + // Change the size of the whole button to be the width of the measured text, plus the position the text element starts in the visual (which is the offset by the size of the button graphic) + XuiElementGetBounds(m_Buttons[iPad][uiTooltip].m_hObj,&fWidth, &fHeight); + XuiElementSetBounds(m_Buttons[iPad][uiTooltip].m_hObj,xuiRect.right+1+m_vPosTextInTooltip[uiTooltip].x,fHeight); + + // Change the width of the text element to be the width of the measured text + XuiElementGetBounds(hTooltipText[iPad][uiTooltip],&fWidth, &fHeight); + XuiElementSetBounds(hTooltipText[iPad][uiTooltip],xuiRect.right,fHeight); + + + hr=XuiTextPresenterMeasureText(hTooltipTextSmall[iPad][uiTooltip], pString, &xuiRectSmall); + + // Change the size of the whole button to be the width of the measured text, plus the position the text element starts in the visual (which is the offset by the size of the button graphic) + XuiElementGetBounds(m_ButtonsSmall[iPad][uiTooltip].m_hObj,&fWidth, &fHeight); + XuiElementSetBounds(m_ButtonsSmall[iPad][uiTooltip].m_hObj,xuiRectSmall.right+1+m_vPosTextInTooltipSmall[uiTooltip].x,fHeight); + + // Change the width of the text element to be the width of the measured text + XuiElementGetBounds(hTooltipTextSmall[iPad][uiTooltip],&fWidth, &fHeight); + XuiElementSetBounds(hTooltipTextSmall[iPad][uiTooltip],xuiRectSmall.right,fHeight); + + m_Buttons[iPad][uiTooltip].SetText(pString); + m_ButtonsSmall[iPad][uiTooltip].SetText(pString); + } + else + { + m_Buttons[iPad][uiTooltip].SetText(L""); + XuiElementGetBounds(m_Buttons[iPad][uiTooltip].m_hObj,&fWidth, &fHeight); + XuiElementSetBounds(m_Buttons[iPad][uiTooltip].m_hObj,m_vPosTextInTooltip[uiTooltip].x,fHeight); + + m_ButtonsSmall[iPad][uiTooltip].SetText(L""); + XuiElementGetBounds(m_ButtonsSmall[iPad][uiTooltip].m_hObj,&fWidth, &fHeight); + XuiElementSetBounds(m_ButtonsSmall[iPad][uiTooltip].m_hObj,m_vPosTextInTooltipSmall[uiTooltip].x,fHeight); + } + + m_iCurrentTooltipTextID[iPad][uiTooltip]=iTextID; + + ReLayout(iPad); + + return hr; +} + +HRESULT CXuiSceneBase::_RefreshTooltips( unsigned int iPad) +{ + // if the tooltip is showing, refresh it to update the opacity + for(int tooltip=0;tooltiplevel!=NULL) + { + __int64 i64TimeOfDay =0; + // are we in the Nether? - Leave the time as 0 if we are, so we show daylight + if(pMinecraft->level->dimension->id==0) + { + i64TimeOfDay = pMinecraft->level->getLevelData()->getTime() % 24000; + } + + if(i64TimeOfDay>14000) + { + hr=XuiElementSetShow(hNight,TRUE); + hr=XuiElementSetShow(hDay,FALSE); + } + else + { + hr=XuiElementSetShow(hNight,FALSE); + hr=XuiElementSetShow(hDay,TRUE); + } + } + else + { + hr=XuiElementSetShow(hNight,FALSE); + hr=XuiElementSetShow(hDay,TRUE); + } + } + hr=XuiElementSetShow(m_Background[iPad],bShow); + + return hr; +} + +HRESULT CXuiSceneBase::_ShowDarkOverlay( unsigned int iPad, BOOL bShow ) +{ + return XuiElementSetShow(m_DarkOverlay[iPad],bShow); +} + +HRESULT CXuiSceneBase::_ShowLogo( unsigned int iPad, BOOL bShow ) +{ + return XuiElementSetShow(m_Logo[iPad],bShow); +} + +HRESULT CXuiSceneBase::_ShowPressStart(unsigned int iPad) +{ + XUIRect xuiRect; + LPCWSTR pString=app.GetString(IDS_PRESS_START_TO_JOIN);; + float fWidth,fHeight,fWidthChange; + + XuiSetTimer( m_hObj,PRESS_START_TIMER,3000); + m_iQuadrantsMask|=1<m_dwTrialTimerLimitSecs) + { + dwTimeTicks=m_dwTrialTimerLimitSecs; + } + + dwTimeTicks=m_dwTrialTimerLimitSecs-dwTimeTicks; + +#ifndef _CONTENT_PACKAGE + if(true) +#else + // display the time - only if there's less than 3 minutes + if(dwTimeTicks<180) +#endif + { + int iMins=dwTimeTicks/60; + int iSeconds=dwTimeTicks%60; + swprintf( wcTime, 20, L"%d:%02d",iMins,iSeconds); + m_TrialTimer.SetText(wcTime); + } + else + { + m_TrialTimer.SetText(L""); + } + + // are we out of time? + if(dwTimeTicks==0) + { + // Trial over + app.SetAction(iPad,eAppAction_TrialOver); + } + + return S_OK; +} + +void CXuiSceneBase::_ReduceTrialTimerValue() +{ + DWORD dwTimeTicks=(int)app.getTrialTimer(); + + if(dwTimeTicks>m_dwTrialTimerLimitSecs) + { + dwTimeTicks=m_dwTrialTimerLimitSecs; + } + + m_dwTrialTimerLimitSecs-=dwTimeTicks; +} + +HRESULT CXuiSceneBase::_ShowTrialTimer(BOOL bVal) +{ + m_TrialTimer.SetShow(bVal); + return S_OK; +} + +bool CXuiSceneBase::_PressStartPlaying(unsigned int iPad) +{ + return m_iQuadrantsMask&(1<1280x720 + scale.x = 2.0f; scale.y = 1.5f; scale.z = 1.0f; + XuiElementSetScale(m_hObj, &scale); + + return S_OK; + } + + + if( position != e_BaseScene_Fullscreen ) + { + // Scale up the tooltips so we can read them + /* + scale.x = 0.75f; scale.y = 0.75f; scale.z = 1.0f; + XuiElementSetScale(m_TooltipGroup[iPad], &scale); + fTooltipHeight*=scale.y; + */ + fTooltipHeight = fTooltipHeightSmall; + + scale.x = 0.5f; scale.y = 0.5f; scale.z = 1.0f; + XuiElementSetScale(m_CrouchIcon[iPad], &scale); + XuiElementSetScale(m_Logo[iPad].m_hObj, &scale); + } + else + { + // if we are not in high def mode, then we need to scale the m_BasePlayerScene scene by 2 (we're using the 640x360 scenes) + scale.x = 1.0f; scale.y = 1.0f; scale.z = 1.0f; + XuiElementSetScale(m_BasePlayerScene[iPad], &scale ); + XuiElementSetScale(m_TooltipGroup[iPad], &scale); + XuiElementSetScale(m_CrouchIcon[iPad], &scale); + XuiElementSetScale(m_Logo[iPad].m_hObj, &scale); + } + + + // The move applies to the whole scene, so we'll need to move tooltips back in some cases + switch( position ) + { + // No position adjustment + case e_BaseScene_Fullscreen: + tooltipsPos.x=SAFEZONE_HALF_WIDTH; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackWidth=XUI_BASE_SCENE_WIDTH; + fBackHeight=XUI_BASE_SCENE_HEIGHT; + + XuiElementGetPosition(m_selectedItemA[iPad], &vBossHealthPos); + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_HALF-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT; + break; + case e_BaseScene_Top_Left: + tooltipsPos.x=SAFEZONE_HALF_WIDTH; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth - 10.0f; + vGamertagPos.y=SAFEZONE_HALF_HEIGHT; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT; + break; + case e_BaseScene_Top: // Top & Bottom - indent by a quarter screen + pos.x += XUI_BASE_SCENE_WIDTH_QUARTER; + tooltipsPos.x=SAFEZONE_HALF_WIDTH - XUI_BASE_SCENE_WIDTH_QUARTER; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH-XUI_BASE_SCENE_WIDTH_QUARTER; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + fBackWidth=XUI_BASE_SCENE_WIDTH; + vBackPos.x=-XUI_BASE_SCENE_WIDTH_QUARTER; + vBackPos.y=0.0f; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH - XUI_BASE_SCENE_WIDTH_QUARTER - fGamertagWidth - SAFEZONE_HALF_WIDTH; + vGamertagPos.y=SAFEZONE_HALF_HEIGHT; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT; + break; + case e_BaseScene_Bottom: + pos.x += XUI_BASE_SCENE_WIDTH_QUARTER; + pos.y += XUI_BASE_SCENE_HEIGHT_HALF; + tooltipsPos.x=SAFEZONE_HALF_WIDTH - XUI_BASE_SCENE_WIDTH_QUARTER; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH-XUI_BASE_SCENE_WIDTH_QUARTER; + crouchIconPos.y=0.0f; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + fBackWidth=XUI_BASE_SCENE_WIDTH; + vBackPos.x=-XUI_BASE_SCENE_WIDTH_QUARTER; + vBackPos.y=0.0f; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH - XUI_BASE_SCENE_WIDTH_QUARTER - fGamertagWidth - SAFEZONE_HALF_WIDTH; + vGamertagPos.y=0.0f; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = 0.0f; + break; + case e_BaseScene_Bottom_Left: + pos.y += XUI_BASE_SCENE_HEIGHT_HALF; + tooltipsPos.x=SAFEZONE_HALF_WIDTH; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH; + crouchIconPos.y=0.0f; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth- 10.0f; + vGamertagPos.y=0.0f; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = 0.0f; + break; + case e_BaseScene_Bottom_Right: + pos.x += XUI_BASE_SCENE_WIDTH_HALF; + pos.y += XUI_BASE_SCENE_HEIGHT_HALF; + tooltipsPos.x=0.0f; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=0.0f; + crouchIconPos.y=0.0f; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth-SAFEZONE_HALF_WIDTH; + vGamertagPos.y=0.0f; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = 0.0f; + break; + case e_BaseScene_Left: + pos.y += XUI_BASE_SCENE_HEIGHT_QUARTER; + tooltipsPos.x=SAFEZONE_HALF_WIDTH; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF+XUI_BASE_SCENE_HEIGHT_QUARTER-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=SAFEZONE_HALF_WIDTH; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT; + vBackPos.x=0.0f; + vBackPos.y=-XUI_BASE_SCENE_HEIGHT_QUARTER; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth- 10.0f; + vGamertagPos.y=SAFEZONE_HALF_HEIGHT - XUI_BASE_SCENE_HEIGHT_QUARTER; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT-XUI_BASE_SCENE_HEIGHT_QUARTER; + break; + case e_BaseScene_Right: + pos.x += XUI_BASE_SCENE_WIDTH_HALF; + pos.y += XUI_BASE_SCENE_HEIGHT_QUARTER; + tooltipsPos.x=0.0f; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF+XUI_BASE_SCENE_HEIGHT_QUARTER-SAFEZONE_HALF_HEIGHT-fTooltipHeight; + crouchIconPos.x=0.0f; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT; + vBackPos.x=0.0f; + vBackPos.y=-XUI_BASE_SCENE_HEIGHT_QUARTER; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth-SAFEZONE_HALF_WIDTH; + vGamertagPos.y=SAFEZONE_HALF_HEIGHT - XUI_BASE_SCENE_HEIGHT_QUARTER; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT-XUI_BASE_SCENE_HEIGHT_QUARTER; + break; + case e_BaseScene_Top_Right: + pos.x += XUI_BASE_SCENE_WIDTH_HALF; + tooltipsPos.x=0.0f; + tooltipsPos.y=XUI_BASE_SCENE_HEIGHT_HALF-fTooltipHeight; + crouchIconPos.x=0.0f; + crouchIconPos.y=SAFEZONE_HALF_HEIGHT; + fBackWidth=XUI_BASE_SCENE_WIDTH_HALF; + fBackHeight=XUI_BASE_SCENE_HEIGHT_HALF; + vGamertagPos.x=XUI_BASE_SCENE_WIDTH_HALF-fGamertagWidth-SAFEZONE_HALF_WIDTH; + vGamertagPos.y=SAFEZONE_HALF_HEIGHT; + vBossHealthPos.x = XUI_BASE_SCENE_WIDTH_QUARTER-(fBossHealthWidth/2); + vBossHealthPos.y = SAFEZONE_HALF_HEIGHT; + break; + } + + XuiElementSetPosition(m_BasePlayerScene[iPad], &pos ); + XuiElementSetPosition( m_TooltipGroup[iPad].m_hObj, &tooltipsPos); + XuiElementSetPosition( m_TooltipGroupSmall[iPad].m_hObj, &tooltipsPos); + XuiElementSetPosition( m_CrouchIcon[iPad].m_hObj, &crouchIconPos); + XuiElementSetPosition( m_DarkOverlay[iPad].m_hObj, &vBackPos ); + XuiElementSetBounds( m_DarkOverlay[iPad].m_hObj, fBackWidth, fBackHeight); + XuiElementSetPosition( m_Background[iPad].m_hObj, &vBackPos ); + XuiElementSetBounds( m_Background[iPad].m_hObj, fBackWidth, fBackHeight ); + vGamertagPos.z=0.0f; + XuiElementSetPosition( m_hGamerTagA[iPad], &vGamertagPos ); + XuiElementSetPosition( m_BossHealthGroup[iPad], &vBossHealthPos ); + + + // 4J Stu - If we already have some scenes open, then call this to update their positions + // Fix for #10960 - All Lang: UI: Split-screen: Changing split screen mode (vertical/horizontal) make window layout strange + if(Minecraft::GetInstance() != NULL && Minecraft::GetInstance()->localplayers[iPad]!=NULL) + { + // 4J-PB - Can only do this once we know what the player's UI settings are, so we need to have the player game settings read + _UpdateSelectedItemPos(iPad); + XUIMessage xuiMsg; + CustomMessage_Splitscreenplayer_Struct myMsgData; + CustomMessage_Splitscreenplayer( &xuiMsg, &myMsgData, false); + XuiBroadcastMessage( GetPlayerBaseScene(iPad), &xuiMsg ); + } + // tell the xui scenes that the base position has changed + XUIMessage xuiMsg; + CustomMessage_BasePositionChanged( &xuiMsg ); + XuiBroadcastMessage( GetPlayerBaseScene(iPad), &xuiMsg ); + + return S_OK; +} + +// The function uses the game mode to decide the offsets for the select item. It needs to be called after the game has loaded. +void CXuiSceneBase::_UpdateSelectedItemPos(unsigned int iPad) +{ + D3DXVECTOR3 selectedItemPos; + selectedItemPos.x = selectedItemPos.y = selectedItemPos.z = 0.0f; + float fSelectedItemWidth, fSelectedItemHeight; + XuiElementGetBounds(m_selectedItemSmallA[iPad],&fSelectedItemWidth, &fSelectedItemHeight); + + float yOffset = 0.0f; + + if( m_bossHealthVisible[iPad] == TRUE ) + { + float tempWidth; + XuiElementGetBounds(m_BossHealthGroup[iPad],&tempWidth, &yOffset); + } + + + // Only adjust if fullscreen for now, leaving code to move others if required, but it's too far up the screen when on the bottom quadrants + if( (m_playerBaseScenePosition[iPad] == e_BaseScene_Fullscreen) && + (RenderManager.IsHiDef() || RenderManager.IsWidescreen()) ) + { + D3DXVECTOR3 selectedItemPos; + selectedItemPos.z=0.0f; + float scale, fTempWidth, fTooltipHeight, fTooltipHeightSmall, fSelectedItemWidth, fSelectedItemHeight, fSelectedItemSmallWidth, fSelectedItemSmallHeight; + XuiElementGetBounds(m_TooltipGroup[iPad].m_hObj,&fTempWidth, &fTooltipHeight); + XuiElementGetBounds(m_TooltipGroupSmall[iPad].m_hObj,&fTempWidth, &fTooltipHeightSmall); + XuiElementGetBounds(m_selectedItemA[iPad],&fSelectedItemWidth, &fSelectedItemHeight); + XuiElementGetBounds(m_selectedItemSmallA[iPad],&fSelectedItemSmallWidth, &fSelectedItemSmallHeight); + if( m_playerBaseScenePosition[iPad] != e_BaseScene_Fullscreen ) + { + fTooltipHeight = fTooltipHeightSmall; + fSelectedItemHeight = fSelectedItemSmallHeight; + + scale = 0.5f; + } + else + { + // if we are not in high def mode, then we need to scale the m_BasePlayerScene scene by 2 (we're using the 640x360 scenes) + scale = 1.0f; + } + + // The move applies to the whole scene, so we'll need to move tooltips back in some cases + + selectedItemPos.y=XUI_BASE_SCENE_HEIGHT-SAFEZONE_HALF_HEIGHT-fTooltipHeight - fSelectedItemHeight; + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_HALF - (fSelectedItemWidth/2.0f); + + // Adjust selectedItemPos based on what gui is displayed + + + // 4J-PB - selected the gui scale based on the slider settings, and on whether we're in Creative or Survival + float fYOffset=0.0f; + + unsigned char ucGuiScale=app.GetGameSettings(iPad,eGameSetting_UISize) + 2; + + if(Minecraft::GetInstance() != NULL && Minecraft::GetInstance()->localgameModes[iPad] != NULL && Minecraft::GetInstance()->localgameModes[iPad]->canHurtPlayer()) + { + // SURVIVAL MODE - Move up further because of hearts, shield and xp + switch(ucGuiScale) + { + case 3: + fYOffset = -130.0f; + break; + case 4: + fYOffset = -168.0f; + break; + default: // 2 + fYOffset = -94.0f; + break; + } + } + else + { + switch(ucGuiScale) + { + case 3: + fYOffset = -83.0f; + break; + case 4: + fYOffset = -114.0f; + break; + default: // 2 + fYOffset = -58.0f; + break; + } + } + + + selectedItemPos.y+=fYOffset - 40.0f; // 40 for the XP display + + XuiElementSetPosition( m_selectedItemA[iPad].m_hObj, &selectedItemPos ); + + //XuiElementSetPosition( m_selectedItemSmallA[iPad].m_hObj, &selectedItemPos ); + } + else + { + // The move applies to the whole scene, so we'll need to move tooltips back in some cases + switch( m_playerBaseScenePosition[iPad] ) + { + case e_BaseScene_Fullscreen: + // 480 non-widescreen + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = SAFEZONE_HALF_HEIGHT + yOffset; + break; + case e_BaseScene_Top_Left: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = SAFEZONE_HALF_HEIGHT + yOffset; + break; + case e_BaseScene_Top: // Top & Bottom - indent by a quarter screen + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = SAFEZONE_HALF_HEIGHT + yOffset; + break; + case e_BaseScene_Bottom: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = 0.0f + yOffset; + break; + case e_BaseScene_Bottom_Left: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = 0.0f + yOffset; + break; + case e_BaseScene_Bottom_Right: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = 0.0f + yOffset; + break; + case e_BaseScene_Left: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = XUI_BASE_SCENE_HEIGHT_HALF;// + yOffset; - don't need the offset for the boss health since we're displaying the item at the bottom of the screen, not the top + break; + case e_BaseScene_Right: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = XUI_BASE_SCENE_HEIGHT_HALF;// + yOffset; - don't need the offset for the boss health since we're displaying the item at the bottom of the screen, not the top + break; + case e_BaseScene_Top_Right: + selectedItemPos.x = XUI_BASE_SCENE_WIDTH_QUARTER - (fSelectedItemWidth/2.0f); + selectedItemPos.y = SAFEZONE_HALF_HEIGHT + yOffset; + break; + } + + // 4J-PB - If it's in split screen vertical, adjust the position + // Adjust selectedItemPos based on what gui is displayed + if((m_playerBaseScenePosition[iPad]==e_BaseScene_Left) || (m_playerBaseScenePosition[iPad]==e_BaseScene_Right)) + { + float scale=0.5f; + selectedItemPos.y -= (scale * 88.0f); + if(Minecraft::GetInstance() != NULL && Minecraft::GetInstance()->localgameModes[iPad] != NULL && Minecraft::GetInstance()->localgameModes[iPad]->canHurtPlayer()) + { + selectedItemPos.y -= (scale * 80.0f); + } + + // 4J-PB - selected the gui scale based on the slider settings + unsigned char ucGuiScale; + float fYOffset=0.0f; + if(m_playerBaseScenePosition[iPad]==e_BaseScene_Fullscreen) + { + ucGuiScale=app.GetGameSettings(iPad,eGameSetting_UISize) + 2; + } + else + { + ucGuiScale=app.GetGameSettings(iPad,eGameSetting_UISizeSplitscreen) + 2; + } + switch(ucGuiScale) + { + case 3: + fYOffset = 55.0f; + break; + case 4: + fYOffset = 45.0f; + break; + default: // 2 + fYOffset = 85.0f; + break; + } + selectedItemPos.y+=fYOffset; + } + + XuiElementSetPosition( m_selectedItemSmallA[iPad].m_hObj, &selectedItemPos ); + XuiElementSetPosition( m_selectedItemA[iPad].m_hObj, &selectedItemPos ); + } +} + +CXuiSceneBase::EBaseScenePosition CXuiSceneBase::_GetPlayerBasePosition(int iPad) +{ + return m_playerBaseScenePosition[iPad]; +} + +void CXuiSceneBase::_SetEmptyQuadrantLogo(int iPad,EBaseScenePosition ePos) +{ + if(m_hEmptyQuadrantLogo!=NULL) + { + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(m_playerBaseScenePosition[i] == e_BaseScene_Fullscreen) + { + // Someone is fullscreen, so don't show this + XuiElementSetShow(m_hEmptyQuadrantLogo,FALSE); + return; + } + } + + D3DXVECTOR3 pos; + + // get the bounds of the logo + + pos.z=0.0f; + switch( ePos ) + { + case e_BaseScene_Top_Left: + pos.x=64.0f; + pos.y=36.0f; + break; + case e_BaseScene_Top_Right: + pos.x=640.0+64.0f; + pos.y=36.0f; + break; + case e_BaseScene_Bottom_Left: + pos.x=64.0f; + pos.y=360.0f+36.0f; + break; + case e_BaseScene_Bottom_Right: + pos.x=640.0+64.0f; + pos.y=360.0f+36.0f; + break; + } + + // set the opacity of the logo + if(ProfileManager.GetLockedProfile()!=-1) + { + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + XuiElementSetOpacity(m_hEmptyQuadrantLogo,0.01f*(float)ucAlpha); + } + + XuiElementSetPosition(m_hEmptyQuadrantLogo, &pos ); + XuiElementSetShow(m_hEmptyQuadrantLogo,TRUE); + } +} + + +HRESULT CXuiSceneBase::_PlayUISFX(ESoundEffect eSound) +{ + HRESULT hr; + bool bUsingTexturepackWithAudio=false; + + // are we using a mash-up pack? + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + if(tPack->hasAudio()) + { + bUsingTexturepackWithAudio=true; + } + } + + /*if(bUsingTexturepackWithAudio) + { + switch(eSound) + { + case eSFX_Back: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_Craft: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_CraftFail: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_Focus: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_Press: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_Scroll: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + default: + hr=S_OK; + break; + } + } + else*/ + { + + switch(eSound) + { + case eSFX_Back: + hr=XuiSoundPlay(m_SFXA[eSFX_Back]); + break; + case eSFX_Craft: + hr=XuiSoundPlay(m_SFXA[eSFX_Craft]); + break; + case eSFX_CraftFail: + hr=XuiSoundPlay(m_SFXA[eSFX_CraftFail]); + break; + case eSFX_Focus: + hr=XuiSoundPlay(m_SFXA[eSFX_Focus]); + break; + case eSFX_Press: + hr=XuiSoundPlay(m_SFXA[eSFX_Press]); + break; + case eSFX_Scroll: + hr=XuiSoundPlay(m_SFXA[eSFX_Scroll]); + break; + default: + hr=S_OK; + break; + } + } + return hr; +} + + +HRESULT CXuiSceneBase::_DisplayGamertag( unsigned int iPad, BOOL bDisplay ) +{ + + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<localplayers[iPad]!=NULL) + { + wstring wsGamertag = convStringToWstring( ProfileManager.GetGamertag(iPad)); + XuiControlSetText(m_hGamerTagA[iPad],wsGamertag.c_str()); + + } + else + { + XuiControlSetText(m_hGamerTagA[iPad],L""); + } + } + } + // The host decides whether these are on or off + if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplaySplitscreenGamertags)!=0) + { + XuiElementSetShow(m_hGamerTagA[iPad],bDisplay); + + // set the opacity of the gamertag + if((bDisplay==TRUE) &&(ProfileManager.GetLockedProfile()!=-1)) + { + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + float fVal; + + if(ucAlpha<80) + { + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(ProfileManager.GetPrimaryPad()); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=0.01f*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=0.01f*80.0f; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + } + else + { + fVal=0.01f*(float)ucAlpha; + } + XuiElementSetOpacity(m_hGamerTagA[iPad],0.01f*fVal); + } + } + else + { + XuiElementSetShow(m_hGamerTagA[iPad],FALSE); + } + + return S_OK; +} + +void CXuiSceneBase::_SetSelectedItem( unsigned int iPad, const wstring& name) +{ + if(app.GetGameSettings(eGameSetting_Hints) == 0 || name.empty()) + { + m_selectedItemA[iPad].SetShow(FALSE); + m_selectedItemSmallA[iPad].SetShow(FALSE); + } + else + { + m_uiSelectedItemOpacityCountDown[iPad] = SharedConstants::TICKS_PER_SECOND * 3; + if(m_playerBaseScenePosition[iPad] == e_BaseScene_Fullscreen) + { + m_selectedItemSmallA[iPad].SetShow(FALSE); + m_selectedItemA[iPad].SetShow(TRUE); + m_selectedItemA[iPad].SetText(name.c_str()); + +// D3DXVECTOR3 vPos; +// XuiElementGetPosition( m_selectedItemA[iPad].m_hObj, &vPos ); +// XuiElementSetPosition( m_selectedItemA[iPad].m_hObj, &vPos ); + + float fVal=0.8f;//0.01f*(float)80; + XuiElementSetOpacity(m_selectedItemA[iPad].m_hObj,fVal); + } + else + { + m_selectedItemA[iPad].SetShow(FALSE); + m_selectedItemSmallA[iPad].SetShow(TRUE); + m_selectedItemSmallA[iPad].SetText(name.c_str()); + +// D3DXVECTOR3 vPos; +// XuiElementGetPosition( m_selectedItemSmallA[iPad].m_hObj, &vPos ); +// XuiElementSetPosition( m_selectedItemSmallA[iPad].m_hObj, &vPos ); + + float fVal=0.8f;//0.01f*(float)80; + XuiElementSetOpacity(m_selectedItemSmallA[iPad].m_hObj,fVal); + } + } +} + +void CXuiSceneBase::_HideAllGameUIElements() +{ + for(int i=0;i0 && lastVisible!=-1 ) + { + float width, height; + XuiElementGetBounds(m_Buttons[iPad][lastVisible].m_hObj, &width, &height); + + // 4J Stu - This is for horizontal layout, will need changed if we do vertical layout + lastPos.x += width + m_iTooltipSpacingGap; + + XuiElementGetBounds(m_ButtonsSmall[iPad][lastVisible].m_hObj, &width, &height); + // 4J Stu - This is for horizontal layout, will need changed if we do vertical layout + lastPosSmall.x += width + m_iTooltipSpacingGapSmall; + } + XuiElementSetPosition( m_Buttons[iPad][i].m_hObj, &lastPos); + XuiElementSetPosition( m_ButtonsSmall[iPad][i].m_hObj, &lastPosSmall); + + lastVisible = i; + } + } +} + +void CXuiSceneBase::TickAllBaseScenes() +{ + if( CXuiSceneBase::Instance != NULL ) + { + CXuiSceneBase::Instance->_TickAllBaseScenes(); + } +} + +HRESULT CXuiSceneBase::SetEnableTooltips( unsigned int iPad, BOOL bVal ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_SetEnableTooltips(iPad, bVal ); + } + return S_OK; +} + +HRESULT CXuiSceneBase::SetTooltipText( unsigned int iPad, unsigned int tooltip, int iTextID ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_SetTooltipText(iPad, tooltip, iTextID ); + } + return S_OK; +} + +HRESULT CXuiSceneBase::RefreshTooltips( unsigned int iPad) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_RefreshTooltips(iPad); + } + return S_OK; +} + +HRESULT CXuiSceneBase::ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_ShowTooltip(iPad, tooltip, show ); + } + return S_OK; +} + +HRESULT CXuiSceneBase::ShowSafeArea( BOOL bShow ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_ShowSafeArea(bShow ); + } + return S_OK; +} + +HRESULT CXuiSceneBase::SetTooltips( unsigned int iPad, int iA, int iB, int iX, int iY , int iLT, int iRT, int iRB, int iLB, int iLS, bool forceUpdate /*= false*/ ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + // Enable all the tooltips. We should disable them in the scenes as required + CXuiSceneBase::Instance->_SetTooltipsEnabled( iPad ); + + int iTooptipsA[BUTTONS_TOOLTIP_MAX]; + iTooptipsA[BUTTON_TOOLTIP_A]=iA; + iTooptipsA[BUTTON_TOOLTIP_B]=iB; + iTooptipsA[BUTTON_TOOLTIP_X]=iX; + iTooptipsA[BUTTON_TOOLTIP_Y]=iY; + iTooptipsA[BUTTON_TOOLTIP_LT]=iLT; + iTooptipsA[BUTTON_TOOLTIP_RT]=iRT; + iTooptipsA[BUTTON_TOOLTIP_LB]=iRB; + iTooptipsA[BUTTON_TOOLTIP_RB]=iLB; + iTooptipsA[BUTTON_TOOLTIP_LS]=iLS; + + for(int i=0;iSetTooltipText(iPad, i, -1 ); + CXuiSceneBase::Instance->_ShowTooltip(iPad, i, false ); + //CXuiSceneBase::Instance->m_iCurrentTooltipTextID[iPad][i]=-1; + } + else if(iTooptipsA[i]==-2) + { + CXuiSceneBase::Instance->_ShowTooltip(iPad, i, true ); + CXuiSceneBase::Instance->SetTooltipText(iPad, i, -2 ); + } + else + { + // does the tooltip need to change? + if(CXuiSceneBase::Instance->m_iCurrentTooltipTextID[iPad][i]!=iTooptipsA[i] || forceUpdate) + { + CXuiSceneBase::Instance->SetTooltipText(iPad, i, iTooptipsA[i] ); + } + CXuiSceneBase::Instance->_ShowTooltip(iPad, i, true ); + } + } + + } + return S_OK; +} + +HRESULT CXuiSceneBase::SetTooltipsEnabled( unsigned int iPad, bool bA, bool bB, bool bX, bool bY,bool bLT, bool bRT, bool bLB, bool bRB, bool bLS) +{ + return CXuiSceneBase::Instance->_SetTooltipsEnabled(iPad, bA, bB, bX, bY, bLT, bRT, bLB, bRB, bLS ); +} + +HRESULT CXuiSceneBase::EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ) +{ + return CXuiSceneBase::Instance->_EnableTooltip(iPad, tooltip, enable); +} + +HRESULT CXuiSceneBase::AnimateKeyPress(DWORD userIndex, DWORD dwKeyCode) +{ + return CXuiSceneBase::Instance->_AnimateKeyPress(userIndex, dwKeyCode ); +} + +HRESULT CXuiSceneBase::ShowSavingMessage( unsigned int iPad, C4JStorage::ESavingMessage eVal ) +{ + if( CXuiSceneBase::Instance != NULL ) + { + return CXuiSceneBase::Instance->_ShowSavingMessage(iPad, eVal); + } + + return S_OK; +} + +HRESULT CXuiSceneBase::ShowBackground( unsigned int iPad, BOOL bShow ) +{ + return CXuiSceneBase::Instance->_ShowBackground(iPad, bShow ); +} + +HRESULT CXuiSceneBase::ShowDarkOverlay( unsigned int iPad, BOOL bShow ) +{ + return CXuiSceneBase::Instance->_ShowDarkOverlay(iPad, bShow ); +} + +HRESULT CXuiSceneBase::ShowLogo( unsigned int iPad, BOOL bShow ) +{ + return CXuiSceneBase::Instance->_ShowLogo(iPad, bShow ); +} + +HRESULT CXuiSceneBase::ShowPressStart(unsigned int iPad) +{ + CXuiSceneBase::Instance->_ShowPressStart(iPad); + return S_OK; +} + +HRESULT CXuiSceneBase::ShowOtherPlayersBaseScene(int iPad, bool show) +{ + CXuiSceneBase::Instance->_ShowOtherPlayersBaseScene(iPad, show); + return S_OK; +} + +HRESULT CXuiSceneBase::UpdateAutosaveCountdownTimer(unsigned int uiSeconds) +{ + CXuiSceneBase::Instance->_UpdateAutosaveCountdownTimer(uiSeconds); + return S_OK; +} + +HRESULT CXuiSceneBase::ShowAutosaveCountdownTimer(BOOL bVal) +{ + CXuiSceneBase::Instance->_ShowAutosaveCountdownTimer(bVal); + return S_OK; +} + +HRESULT CXuiSceneBase::UpdateTrialTimer(unsigned int iPad) +{ + CXuiSceneBase::Instance->_UpdateTrialTimer(iPad); + return S_OK; +} +HRESULT CXuiSceneBase::ShowTrialTimer(BOOL bVal) +{ + CXuiSceneBase::Instance->_ShowTrialTimer(bVal); + return S_OK; +} + +void CXuiSceneBase::ReduceTrialTimerValue() +{ + CXuiSceneBase::Instance->_ReduceTrialTimerValue(); +} + +bool CXuiSceneBase::PressStartPlaying(unsigned int iPad) +{ + return CXuiSceneBase::Instance->_PressStartPlaying(iPad); +} + +HRESULT CXuiSceneBase::HidePressStart() +{ + return CXuiSceneBase::Instance->_HidePressStart(); +} + +HRESULT CXuiSceneBase::SetPlayerBaseScenePosition( unsigned int iPad, EBaseScenePosition position ) +{ + return CXuiSceneBase::Instance->_SetPlayerBaseScenePosition(iPad, position ); +} + +HRESULT CXuiSceneBase::SetPlayerBasePositions(EBaseScenePosition pad0, EBaseScenePosition pad1, EBaseScenePosition pad2, EBaseScenePosition pad3) +{ + SetPlayerBaseScenePosition( 0, pad0 ); + SetPlayerBaseScenePosition( 1, pad1 ); + SetPlayerBaseScenePosition( 2, pad2 ); + SetPlayerBaseScenePosition( 3, pad3 ); + + return S_OK; +} + +HRESULT CXuiSceneBase::UpdatePlayerBasePositions() +{ + EBaseScenePosition padPositions[XUSER_MAX_COUNT]; + + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + padPositions[idx] = e_BaseScene_NotSet; + } + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + // If the game is not started (or is being held paused for a bit) then display all scenes fullscreen + if( pMinecraft == NULL ) + { + for( BYTE idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + padPositions[idx] = e_BaseScene_Fullscreen; + } + } + + // If the game is not in split-screen, then display the primary pad at fullscreen + else if(app.GetLocalPlayerCount()<2) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + padPositions[primaryPad] = e_BaseScene_Fullscreen; + + // May need to turn off the player who just left + for( BYTE idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + DisplayGamertag(idx,FALSE); + } + } + + // We are in splitscreen so work out where each player should be + else + { + + for( BYTE idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(pMinecraft->localplayers[idx] != NULL) + { + if(pMinecraft->localplayers[idx]->m_iScreenSection==C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + DisplayGamertag(idx,FALSE); + } + else + { + DisplayGamertag(idx,TRUE); + } + + switch( pMinecraft->localplayers[idx]->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + padPositions[idx] = e_BaseScene_Fullscreen; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + padPositions[idx] = e_BaseScene_Top; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + padPositions[idx] = e_BaseScene_Bottom; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + padPositions[idx] = e_BaseScene_Left; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + padPositions[idx] = e_BaseScene_Right; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + padPositions[idx] = e_BaseScene_Top_Left; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + padPositions[idx] = e_BaseScene_Top_Right; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + padPositions[idx] = e_BaseScene_Bottom_Left; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + padPositions[idx] = e_BaseScene_Bottom_Right; + break; + } + } + else + { + padPositions[idx] = e_BaseScene_NotSet; + DisplayGamertag(idx,FALSE); + } + } + } + + return SetPlayerBasePositions(padPositions[0], padPositions[1], padPositions[2], padPositions[3]); +} + +CXuiSceneBase::EBaseScenePosition CXuiSceneBase::GetPlayerBasePosition(int iPad) +{ + return CXuiSceneBase::Instance->_GetPlayerBasePosition(iPad); +} + +void CXuiSceneBase::UpdateSelectedItemPos(int iPad) +{ + CXuiSceneBase::Instance->_UpdateSelectedItemPos(iPad); +} + +HXUIOBJ CXuiSceneBase::GetPlayerBaseScene(int iPad) +{ + return CXuiSceneBase::Instance->_GetPlayerBaseScene(iPad); +} + +HRESULT CXuiSceneBase::PlayUISFX(ESoundEffect eSound) +{ + return CXuiSceneBase::Instance->_PlayUISFX(eSound); +} + +void CXuiSceneBase::SetEmptyQuadrantLogo(int iScreenSection) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + EBaseScenePosition ePos=e_BaseScene_Top_Left; + int iPad; + // find the empty player + for( iPad = 0; iPad < XUSER_MAX_COUNT; ++iPad) + { + if(pMinecraft->localplayers[iPad] == NULL) + { + switch( iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + ePos = e_BaseScene_Top_Left; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + ePos = e_BaseScene_Top_Right; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + ePos = e_BaseScene_Bottom_Left; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + ePos = e_BaseScene_Bottom_Right; + break; + } + break; + } + } + + CXuiSceneBase::Instance->_SetEmptyQuadrantLogo(iPad,ePos); +} + +HRESULT CXuiSceneBase::DisplayGamertag( unsigned int iPad, BOOL bDisplay ) +{ + + CXuiSceneBase::Instance->_DisplayGamertag(iPad,bDisplay); + return S_OK; +} + +void CXuiSceneBase::SetSelectedItem( unsigned int iPad, const wstring &name) +{ + CXuiSceneBase::Instance->_SetSelectedItem(iPad,name); +} + +void CXuiSceneBase::HideAllGameUIElements() +{ + CXuiSceneBase::Instance->_HideAllGameUIElements(); +} + +bool CXuiSceneBase::GetBaseSceneSafeZone( unsigned int iPad, D3DXVECTOR2 &origin, float &width, float &height ) +{ + return CXuiSceneBase::Instance->_GetBaseSceneSafeZone(iPad,origin,width,height); +} + + +#ifndef _XBOX +void CXuiSceneBase::CreateBaseSceneInstance() +{ + CXuiSceneBase *sceneBase = new CXuiSceneBase(); + BOOL handled; + sceneBase->OnInit(NULL,handled); +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Base.h b/Minecraft.Client/Common/XUI/XUI_Scene_Base.h new file mode 100644 index 0000000..1a5b5d8 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Base.h @@ -0,0 +1,432 @@ +#pragma once + +#include "../media/xuiscene_base.h" +#include "XUI_Ctrl_SlotItem.h" +#include "XUI_CustomMessages.h" +#include "..\UI\UIEnums.h" +#include "..\..\..\Minecraft.World\SoundTypes.h" + +#define BUTTON_TOOLTIP_A 0 +#define BUTTON_TOOLTIP_B 1 +#define BUTTON_TOOLTIP_X 2 +#define BUTTON_TOOLTIP_Y 3 +#define BUTTON_TOOLTIP_LT 4 +#define BUTTON_TOOLTIP_RT 5 +#define BUTTON_TOOLTIP_LB 6 +#define BUTTON_TOOLTIP_RB 7 +#define BUTTON_TOOLTIP_LS 8 +#define BUTTONS_TOOLTIP_MAX 9 + +#define SFX_BACK 0 +#define SFX_CRAFT 1 +#define SFX_CRAFTFAIL 2 +#define SFX_FOCUS 3 +#define SFX_PRESS 4 +#define SFX_SCROLL 5 +#define SFX_MAX 6 + + +// This should be our target screen height and width +#define XUI_BASE_SCENE_WIDTH 1280.0f +#define XUI_BASE_SCENE_HEIGHT 720.0f + +#define XUI_BASE_SCENE_WIDTH_HALF 640.0f +#define XUI_BASE_SCENE_HEIGHT_HALF 360.0f +#define XUI_BASE_SCENE_WIDTH_QUARTER 320.0f +#define XUI_BASE_SCENE_HEIGHT_QUARTER 180.0f +#define SAFEZONE_HALF_HEIGHT 36.0f +#define SAFEZONE_HALF_WIDTH 64.0f + +// How much we scale each base for splitscreen (should be 0.5f) +#define XUI_BASE_SPLITSCREEN_SCALE 1.0f//0.5f // 4J-PB - TODO - move scenes instead + +// We make the tooltips bigger as they are unreadable when scaled by the above +#define XUI_BASE_SPLIT_TOOLTIPS_SCALE 1.0f//1.5f + +// The percentage of starting size that the tooltips grow by +#define XUI_BASE_SPLIT_TOOLTIPS_DIFF (XUI_BASE_SPLIT_TOOLTIPS_SCALE - 1.0f) + +class CXuiSceneBase : public CXuiSceneImpl +{ +public: + enum EBaseScenePosition + { + e_BaseScene_NotSet, + + // 1 player + e_BaseScene_Fullscreen, + + // 2 Player split-screen + e_BaseScene_Top, + e_BaseScene_Bottom, + e_BaseScene_Left, + e_BaseScene_Right, + + // 3/4 Player split-screen + e_BaseScene_Top_Left, + e_BaseScene_Top_Right, + e_BaseScene_Bottom_Left, + e_BaseScene_Bottom_Right, + }; + +protected: + static const int m_iTooltipSpacingGap=10; + static const int m_iTooltipSpacingGapSmall=5; + D3DXVECTOR3 m_vPosTextInTooltip[BUTTONS_TOOLTIP_MAX]; + D3DXVECTOR3 m_vPosTextInTooltipSmall[BUTTONS_TOOLTIP_MAX]; + D3DXVECTOR3 vLogoPosA[XUSER_MAX_COUNT]; + + // We have a group of these per player + CXuiScene m_BasePlayerScene[XUSER_MAX_COUNT]; + // Control and Element wrapper objects. + CXuiControl m_TooltipGroup[XUSER_MAX_COUNT]; + CXuiControl m_Buttons[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + CXuiControl m_TooltipGroupSmall[XUSER_MAX_COUNT]; + CXuiControl m_ButtonsSmall[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + CXuiControl m_bottomLeftAnchorPoint[XUSER_MAX_COUNT]; + CXuiControl m_topLeftAnchorPoint[XUSER_MAX_COUNT]; + CXuiControl m_SavingIcon; + CXuiControl m_Background[XUSER_MAX_COUNT]; + CXuiControl m_DarkOverlay[XUSER_MAX_COUNT]; + CXuiControl m_Logo[XUSER_MAX_COUNT]; + CXuiControl m_CrouchIcon[XUSER_MAX_COUNT]; + CXuiControl m_PressStart; + CXuiControl m_TrialTimer; + CXuiControl m_SafeArea; + CXuiControl m_BossHealthGroup[XUSER_MAX_COUNT]; + CXuiControl m_BossHealthText[XUSER_MAX_COUNT]; + CXuiProgressBar *m_pBossHealthProgress; + CXuiProgressBar m_BossHealthProgress1[XUSER_MAX_COUNT]; + CXuiProgressBar m_BossHealthProgress2[XUSER_MAX_COUNT]; + CXuiProgressBar m_BossHealthProgress3[XUSER_MAX_COUNT]; + CXuiProgressBar m_BossHealthProgress1_small[XUSER_MAX_COUNT]; + CXuiProgressBar m_BossHealthProgress2_small[XUSER_MAX_COUNT]; + CXuiProgressBar m_BossHealthProgress3_small[XUSER_MAX_COUNT]; + int m_ticksWithNoBoss; + CXuiSound m_SFXA[SFX_MAX]; + HXUIOBJ m_hEmptyQuadrantLogo; + HXUIOBJ m_hGamerTagA[XUSER_MAX_COUNT]; + CXuiControl m_selectedItemA[XUSER_MAX_COUNT]; + CXuiControl m_selectedItemSmallA[XUSER_MAX_COUNT]; + + BOOL m_visible[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + BOOL m_bossHealthVisible[XUSER_MAX_COUNT]; + int m_iWrongTexturePackTickC; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_SKIN_CHANGED( OnSkinChanged ) +// XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) +// XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiPressStartMessage, m_PressStart) + MAP_CONTROL(IDC_XuiTrialTimer, m_TrialTimer) + MAP_CONTROL(IDC_XuiSavingIcon, m_SavingIcon) + MAP_CONTROL(IDC_SafeArea, m_SafeArea) + MAP_CONTROL(IDC_XuiSoundXACTBack, m_SFXA[SFX_BACK]) + MAP_CONTROL(IDC_XuiSoundXACTCraft,m_SFXA[SFX_CRAFT]) + MAP_CONTROL(IDC_XuiSoundXACTCraftFail,m_SFXA[SFX_CRAFTFAIL]) + MAP_CONTROL(IDC_XuiSoundXACTPress,m_SFXA[SFX_PRESS]) + MAP_CONTROL(IDC_XuiSoundXACTFocus,m_SFXA[SFX_FOCUS]) + MAP_CONTROL(IDC_XuiSoundXACTScroll,m_SFXA[SFX_SCROLL]) + + //MAP_CONTROL(IDC_BossHealth, m_BossHealthGroup) + //BEGIN_MAP_CHILD_CONTROLS(m_BossHealthGroup) + // MAP_CONTROL(IDC_TitleText, m_BossHealthText) + // MAP_CONTROL(IDC_ProgressBar, m_BossHealthProgress) + //END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_BasePlayer0, m_BasePlayerScene[0]) + BEGIN_MAP_CHILD_CONTROLS(m_BasePlayerScene[0]) + MAP_CONTROL(IDC_BottomLeftAnchorPoint, m_bottomLeftAnchorPoint[0]) + MAP_CONTROL(IDC_TopLeftAnchorPoint, m_topLeftAnchorPoint[0]) + MAP_CONTROL(IDC_Tooltips, m_TooltipGroup[0]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroup[0]) + MAP_CONTROL(IDC_AButton, m_Buttons[0][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_Buttons[0][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_Buttons[0][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_Buttons[0][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_Buttons[0][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_Buttons[0][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_Buttons[0][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_Buttons[0][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_Buttons[0][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TooltipsSmall, m_TooltipGroupSmall[0]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroupSmall[0]) + MAP_CONTROL(IDC_AButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_ButtonsSmall[0][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_ButtonsSmall[0][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_ButtonsSmall[0][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_ButtonsSmall[0][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Background, m_Background[0]) + MAP_CONTROL(IDC_XuiDarkOverlay, m_DarkOverlay[0]) + MAP_CONTROL(IDC_Logo, m_Logo[0]) + MAP_CONTROL(IDC_SelectedItem, m_selectedItemA[0]) + MAP_CONTROL(IDC_SelectedItemSmall, m_selectedItemSmallA[0]) + MAP_CONTROL(IDC_BossHealth, m_BossHealthGroup[0]) + BEGIN_MAP_CHILD_CONTROLS(m_BossHealthGroup[0]) + MAP_CONTROL(IDC_TitleText, m_BossHealthText[0]) + MAP_CONTROL(IDC_ProgressBar1, m_BossHealthProgress1[0]) + MAP_CONTROL(IDC_ProgressBar2, m_BossHealthProgress2[0]) + MAP_CONTROL(IDC_ProgressBar3, m_BossHealthProgress3[0]) + MAP_CONTROL(IDC_ProgressBar1_small, m_BossHealthProgress1_small[0]) + MAP_CONTROL(IDC_ProgressBar2_small, m_BossHealthProgress2_small[0]) + MAP_CONTROL(IDC_ProgressBar3_small, m_BossHealthProgress3_small[0]) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_BasePlayer1, m_BasePlayerScene[1]) + BEGIN_MAP_CHILD_CONTROLS(m_BasePlayerScene[1]) + MAP_CONTROL(IDC_BottomLeftAnchorPoint, m_bottomLeftAnchorPoint[1]) + MAP_CONTROL(IDC_TopLeftAnchorPoint, m_topLeftAnchorPoint[1]) + MAP_CONTROL(IDC_Tooltips, m_TooltipGroup[1]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroup[1]) + MAP_CONTROL(IDC_AButton, m_Buttons[1][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_Buttons[1][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_Buttons[1][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_Buttons[1][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_Buttons[1][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_Buttons[1][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_Buttons[1][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_Buttons[1][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_Buttons[1][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TooltipsSmall, m_TooltipGroupSmall[1]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroupSmall[1]) + MAP_CONTROL(IDC_AButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_ButtonsSmall[1][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_ButtonsSmall[1][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_ButtonsSmall[1][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_ButtonsSmall[1][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Background, m_Background[1]) + MAP_CONTROL(IDC_XuiDarkOverlay, m_DarkOverlay[1]) + MAP_CONTROL(IDC_Logo, m_Logo[1]) + MAP_CONTROL(IDC_SelectedItem, m_selectedItemA[1]) + MAP_CONTROL(IDC_SelectedItemSmall, m_selectedItemSmallA[1]) + MAP_CONTROL(IDC_BossHealth, m_BossHealthGroup[1]) + BEGIN_MAP_CHILD_CONTROLS(m_BossHealthGroup[1]) + MAP_CONTROL(IDC_TitleText, m_BossHealthText[1]) + MAP_CONTROL(IDC_ProgressBar1, m_BossHealthProgress1[1]) + MAP_CONTROL(IDC_ProgressBar2, m_BossHealthProgress2[1]) + MAP_CONTROL(IDC_ProgressBar3, m_BossHealthProgress3[1]) + MAP_CONTROL(IDC_ProgressBar1_small, m_BossHealthProgress1_small[1]) + MAP_CONTROL(IDC_ProgressBar2_small, m_BossHealthProgress2_small[1]) + MAP_CONTROL(IDC_ProgressBar3_small, m_BossHealthProgress3_small[1]) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_BasePlayer2, m_BasePlayerScene[2]) + BEGIN_MAP_CHILD_CONTROLS(m_BasePlayerScene[2]) + MAP_CONTROL(IDC_BottomLeftAnchorPoint, m_bottomLeftAnchorPoint[2]) + MAP_CONTROL(IDC_TopLeftAnchorPoint, m_topLeftAnchorPoint[2]) + MAP_CONTROL(IDC_Tooltips, m_TooltipGroup[2]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroup[2]) + MAP_CONTROL(IDC_AButton, m_Buttons[2][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_Buttons[2][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_Buttons[2][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_Buttons[2][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_Buttons[2][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_Buttons[2][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_Buttons[2][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_Buttons[2][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_Buttons[2][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TooltipsSmall, m_TooltipGroupSmall[2]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroupSmall[2]) + MAP_CONTROL(IDC_AButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_ButtonsSmall[2][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_ButtonsSmall[2][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_ButtonsSmall[2][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_ButtonsSmall[2][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Background, m_Background[2]) + MAP_CONTROL(IDC_XuiDarkOverlay, m_DarkOverlay[2]) + MAP_CONTROL(IDC_Logo, m_Logo[2]) + MAP_CONTROL(IDC_SelectedItem, m_selectedItemA[2]) + MAP_CONTROL(IDC_SelectedItemSmall, m_selectedItemSmallA[2]) + MAP_CONTROL(IDC_BossHealth, m_BossHealthGroup[2]) + BEGIN_MAP_CHILD_CONTROLS(m_BossHealthGroup[2]) + MAP_CONTROL(IDC_TitleText, m_BossHealthText[2]) + MAP_CONTROL(IDC_ProgressBar1, m_BossHealthProgress1[2]) + MAP_CONTROL(IDC_ProgressBar2, m_BossHealthProgress2[2]) + MAP_CONTROL(IDC_ProgressBar3, m_BossHealthProgress3[2]) + MAP_CONTROL(IDC_ProgressBar1_small, m_BossHealthProgress1_small[2]) + MAP_CONTROL(IDC_ProgressBar2_small, m_BossHealthProgress2_small[2]) + MAP_CONTROL(IDC_ProgressBar3_small, m_BossHealthProgress3_small[2]) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_BasePlayer3, m_BasePlayerScene[3]) + BEGIN_MAP_CHILD_CONTROLS(m_BasePlayerScene[3]) + MAP_CONTROL(IDC_BottomLeftAnchorPoint, m_bottomLeftAnchorPoint[3]) + MAP_CONTROL(IDC_TopLeftAnchorPoint, m_topLeftAnchorPoint[3]) + MAP_CONTROL(IDC_Tooltips, m_TooltipGroup[3]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroup[3]) + MAP_CONTROL(IDC_AButton, m_Buttons[3][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_Buttons[3][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_Buttons[3][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_Buttons[3][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_Buttons[3][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_Buttons[3][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_Buttons[3][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_Buttons[3][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_Buttons[3][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_TooltipsSmall, m_TooltipGroupSmall[3]) + BEGIN_MAP_CHILD_CONTROLS(m_TooltipGroupSmall[3]) + MAP_CONTROL(IDC_AButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_A]) + MAP_CONTROL(IDC_BButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_B]) + MAP_CONTROL(IDC_XButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_X]) + MAP_CONTROL(IDC_YButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_Y]) + MAP_CONTROL(IDC_LTrigger, m_ButtonsSmall[3][BUTTON_TOOLTIP_LT]) + MAP_CONTROL(IDC_RTrigger, m_ButtonsSmall[3][BUTTON_TOOLTIP_RT]) + MAP_CONTROL(IDC_RBButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_RB]) + MAP_CONTROL(IDC_LBButton, m_ButtonsSmall[3][BUTTON_TOOLTIP_LB]) + MAP_CONTROL(IDC_LStick, m_Buttons[3][BUTTON_TOOLTIP_LS]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Background, m_Background[3]) + MAP_CONTROL(IDC_XuiDarkOverlay, m_DarkOverlay[3]) + MAP_CONTROL(IDC_Logo, m_Logo[3]) + MAP_CONTROL(IDC_SelectedItem, m_selectedItemA[3]) + MAP_CONTROL(IDC_SelectedItemSmall, m_selectedItemSmallA[3]) + MAP_CONTROL(IDC_BossHealth, m_BossHealthGroup[3]) + BEGIN_MAP_CHILD_CONTROLS(m_BossHealthGroup[3]) + MAP_CONTROL(IDC_TitleText, m_BossHealthText[3]) + MAP_CONTROL(IDC_ProgressBar1, m_BossHealthProgress1[3]) + MAP_CONTROL(IDC_ProgressBar2, m_BossHealthProgress2[3]) + MAP_CONTROL(IDC_ProgressBar3, m_BossHealthProgress3[3]) + MAP_CONTROL(IDC_ProgressBar1_small, m_BossHealthProgress1_small[3]) + MAP_CONTROL(IDC_ProgressBar2_small, m_BossHealthProgress2_small[3]) + MAP_CONTROL(IDC_ProgressBar3_small, m_BossHealthProgress3_small[3]) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + HRESULT OnSkinChanged(BOOL& bHandled); +// HRESULT OnCustomMessage_DLCInstalled(); +// HRESULT OnCustomMessage_DLCMountingComplete(); + +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneBase, L"CXuiSceneBase", XUI_CLASS_SCENE ) + +private: + void _TickAllBaseScenes(); + HRESULT _SetTooltipText( unsigned int iPad, unsigned int tooltip, int iTextID ); + HRESULT _SetEnableTooltips( unsigned int iPad, BOOL bVal ); + HRESULT _ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ); + HRESULT _SetTooltipsEnabled( unsigned int iPad, bool bA = true, bool bB = true, bool bX = true, bool bY = true, bool bLT = true, bool bRT = true, bool bLB=true, bool bRB = true, bool bLS = true); + HRESULT _RefreshTooltips( unsigned int iPad); + HRESULT _EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ); + HRESULT _ShowSavingMessage( unsigned int iPad, C4JStorage::ESavingMessage eVal ); + HRESULT _ShowBackground( unsigned int iPad, BOOL bShow ); + HRESULT _ShowDarkOverlay( unsigned int iPad, BOOL bShow ); + HRESULT _ShowLogo( unsigned int iPad, BOOL bShow ); + HRESULT _ShowPressStart(unsigned int iPad); + HRESULT _UpdateAutosaveCountdownTimer(unsigned int uiSeconds); + HRESULT _ShowAutosaveCountdownTimer(BOOL bVal); + HRESULT _UpdateTrialTimer(unsigned int iPad); + HRESULT _ShowTrialTimer(BOOL bVal); + void _ReduceTrialTimerValue(); + HRESULT _HidePressStart(); + HRESULT _ShowSafeArea( BOOL bShow ); + HRESULT _ShowOtherPlayersBaseScene(int iPad, bool show); + bool _PressStartPlaying(unsigned int iPad); + HRESULT _SetPlayerBaseScenePosition( unsigned int iPad, EBaseScenePosition position ); + void _UpdateSelectedItemPos( unsigned int iPad); + EBaseScenePosition _GetPlayerBasePosition(int iPad); + HRESULT _AnimateKeyPress(DWORD userIndex, DWORD dwKeyCode); + HXUIOBJ _GetPlayerBaseScene(int iPad) {return m_BasePlayerScene[iPad].m_hObj;} + HRESULT _PlayUISFX(ESoundEffect eSound); + void _SetEmptyQuadrantLogo(int iPad,EBaseScenePosition ePos); + HRESULT _DisplayGamertag( unsigned int iPad, BOOL bDisplay ); + void _SetSelectedItem( unsigned int iPad, const wstring& name); + void _HideAllGameUIElements(); + bool _GetBaseSceneSafeZone( unsigned int iPad, D3DXVECTOR2 &origin, float &width, float &height); + + void ReLayout( unsigned int iPad ); + +private: + static CXuiSceneBase *Instance; + int m_iCurrentTooltipTextID[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + HXUIOBJ hTooltipText[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + HXUIOBJ hTooltipTextSmall[XUSER_MAX_COUNT][BUTTONS_TOOLTIP_MAX]; + EBaseScenePosition m_playerBaseScenePosition[XUSER_MAX_COUNT]; + bool m_bCrouching[XUSER_MAX_COUNT]; + int m_iQuadrantsMask; + unsigned int m_uiSelectedItemOpacityCountDown[XUSER_MAX_COUNT]; + +public: + static DWORD m_dwTrialTimerLimitSecs; + +public: + static CXuiSceneBase *GetInstance() { return Instance; } + static void TickAllBaseScenes(); + static HRESULT SetTooltipText( unsigned int iPad, unsigned int tooltip, int iTextID ); + static HRESULT SetEnableTooltips( unsigned int iPad, BOOL bVal ); + static HRESULT ShowTooltip( unsigned int iPad, unsigned int tooltip, bool show ); + static HRESULT SetTooltips( unsigned int iPad, int iA, int iB=-1, int iX=-1, int iY=-1 , int iLT=-1, int iRT=-1, int iLB=-1, int iRB=-1, int iLS=-1, bool forceUpdate = false); + static HRESULT RefreshTooltips( unsigned int iPad); + static HRESULT EnableTooltip( unsigned int iPad, unsigned int tooltip, bool enable ); + static HRESULT SetTooltipsEnabled( unsigned int iPad, bool bA = true, bool bB = true, bool bX = true, bool bY = true, bool bLT = true, bool bRT = true, bool bLB = true, bool bRB=true, bool bLS=true); + static HRESULT AnimateKeyPress(DWORD userIndex, DWORD dwKeyCode); + static HRESULT ShowSavingMessage( unsigned int iPad, C4JStorage::ESavingMessage eVal); + static HRESULT ShowBackground( unsigned int iPad, BOOL bShow ); + static HRESULT ShowDarkOverlay( unsigned int iPad, BOOL bShow ); + static HRESULT ShowLogo( unsigned int iPad, BOOL bShow ); + static HRESULT UpdateAutosaveCountdownTimer(unsigned int uiSeconds); + static HRESULT ShowAutosaveCountdownTimer(BOOL bVal); + static HRESULT UpdateTrialTimer(unsigned int iPad); + static HRESULT ShowTrialTimer(BOOL bVal); + static void ReduceTrialTimerValue(); + static HRESULT HidePressStart(); + static HRESULT ShowSafeArea( BOOL bShow ); + static HRESULT ShowOtherPlayersBaseScene(int iPad, bool show); + + static HRESULT ShowPressStart(unsigned int iPad); + static bool PressStartPlaying(unsigned int iPad); + static HRESULT SetPlayerBaseScenePosition( unsigned int iPad, EBaseScenePosition position ); + static HRESULT SetPlayerBasePositions(EBaseScenePosition pad0, EBaseScenePosition pad1, EBaseScenePosition pad2, EBaseScenePosition pad3); + static HRESULT UpdatePlayerBasePositions(); + static EBaseScenePosition GetPlayerBasePosition(int iPad); + static void UpdateSelectedItemPos(int iPad); + + + static HXUIOBJ GetPlayerBaseScene(int iPad); + static HRESULT PlayUISFX(ESoundEffect eSound); + static void SetEmptyQuadrantLogo(int iSection); + static HRESULT DisplayGamertag( unsigned int iPad, BOOL bDisplay ); + static void SetSelectedItem( unsigned int iPad, const wstring &name); + static void HideAllGameUIElements(); + + // Returns details on the fully transformed (ie screen space) base scene position, adjusted for safe zones + static bool GetBaseSceneSafeZone( unsigned int iPad, D3DXVECTOR2 &origin, float &width, float &height); + +#ifndef _XBOX + static void CreateBaseSceneInstance(); +#endif +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.cpp new file mode 100644 index 0000000..cf60096 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.cpp @@ -0,0 +1,156 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_BrewingStand.h" +#include "XUI_Ctrl_BrewProgress.h" +#include "XUI_Ctrl_BubblesProgress.h" + + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneBrewingStand::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneBrewingStand::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_BrewingStandText,app.GetString(IDS_BREWING_STAND)); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + BrewingScreenInput* initData = (BrewingScreenInput*)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Brewing_Menu, this); + } +#endif + + BrewingStandMenu* menu = new BrewingStandMenu( initData->inventory, initData->brewingStand ); + + + InitDataAssociations(m_iPad, menu); + + m_progressControl->SetUserData( initData->brewingStand.get() ); + + m_bubbleProgress->SetUserData( initData->brewingStand.get() ); + + delete initData; + + CXuiSceneAbstractContainer::Initialize( m_iPad, menu, true, BrewingStandMenu::INV_SLOT_START, eSectionBrewingUsing, eSectionBrewingMax ); + + //app.SetRichPresenceContextValue(m_iPad,CONTEXT_GAME_STATE_FORGING); + + return S_OK; +} + +HRESULT CXuiSceneBrewingStand::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +CXuiControl* CXuiSceneBrewingStand::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionBrewingBottle1: + return (CXuiControl *)m_bottle1Control; + break; + case eSectionBrewingBottle2: + return (CXuiControl *)m_bottle2Control; + break; + case eSectionBrewingBottle3: + return (CXuiControl *)m_bottle3Control; + break; + case eSectionBrewingIngredient: + return (CXuiControl *)m_ingredientControl; + break; + case eSectionBrewingInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionBrewingUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneBrewingStand::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionBrewingBottle1: + return m_bottle1Control; + break; + case eSectionBrewingBottle2: + return m_bottle2Control; + break; + case eSectionBrewingBottle3: + return m_bottle3Control; + break; + case eSectionBrewingIngredient: + return m_ingredientControl; + break; + case eSectionBrewingInventory: + return m_inventoryControl; + break; + case eSectionBrewingUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneBrewingStand::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + // TODO Inventory dimensions need defined as constants + m_ingredientControl->SetData( iPad, menu, 1, 1, BrewingStandMenu::INGREDIENT_SLOT ); + + m_bottle1Control->SetData( iPad, menu, 1, 1, BrewingStandMenu::BOTTLE_SLOT_START ); + m_bottle2Control->SetData( iPad, menu, 1, 1, BrewingStandMenu::BOTTLE_SLOT_START + 1); + m_bottle3Control->SetData( iPad, menu, 1, 1, BrewingStandMenu::BOTTLE_SLOT_START + 2); + + //m_litProgressControl->SetUserData( initData->furnace.get() ); + + //m_burnProgress->SetUserData( initData->furnace.get() ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, BrewingStandMenu::INV_SLOT_START); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.h b/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.h new file mode 100644 index 0000000..370b6b1 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_BrewingStand.h @@ -0,0 +1,75 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_brewingstand.h" +#include "XUI_Scene_AbstractContainer.h" +#include "..\UI\IUIScene_BrewingMenu.h" + +class CXuiCtrlSlotList; +class CXuiCtrlBrewProgress; +class CXuiCtrlBubblesProgress; + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneBrewingStand : public CXuiSceneAbstractContainer, public IUIScene_BrewingMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneBrewingStand, L"CXuiSceneBrewingStand", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Ingredient, m_ingredientControl) + MAP_OVERRIDE(IDC_Bottle1, m_bottle1Control) + MAP_OVERRIDE(IDC_Bottle2, m_bottle2Control) + MAP_OVERRIDE(IDC_Bottle3, m_bottle3Control) + + MAP_OVERRIDE(IDC_Progress, m_progressControl) + MAP_OVERRIDE(IDC_Bubbles, m_bubbleProgress) + MAP_CONTROL(IDC_BrewingStandText,m_BrewingStandText) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); +// HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + +private: + CXuiCtrlSlotList *m_ingredientControl; + CXuiCtrlSlotList *m_bottle1Control; + CXuiCtrlSlotList *m_bottle2Control; + CXuiCtrlSlotList *m_bottle3Control; + + CXuiCtrlBrewProgress *m_progressControl; + CXuiCtrlBubblesProgress *m_bubbleProgress; + CXuiControl m_BrewingStandText; + + CXuiControl m_sceneGroup; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Container.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Container.cpp new file mode 100644 index 0000000..39b836d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Container.cpp @@ -0,0 +1,162 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\Container.h" +#include "..\..\..\Minecraft.World\ContainerMenu.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_Container.h" +#include "XUI_Ctrl_SlotItemListItem.h" +#include "XUI_Ctrl_SlotItem.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" + +// The height of one row of slots +//#define ROW_HEIGHT 42.0f - comes from the pointer height in the xui + +// The number of container rows that are visible in the Xui file at it's default size +#define CONTAINER_DEFAULT_ROWS 3 + + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneContainer::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneContainer::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + D3DXVECTOR3 vec; + MapChildControls(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + ContainerScreenInput* initData = (ContainerScreenInput*)pInitData->pvInitData; + + XuiControlSetText(m_ChestText,app.GetString(initData->container->getName())); + + ContainerMenu* menu = new ContainerMenu( initData->inventory, initData->container ); + + shared_ptr container = initData->container; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + +#ifdef _XBOX + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Container_Menu, this); + } +#endif + + // if we are in splitscreen, then we need to figure out if we want to move this scene + int rows = container->getContainerSize() / 9; + // use the pointer size in the xui to set the row height + float fPointerWidth,fPointerHeight; + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + m_pointerControl->GetBounds(&fPointerWidth, &fPointerHeight); + + // Adjust the height to show the correct number of container rows + float height, width; + this->GetBounds( &width, &height ); + int rowDiff = CONTAINER_DEFAULT_ROWS - rows; + //height = height - (rowDiff * ROW_HEIGHT); + height = height - (rowDiff * fPointerHeight); + this->SetBounds( width, height ); + + // Update the position after the height change so that we are still centred + D3DXVECTOR3 vPos; + this->GetPosition( &vPos ); + vPos.y = vPos.y + ( (rowDiff * fPointerHeight) / 2 ); + // Make sure that the y offset is even for SD modes, as the y in xui coordinates will end up being scaled by a factor of 1.5 + // to get it into actual back buffer coordinates, and we need those to remain whole numbers to avoid issues with point sampling + if(!RenderManager.IsHiDef()) + { + int iY = (int)(vPos.y); + iY &= 0xfffffffe; + vPos.y = (float)iY; + } + this->SetPosition( &vPos ); + + InitDataAssociations(initData->iPad, menu); + + CXuiSceneAbstractContainer::Initialize( initData->iPad, menu, true, container->getContainerSize(), eSectionContainerUsing, eSectionContainerMax ); + + delete initData; + + return S_OK; +} + +HRESULT CXuiSceneContainer::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +CXuiControl* CXuiSceneContainer::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionContainerChest: + return (CXuiControl *)m_containerControl; + break; + case eSectionContainerInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionContainerUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneContainer::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionContainerChest: + return m_containerControl; + break; + case eSectionContainerInventory: + return m_inventoryControl; + break; + case eSectionContainerUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneContainer::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + int containerSize = menu->getSize() - (27 + 9); + int rows = containerSize / 9; + + // TODO Inventory dimensions need defined as constants + m_containerControl->SetData( iPad, menu, rows, 9, 0 ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, containerSize); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Container.h b/Minecraft.Client/Common/XUI/XUI_Scene_Container.h new file mode 100644 index 0000000..475a697 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Container.h @@ -0,0 +1,57 @@ +#pragma once +#include "..\Media\xuiscene_container.h" +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_CustomMessages.h" +#include "..\UI\IUIScene_ContainerMenu.h" + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneContainer : public CXuiSceneAbstractContainer, public IUIScene_ContainerMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneContainer, L"CXuiSceneContainer", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + // Common to all abstract container scenes + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Container, m_containerControl) + MAP_CONTROL(IDC_ChestText,m_ChestText) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + //HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) { return S_OK;} + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + +private: + CXuiCtrlSlotList* m_containerControl; + CXuiControl m_sceneGroup; + CXuiControl m_ChestText; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.cpp new file mode 100644 index 0000000..a5dc658 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.cpp @@ -0,0 +1,636 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.crafting.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\Tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "XUI_Ctrl_SlotList.h" + +#define IGNORE_KEYPRESS_TIMERID 0 +#define IGNORE_KEYPRESS_TIME 100 + +////////////////////////////////////////////////////////////////////////// +// +// +// +////////////////////////////////////////////////////////////////////////// +CXuiSceneCraftingPanel::CXuiSceneCraftingPanel() +{ +} + +////////////////////////////////////////////////////////////////////////// +// +// OnInit +// +////////////////////////////////////////////////////////////////////////// +HRESULT CXuiSceneCraftingPanel::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_bIgnoreKeyPresses=true; + + D3DXVECTOR3 vec; + VOID *pObj; + CraftingPanelScreenInput* pCraftingPanelData = (CraftingPanelScreenInput*)pInitData->pvInitData; + m_iContainerType=pCraftingPanelData->iContainerType; + m_pPlayer=pCraftingPanelData->player; + m_iPad=pCraftingPanelData->iPad; + m_bSplitscreen=pCraftingPanelData->bSplitscreen; + + HRESULT hr = S_OK; + + MapChildControls(); + + if(m_iContainerType==RECIPE_TYPE_2x2) + { + // TODO Inventory dimensions need defined as constants + m_inventoryControl->SetData( m_iPad, m_pPlayer->inventoryMenu, 3, 9, InventoryMenu::INV_SLOT_START, InventoryMenu::INV_SLOT_END ); + + // TODO Inventory dimensions need defined as constants + m_useRowControl->SetData( m_iPad, m_pPlayer->inventoryMenu, 1, 9, InventoryMenu::USE_ROW_SLOT_START, InventoryMenu::USE_ROW_SLOT_END ); + } + else + { + CraftingMenu *menu = new CraftingMenu(m_pPlayer->inventory, m_pPlayer->level, pCraftingPanelData->x, pCraftingPanelData->y, pCraftingPanelData->z); + Minecraft::GetInstance()->localplayers[m_iPad]->containerMenu = menu; + // TODO Inventory dimensions need defined as constants + m_inventoryControl->SetData( m_iPad, menu, 3, 9, CraftingMenu::INV_SLOT_START, CraftingMenu::INV_SLOT_END ); + + // TODO Inventory dimensions need defined as constants + m_useRowControl->SetData( m_iPad, menu, 1, 9, CraftingMenu::USE_ROW_SLOT_START, CraftingMenu::USE_ROW_SLOT_END ); + } + + delete pCraftingPanelData; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + XuiElementSetShow(m_hGrid,TRUE); + XuiElementSetShow(m_hPanel,TRUE); + + // Set up the CXuiCtrlCraftIngredientSlots + if(m_iContainerType==RECIPE_TYPE_3x3) + { + m_pCursors=m_pHSlotsCraftingTableCursors; + + m_iIngredientsMaxSlotC = m_iIngredients3x3SlotC; + for(int i=0;iGetPosition(&m_vSlot0Pos); + m_pHSlotsBrushImageControl[1]->GetPosition(&vec); + m_fSlotSize=vec.x-m_vSlot0Pos.x; + + // store the slot 0 highlight position + m_hHighlight.GetPosition(&m_vSlot0HighlightPos); + // Store the V slot position + m_hScrollBar2.GetPosition(&m_vSlot0V2ScrollPos); + m_hScrollBar3.GetPosition(&m_vSlot0V3ScrollPos); + + // get the position of the slot from the xui, and apply any offset needed + for(int i=0;iSetShow(FALSE); + } + + XuiElementSetShow(m_hGridInventory,FALSE); + + m_hScrollBar2.SetShow(FALSE); + m_hScrollBar3.SetShow(FALSE); + + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_CRAFTING); + m_GroupName.SetText(GetGroupNameText(m_pGroupA[m_iGroupIndex])); + + UpdateTooltips(); + + // Update the tutorial state + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + if(m_iContainerType==RECIPE_TYPE_2x2) + { + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_2x2Crafting_Menu, this); + } + else + { + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_3x3Crafting_Menu, this); + } + } +#endif + + XuiSetTimer(m_hObj,IGNORE_KEYPRESS_TIMERID,IGNORE_KEYPRESS_TIME); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////// +// +// OnTransitionEnd +// +////////////////////////////////////////////////////////////////////////// +HRESULT CXuiSceneCraftingPanel::OnTransitionEnd( XUIMessageTransition *pTransData, BOOL& bHandled ) +{ + // are we being destroyed? If so, don't do anything + if(pTransData->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) + { + return S_OK; + } + + // Fix for issue caused by autosave while crafting is up + if(pTransData->dwTransType == XUI_TRANSITION_TO || pTransData->dwTransType == XUI_TRANSITION_BACKTO) + { + // Update the tab icons + if(m_iContainerType==RECIPE_TYPE_3x3) + { + for(int i=0;iUserIndex, mapVKToAction(pInputData->dwKeyCode), (pInputData->dwFlags & XUI_INPUT_FLAG_REPEAT) != 0); + + return S_OK; +} + +int CXuiSceneCraftingPanel::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: + case VK_PAD_LTHUMB_LEFT: + action = ACTION_MENU_LEFT; + break; + case VK_PAD_DPAD_RIGHT: + case VK_PAD_LTHUMB_RIGHT: + action = ACTION_MENU_RIGHT; + break; + case VK_PAD_LTHUMB_UP: + case VK_PAD_DPAD_UP: + action = ACTION_MENU_UP; + break; + case VK_PAD_LTHUMB_DOWN: + 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; +} + +////////////////////////////////////////////////////////////////////////// +// +// OnGetSourceImage +// +////////////////////////////////////////////////////////////////////////// +HRESULT CXuiSceneCraftingPanel::OnGetSourceImage(XUIMessageGetSourceImage* pData, BOOL& rfHandled) +{ + HRESULT hr = S_OK; + //int iId=pData->iItem; + int iId=(pData->iData>>22)&0x1FF; + pData->szPath = NULL; + pData->bDirty=true; + rfHandled = TRUE; + return hr; +} + +////////////////////////////////////////////////////////////////////////// +// +// OnDestroy +// +////////////////////////////////////////////////////////////////////////// +HRESULT CXuiSceneCraftingPanel::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + + return S_OK; +} + +HRESULT CXuiSceneCraftingPanel::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +HRESULT CXuiSceneCraftingPanel::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if(pData->nId==IGNORE_KEYPRESS_TIMERID) + { + XuiKillTimer(m_hObj,IGNORE_KEYPRESS_TIMERID); + m_bIgnoreKeyPresses=false; + } + + return S_OK; +} + +HRESULT CXuiSceneCraftingPanel::OnKillFocus(HXUIOBJ hObjGettingFocus, BOOL& bHandled) +{ + return S_OK; +} + +int CXuiSceneCraftingPanel::getPad() +{ + return m_iPad; +} + +void CXuiSceneCraftingPanel::hideAllHSlots() +{ + for(int i=0;iSetShow(FALSE); + } +} + +void CXuiSceneCraftingPanel::hideAllVSlots() +{ + for(int i=0;iSetShow(FALSE); + } +} + + +void CXuiSceneCraftingPanel::hideAllIngredientsSlots() +{ + for(int i=0;iSetShow(FALSE); + } +} + +void CXuiSceneCraftingPanel::setCraftHSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha) +{ + m_pHSlotsBrushImageControl[iIndex]->SetIcon(iPad, item, 9, uiAlpha, false); + //m_pHSlotsBrushImageControl[iIndex]->SetPassThroughDataAssociation(MAKE_SLOTDISPLAY_ITEM_BITMASK(item->id,item->getAuxValue(),item->isFoil()),MAKE_SLOTDISPLAY_DATA_BITMASK (iPad, uiAlpha, false, item->GetCount(), 9,0) ); + //m_pHSlotsBrushImageControl[iIndex]->SetShow(TRUE); +} + +void CXuiSceneCraftingPanel::setCraftVSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha) +{ + m_pVSlotsBrushImageControl[iIndex]->SetIcon(iPad, item, 9, uiAlpha, false); + //m_pVSlotsBrushImageControl[iIndex]->SetPassThroughDataAssociation(MAKE_SLOTDISPLAY_ITEM_BITMASK(item->id,item->getAuxValue(),item->isFoil()),MAKE_SLOTDISPLAY_DATA_BITMASK (iPad, uiAlpha, false, item->GetCount(), 9,0) ); + //m_pVSlotsBrushImageControl[iIndex]->SetShow(TRUE); +} + +void CXuiSceneCraftingPanel::setCraftingOutputSlotItem(int iPad, shared_ptr item) +{ + if(item == NULL) + { + m_pCraftingOutput->SetIcon(iPad, 0,0,0,0,0,false); + } + else + { + m_pCraftingOutput->SetIcon(iPad, item->id,item->getAuxValue(),item->GetCount(),12,31,true,item->isFoil()); + } +} + +void CXuiSceneCraftingPanel::setCraftingOutputSlotRedBox(bool show) +{ + m_pCraftingOutput->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneCraftingPanel::setIngredientSlotItem(int iPad, int index, shared_ptr item) +{ + if(item == NULL) + { + m_pCraftingIngredientA[index]->SetIcon(iPad, 0,0,0,0,0,false); + } + else + { + m_pCraftingIngredientA[index]->SetIcon(m_iPad, item->id,item->getAuxValue(),0,8,31,false); + } +} + +void CXuiSceneCraftingPanel::setIngredientSlotRedBox(int index, bool show) +{ + m_pCraftingIngredientA[index]->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneCraftingPanel::setIngredientDescriptionItem(int iPad, int index, shared_ptr item) +{ + m_pCraftIngredientDescA[index]->SetIcon(iPad, item->id,item->getAuxValue(),item->GetCount(),8,31,TRUE,item->isFoil(),FALSE); +} + +void CXuiSceneCraftingPanel::setIngredientDescriptionRedBox(int index, bool show) +{ + m_pCraftIngredientDescA[index]->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneCraftingPanel::setIngredientDescriptionText(int index, LPCWSTR text) +{ + m_pCraftIngredientDescA[index]->SetDescription(text); +} + +void CXuiSceneCraftingPanel::setShowCraftHSlot(int iIndex, bool show) +{ + m_pHSlotsBrushImageControl[iIndex]->SetShow(show?TRUE:FALSE); +} + +void CXuiSceneCraftingPanel::showTabHighlight(int iIndex, bool show) +{ + m_hTabGroupA[iIndex].SetShow(show?TRUE:FALSE); +} + +void CXuiSceneCraftingPanel::setGroupText(LPCWSTR text) +{ + m_GroupName.SetText(text); +} + +void CXuiSceneCraftingPanel::setDescriptionText(LPCWSTR text) +{ + m_DescriptionText.SetText(text); +} + +void CXuiSceneCraftingPanel::setItemText(LPCWSTR text) +{ + m_ItemName.SetText(text); +} + +void CXuiSceneCraftingPanel::UpdateMultiPanel() +{ + switch(m_iDisplayDescription) + { + case DISPLAY_INVENTORY: + // turn off all the ingredients display + for(int i=0;i<4;i++) + { + m_pCraftIngredientDescA[i]->SetShow(FALSE); + } + + XuiElementSetShow(m_hGridInventory,TRUE); + XuiControlSetText(m_InventoryText,app.GetString(IDS_INVENTORY)); + XuiElementSetShow(m_InventoryText,TRUE); + break; + case DISPLAY_DESCRIPTION: + // turn off the inventory + XuiElementSetShow(m_hGridInventory,FALSE); + XuiElementSetShow(m_DescriptionText,TRUE); + XuiElementSetShow(m_InventoryText,FALSE); + break; + case DISPLAY_INGREDIENTS: + // turn off all the descriptions + XuiElementSetShow(m_DescriptionText,FALSE); + + // display the ingredients + for(int i=0;iSetShow(TRUE); + } + + if(m_iIngredientsC==0) + { + XuiElementSetShow(m_InventoryText,FALSE); + } + else + { + XuiControlSetText(m_InventoryText,app.GetString(IDS_INGREDIENTS)); + XuiElementSetShow(m_InventoryText,TRUE); + } + break; + } +} + +void CXuiSceneCraftingPanel::scrollDescriptionUp() +{ + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_DescriptionText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_DescriptionText.m_hObj,-1); + } +} + +void CXuiSceneCraftingPanel::scrollDescriptionDown() +{ + XUIHtmlScrollInfo ScrollInfo; + + XuiHtmlControlGetVScrollInfo(m_DescriptionText.m_hObj,&ScrollInfo); + if(!ScrollInfo.bScrolling) + { + XuiHtmlControlVScrollBy(m_DescriptionText.m_hObj,1); + } +} + +void CXuiSceneCraftingPanel::updateHighlightAndScrollPositions() +{ + D3DXVECTOR3 vec; + + vec.z=0.0f; + vec.x=m_vSlot0HighlightPos.x + (m_iCurrentSlotHIndex*m_fSlotSize); + vec.y=m_vSlot0HighlightPos.y + ((m_iCurrentSlotVIndex-1)*m_fSlotSize); // vslot 1 is the centred one + m_hHighlight.SetPosition(&vec); + + // Update the scroll icons for all h boxes + for(int i=0;i1)) + { + m_pCursors[i].SetShow(TRUE); + } + else + { + m_pCursors[i].SetShow(FALSE); + } + } + + if(CanBeMadeA[m_iCurrentSlotHIndex].iCount<2) + { + m_hScrollBar2.SetShow(FALSE); + m_hScrollBar3.SetShow(FALSE); + } + else if(CanBeMadeA[m_iCurrentSlotHIndex].iCount<3) + { + // 2 slot + vec.x=m_vSlot0V2ScrollPos.x + (m_iCurrentSlotHIndex*m_fSlotSize); + vec.y=m_vSlot0V2ScrollPos.y; + m_hScrollBar2.SetPosition(&vec); + m_hScrollBar2.SetShow(TRUE); + m_hScrollBar3.SetShow(FALSE); + } + else + { + // 3 slot + vec.x=m_vSlot0V3ScrollPos.x + (m_iCurrentSlotHIndex*m_fSlotSize); + vec.y=m_vSlot0V3ScrollPos.y;//+m_fSlotSize; + + m_hScrollBar3.SetPosition(&vec); + m_hScrollBar2.SetShow(FALSE); + m_hScrollBar3.SetShow(TRUE); + } +} + +void CXuiSceneCraftingPanel::updateVSlotPositions(int iSlots, int i) +{ + D3DXVECTOR3 vec; + if(iSlots==2) + { + vec.y=m_vSlot0Pos.y + (1-i)*m_fSlotSize; + } + else + { + vec.y=m_vSlot0Pos.y + (i-1)*m_fSlotSize; + } + vec.x=m_vSlot0Pos.x + m_iCurrentSlotHIndex*m_fSlotSize; + vec.z=0.0f; + m_pVSlotsBrushImageControl[i]->SetPosition(&vec); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.h b/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.h new file mode 100644 index 0000000..0c6e22f --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_CraftingPanel.h @@ -0,0 +1,205 @@ +#pragma once +using namespace std; + +#include "../media/xuiscene_craftingpanel_2x2.h" +#include "XUI_Ctrl_MinecraftSlot.h" +#include "..\..\..\Minecraft.World\Recipy.h" +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "..\..\..\Minecraft.World\Item.h" +#include "XUI_CustomMessages.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\UI\IUIScene_CraftingMenu.h" + +class CXuiCtrlSlotList; + +class CXuiSceneCraftingPanel : public CXuiSceneImpl, public IUIScene_CraftingMenu +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneCraftingPanel, L"CXuiSceneCraftingPanel", XUI_CLASS_SCENE ) + + protected: + // Control and Element wrapper objects. + + CXuiImageElement m_hPanel; + CXuiImageElement m_hHighlight; + CXuiImageElement m_hScrollBar3; + CXuiImageElement m_hScrollBar2; + CXuiControl m_GroupName; + CXuiHtmlControl m_DescriptionText; + CXuiControl m_ItemName,m_InventoryText; + CXuiElement m_Group; + CXuiElement m_hGrid; + CXuiElement m_hGridInventory; + CXuiImageElement m_hTabGroupA[m_iMaxGroup3x3]; + CXuiControl m_hGroupIconA[m_iMaxGroup3x3]; + CXuiControl m_pHSlotsCraftingCursors[m_iMaxHCraftingSlotC]; + CXuiControl m_pHSlotsCraftingTableCursors[m_iMaxHSlotC]; + CXuiControl *m_pCursors; + CXuiControl m_hCraftIngredientA[m_iIngredients3x3SlotC]; + CXuiControl m_hCraftIngredientDescA[4]; // Max ingredients is 4 for bread + CXuiControl m_hCraftOutput; + CXuiControl m_sceneGroup; + + CXuiCtrlSlotList* m_inventoryControl; + CXuiCtrlSlotList* m_useRowControl; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TRANSITION_END( OnTransitionEnd) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceImage) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_KILL_FOCUS( OnKillFocus) + XUI_ON_XM_INVENTORYUPDATED_MESSAGE( OnCustomMessage_InventoryUpdated ) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS(m_sceneGroup) + MAP_CONTROL(IDC_MainPanel,m_hPanel) + MAP_CONTROL(IDC_XuiGroupName,m_GroupName) + + MAP_CONTROL(IDC_SceneCraftScrollGroup, m_Group) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_XuiHighlight,m_hHighlight) + MAP_CONTROL(IDC_XuiImageScrollBar,m_hScrollBar3) + MAP_CONTROL(IDC_XuiImageScrollBar2Slot,m_hScrollBar2) + MAP_OVERRIDE(IDC_XuiHSlot0, m_pHSlotsBrushImageControl[0]) + MAP_OVERRIDE(IDC_XuiHSlot1, m_pHSlotsBrushImageControl[1]) + MAP_OVERRIDE(IDC_XuiHSlot2, m_pHSlotsBrushImageControl[2]) + MAP_OVERRIDE(IDC_XuiHSlot3, m_pHSlotsBrushImageControl[3]) + MAP_OVERRIDE(IDC_XuiHSlot4, m_pHSlotsBrushImageControl[4]) + MAP_OVERRIDE(IDC_XuiHSlot5, m_pHSlotsBrushImageControl[5]) + MAP_OVERRIDE(IDC_XuiHSlot6, m_pHSlotsBrushImageControl[6]) + MAP_OVERRIDE(IDC_XuiHSlot7, m_pHSlotsBrushImageControl[7]) + MAP_OVERRIDE(IDC_XuiHSlot8, m_pHSlotsBrushImageControl[8]) + MAP_OVERRIDE(IDC_XuiHSlot9, m_pHSlotsBrushImageControl[9]) + MAP_OVERRIDE(IDC_XuiHSlot10, m_pHSlotsBrushImageControl[10]) + MAP_OVERRIDE(IDC_XuiHSlot11, m_pHSlotsBrushImageControl[11]) + + + MAP_OVERRIDE(IDC_XuiVSlot0, m_pVSlotsBrushImageControl[0]) + MAP_OVERRIDE(IDC_XuiVSlot1, m_pVSlotsBrushImageControl[1]) + MAP_OVERRIDE(IDC_XuiVSlot2, m_pVSlotsBrushImageControl[2]) + + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_Group_Tab_Icons, m_Group) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_Icon_1,m_hGroupIconA[0]) + MAP_CONTROL(IDC_Icon_2,m_hGroupIconA[1]) + MAP_CONTROL(IDC_Icon_3,m_hGroupIconA[2]) + MAP_CONTROL(IDC_Icon_4,m_hGroupIconA[3]) + MAP_CONTROL(IDC_Icon_5,m_hGroupIconA[4]) + MAP_CONTROL(IDC_Icon_6,m_hGroupIconA[5]) + MAP_CONTROL(IDC_Icon_7,m_hGroupIconA[6]) + + END_MAP_CHILD_CONTROLS() + + + MAP_CONTROL(IDC_Group_Tab_Images, m_Group) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_TabImage1,m_hTabGroupA[0]) + MAP_CONTROL(IDC_TabImage2,m_hTabGroupA[1]) + MAP_CONTROL(IDC_TabImage3,m_hTabGroupA[2]) + MAP_CONTROL(IDC_TabImage4,m_hTabGroupA[3]) + MAP_CONTROL(IDC_TabImage5,m_hTabGroupA[4]) + MAP_CONTROL(IDC_TabImage6,m_hTabGroupA[5]) + MAP_CONTROL(IDC_TabImage7,m_hTabGroupA[6]) + + END_MAP_CHILD_CONTROLS() + + + MAP_CONTROL(IDC_Grid, m_hGrid) + BEGIN_MAP_CHILD_CONTROLS(m_hGrid) + MAP_CONTROL(IDC_XuiHTMLText,m_DescriptionText) + MAP_CONTROL(IDC_Inventory,m_InventoryText) + MAP_CONTROL(IDC_XuiItemName,m_ItemName) + MAP_CONTROL(IDC_CraftingInput1,m_hCraftIngredientA[0]) + MAP_CONTROL(IDC_CraftingInput2,m_hCraftIngredientA[1]) + MAP_CONTROL(IDC_CraftingInput3,m_hCraftIngredientA[2]) + MAP_CONTROL(IDC_CraftingInput4,m_hCraftIngredientA[3]) + MAP_CONTROL(IDC_CraftingInput5,m_hCraftIngredientA[4]) + MAP_CONTROL(IDC_CraftingInput6,m_hCraftIngredientA[5]) + MAP_CONTROL(IDC_CraftingInput7,m_hCraftIngredientA[6]) + MAP_CONTROL(IDC_CraftingInput8,m_hCraftIngredientA[7]) + MAP_CONTROL(IDC_CraftingInput9,m_hCraftIngredientA[8]) + MAP_CONTROL(IDC_Ingredient1,m_hCraftIngredientDescA[0]) + MAP_CONTROL(IDC_Ingredient2,m_hCraftIngredientDescA[1]) + MAP_CONTROL(IDC_Ingredient3,m_hCraftIngredientDescA[2]) + MAP_CONTROL(IDC_Ingredient4,m_hCraftIngredientDescA[3]) + + MAP_CONTROL(IDC_CraftingOutputRed,m_hCraftOutput) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_InventoryGrid, m_hGridInventory) + BEGIN_MAP_CHILD_CONTROLS(m_hGridInventory) + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransData, BOOL& bHandled); + HRESULT OnGetSourceImage(XUIMessageGetSourceImage* pData, BOOL& rfHandled); + HRESULT OnDestroy(); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + HRESULT OnKillFocus(HXUIOBJ hObjGettingFocus, BOOL& bHandled); + HRESULT OnCustomMessage_InventoryUpdated(); + + CXuiSceneCraftingPanel(); + ~CXuiSceneCraftingPanel() { } + +private: + float m_fSlotSize; + D3DXVECTOR3 m_vSlot0Pos; + D3DXVECTOR3 m_vSlot0HighlightPos; + D3DXVECTOR3 m_vSlot0V2ScrollPos; + D3DXVECTOR3 m_vSlot0V3ScrollPos; + + CXuiCtrlCraftIngredientSlot *m_pCraftingOutput; + CXuiCtrlCraftIngredientSlot *m_pCraftingIngredientA[m_iIngredients3x3SlotC]; + CXuiCtrlCraftIngredientSlot *m_pCraftIngredientDescA[4]; + CXuiCtrlMinecraftSlot *m_pHSlotsBrushImageControl[m_iMaxHSlotC]; + CXuiCtrlMinecraftSlot *m_pVSlotsBrushImageControl[m_iMaxDisplayedVSlotC]; + CXuiControl *GroupTypeIconA[Recipy::eGroupType_Max]; + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + + int mapVKToAction(int vk); + +protected: + virtual int getPad(); + virtual void hideAllHSlots(); + virtual void hideAllVSlots(); + virtual void hideAllIngredientsSlots(); + virtual void setCraftHSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha); + virtual void setCraftVSlotItem(int iPad, int iIndex, shared_ptr item, unsigned int uiAlpha); + virtual void setCraftingOutputSlotItem(int iPad, shared_ptr item); + virtual void setCraftingOutputSlotRedBox(bool show); + virtual void setIngredientSlotItem(int iPad, int index, shared_ptr item); + virtual void setIngredientSlotRedBox(int index, bool show); + virtual void setIngredientDescriptionItem(int iPad, int index, shared_ptr item); + virtual void setIngredientDescriptionRedBox(int index, bool show); + virtual void setIngredientDescriptionText(int index, LPCWSTR text); + virtual void setShowCraftHSlot(int iIndex, bool show); + virtual void showTabHighlight(int iIndex, bool show); + virtual void setGroupText(LPCWSTR text); + virtual void setDescriptionText(LPCWSTR text); + virtual void setItemText(LPCWSTR text); + virtual void scrollDescriptionUp(); + virtual void scrollDescriptionDown(); + virtual void updateHighlightAndScrollPositions(); + virtual void updateVSlotPositions(int iSlots, int i); + + virtual void UpdateMultiPanel(); +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.cpp new file mode 100644 index 0000000..5b3c722 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\Slot.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.dimension.h" + +//#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_SlotList.h" + +#include "XUI_Scene_Enchant.h" +#include "XUI_Scene_Inventory.h" +#include "XUI_Ctrl_EnchantButton.h" + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneEnchant::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneEnchant::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + D3DXVECTOR3 vec; + MapChildControls(); + + XuiControlSetText(m_EnchantText,app.GetString(IDS_ENCHANT)); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + EnchantingScreenInput *initData = (EnchantingScreenInput *) pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Enchanting_Menu, this); + } +#endif + + EnchantmentMenu *menu = new EnchantmentMenu(initData->inventory, initData->level, initData->x, initData->y, initData->z); + + InitDataAssociations(initData->iPad, menu); + + m_enchant1->SetEnable(FALSE); + m_enchant2->SetEnable(FALSE); + m_enchant3->SetEnable(FALSE); + m_enchant1->SetData(m_iPad, 0); + m_enchant2->SetData(m_iPad, 1); + m_enchant3->SetData(m_iPad, 2); + + CXuiSceneAbstractContainer::Initialize( initData->iPad, menu, false, EnchantmentMenu::INV_SLOT_START, CXuiSceneAbstractContainer::eSectionEnchantUsing, CXuiSceneAbstractContainer::eSectionEnchantMax ); + + delete initData; + + return S_OK; +} + +HRESULT CXuiSceneEnchant::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +CXuiControl* CXuiSceneEnchant::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case CXuiSceneAbstractContainer::eSectionEnchantInventory: + return (CXuiControl *)m_inventoryControl; + break; + case CXuiSceneAbstractContainer::eSectionEnchantUsing: + return (CXuiControl *)m_useRowControl; + break; + case CXuiSceneAbstractContainer::eSectionEnchantSlot: + return (CXuiControl *)m_ingredientControl; + break; + case eSectionEnchantButton1: + return m_enchant1; + break; + case eSectionEnchantButton2: + return m_enchant2; + break; + case eSectionEnchantButton3: + return m_enchant3; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneEnchant::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case CXuiSceneAbstractContainer::eSectionEnchantInventory: + return m_inventoryControl; + break; + case CXuiSceneAbstractContainer::eSectionEnchantUsing: + return m_useRowControl; + break; + case CXuiSceneAbstractContainer::eSectionEnchantSlot: + return m_ingredientControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneEnchant::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + m_ingredientControl->SetData( iPad, menu, 1, 1, EnchantmentMenu::INGREDIENT_SLOT ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, EnchantmentMenu::INV_SLOT_START); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.h b/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.h new file mode 100644 index 0000000..5cab04a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Enchant.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "..\Media\xuiscene_enchant.h" + +#include "..\..\BookModel.h" + +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Ctrl_EnchantmentBook.h" +#include "XUI_Scene_AbstractContainer.h" +#include "..\UI\IUIScene_EnchantingMenu.h" +#include "XUI_CustomMessages.h" + +#include "XUI_Scene_Enchant.h" + +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\SimpleContainer.h" + +class Level; +class CXuiCtrlEnchantmentButton; +class EnchantmentMenu; + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneEnchant : public CXuiSceneAbstractContainer, public IUIScene_EnchantingMenu +{ + friend class CXuiCtrlEnchantmentButtonText; +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneEnchant, L"CXuiSceneEnchant", XUI_CLASS_SCENE ) + +protected: + CXuiCtrlEnchantmentButton *m_enchant1; + CXuiCtrlEnchantmentButton *m_enchant2; + CXuiCtrlEnchantmentButton *m_enchant3; + CXuiControl m_sceneGroup; + CXuiCtrlSlotList* m_ingredientControl; + CXuiControl m_EnchantText; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Ingredient, m_ingredientControl) + MAP_OVERRIDE(IDC_EnchantButton1, m_enchant1) + MAP_OVERRIDE(IDC_EnchantButton2, m_enchant2) + MAP_OVERRIDE(IDC_EnchantButton3, m_enchant3) + MAP_CONTROL(IDC_EnchantText,m_EnchantText) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + +private: + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.cpp new file mode 100644 index 0000000..832d6b4 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\FurnaceMenu.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_Furnace.h" +#include "XUI_Ctrl_BurnProgress.h" +#include "XUI_Ctrl_FireProgress.h" + + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneFurnace::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneFurnace::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_FurnaceText,app.GetString(IDS_FURNACE)); + XuiControlSetText(m_IngredientText,app.GetString(IDS_INGREDIENT)); + XuiControlSetText(m_FuelText,app.GetString(IDS_FUEL)); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + FurnaceScreenInput* initData = (FurnaceScreenInput*)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Furnace_Menu, this); + } +#endif + + FurnaceMenu* menu = new FurnaceMenu( initData->inventory, initData->furnace ); + + + InitDataAssociations(m_iPad, menu); + + m_litProgressControl->SetUserData( initData->furnace.get() ); + + m_burnProgress->SetUserData( initData->furnace.get() ); + + CXuiSceneAbstractContainer::Initialize( m_iPad, menu, true, FurnaceMenu::INV_SLOT_START, eSectionFurnaceUsing, eSectionFurnaceMax ); + + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_FORGING); + + delete initData; + + return S_OK; +} + +HRESULT CXuiSceneFurnace::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +CXuiControl* CXuiSceneFurnace::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionFurnaceResult: + return (CXuiControl *)m_resultControl; + break; + case eSectionFurnaceFuel: + return (CXuiControl *)m_fuelControl; + break; + case eSectionFurnaceIngredient: + return (CXuiControl *)m_ingredientControl; + break; + case eSectionFurnaceInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionFurnaceUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneFurnace::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionFurnaceResult: + return m_resultControl; + break; + case eSectionFurnaceFuel: + return m_fuelControl; + break; + case eSectionFurnaceIngredient: + return m_ingredientControl; + break; + case eSectionFurnaceInventory: + return m_inventoryControl; + break; + case eSectionFurnaceUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneFurnace::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + // TODO Inventory dimensions need defined as constants + m_ingredientControl->SetData( iPad, menu, 1, 1, FurnaceMenu::INGREDIENT_SLOT ); + + m_fuelControl->SetData( iPad, menu, 1, 1, FurnaceMenu::FUEL_SLOT ); + + m_resultControl->SetData( iPad, menu, 1, 1, FurnaceMenu::RESULT_SLOT ); + + //m_litProgressControl->SetUserData( initData->furnace.get() ); + + //m_burnProgress->SetUserData( initData->furnace.get() ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, FurnaceMenu::INV_SLOT_START); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.h b/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.h new file mode 100644 index 0000000..22cbf26 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Furnace.h @@ -0,0 +1,79 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_furnace.h" +#include "XUI_Scene_AbstractContainer.h" +#include "..\UI\IUIScene_FurnaceMenu.h" + +class CXuiCtrlSlotList; +class CXuiCtrlFireProgress; +class CXuiCtrlBurnProgress; + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneFurnace : public CXuiSceneAbstractContainer, public IUIScene_FurnaceMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneFurnace, L"CXuiSceneFurnace", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Ingredient, m_ingredientControl) + MAP_OVERRIDE(IDC_Fuel, m_fuelControl) + MAP_OVERRIDE(IDC_Result, m_resultControl) + + MAP_OVERRIDE(IDC_Lit, m_litProgressControl) + MAP_OVERRIDE(IDC_Burn, m_burnProgress) + + MAP_CONTROL(IDC_FurnaceText,m_FurnaceText) + MAP_CONTROL(IDC_IngredientText,m_IngredientText) + MAP_CONTROL(IDC_FuelText,m_FuelText) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); +// HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + +private: + CXuiCtrlSlotList *m_ingredientControl; + CXuiCtrlSlotList *m_fuelControl; + CXuiCtrlSlotList *m_resultControl; + + CXuiCtrlFireProgress *m_litProgressControl; + CXuiCtrlBurnProgress *m_burnProgress; + + CXuiControl m_FurnaceText; + CXuiControl m_IngredientText; + CXuiControl m_FuelText; + + CXuiControl m_sceneGroup; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.cpp new file mode 100644 index 0000000..04e77e5 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.cpp @@ -0,0 +1,234 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Controls.h" +#include "XUI_Scene_Inventory.h" + + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneInventory::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneInventory::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + D3DXVECTOR3 vec; + MapChildControls(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + InventoryScreenInput *initData = (InventoryScreenInput *)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + } + +#ifdef _XBOX + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Inventory_Menu, this); + } +#endif + + InventoryMenu *menu = dynamic_cast( initData->player->inventoryMenu ); + +#if 0 + // TODO Inventory dimensions need defined as constants + m_armorGroup->SetData( initData->iPad, menu, 4, 1, InventoryMenu::ARMOR_SLOT_START ); +#endif + InitDataAssociations(initData->iPad, menu); + + initData->player->awardStat(GenericStats::openInventory(), GenericStats::param_noArgs()); + + CXuiSceneAbstractContainer::Initialize( initData->iPad, menu, false, InventoryMenu::INV_SLOT_START, eSectionInventoryUsing, eSectionInventoryMax, initData->bNavigateBack ); + + delete initData; + + float fWidth; + m_effectsGroup.GetBounds(&fWidth, &m_effectAreaHeight); // Get total height available for effects display + m_hEffectDisplayA[0]->GetBounds(&fWidth, &m_effectDisplayHeight); // Get height of one effect + + D3DXVECTOR3 firstEffectPos, secondEffectPos; + m_hEffectDisplayA[0]->GetPosition(&firstEffectPos); + m_hEffectDisplayA[1]->GetPosition(&secondEffectPos); + m_effectDisplaySpacing = firstEffectPos.y - secondEffectPos.y; // Stack from the bottom + + updateEffectsDisplay(); + XuiSetTimer(m_hObj,INVENTORY_UPDATE_EFFECTS_TIMER_ID,INVENTORY_UPDATE_EFFECTS_TIMER_TIME); + + return S_OK; +} + +HRESULT CXuiSceneInventory::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +HRESULT CXuiSceneInventory::handleCustomTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + if(pTimer->nId == INVENTORY_UPDATE_EFFECTS_TIMER_ID) + { + updateEffectsDisplay(); + bHandled = TRUE; + } + return S_OK; +} + +CXuiControl* CXuiSceneInventory::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryArmor: + return (CXuiControl *)m_armorGroup; + break; + case eSectionInventoryInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionInventoryUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneInventory::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryArmor: + return m_armorGroup; + break; + case eSectionInventoryInventory: + return m_inventoryControl; + break; + case eSectionInventoryUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneInventory::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + // TODO Inventory dimensions need defined as constants + m_armorGroup->SetData( iPad, menu, 4, 1, InventoryMenu::ARMOR_SLOT_START ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, InventoryMenu::INV_SLOT_START); +} + +void CXuiSceneInventory::updateEffectsDisplay() +{ + // Update with the current effects + Minecraft *pMinecraft = Minecraft::GetInstance(); + shared_ptr player = pMinecraft->localplayers[m_iPad]; + + if(player == NULL) return; + + vector *activeEffects = player->getActiveEffects(); + + // Work out how to arrange the effects + int effectCount = (int)activeEffects->size(); + + // Total size of all effects + spacing, minus spacing for the last effect + float fHeight = (effectCount * m_effectDisplaySpacing) - (m_effectDisplaySpacing - m_effectDisplayHeight); + float fNextEffectYOffset = m_effectDisplaySpacing; + + if(fHeight > m_effectAreaHeight) + { + fNextEffectYOffset = (m_effectAreaHeight + 1) / effectCount; + fNextEffectYOffset = floor(fNextEffectYOffset); + } + + // Fill out details for display + D3DXVECTOR3 position; + m_hEffectDisplayA[0]->GetPosition(&position); + AUTO_VAR(it, activeEffects->begin()); + for(unsigned int i = 0; i < MAX_EFFECTS; ++i) + { + if(it != activeEffects->end()) + { + m_hEffectDisplayA[i]->SetShow(TRUE); + + if(i > 0) position.y -= fNextEffectYOffset; // Stack from the bottom + m_hEffectDisplayA[i]->SetPosition(&position); + + MobEffectInstance *effect = *it; + + MobEffect *mobEffect = MobEffect::effects[effect->getId()]; + if (mobEffect->hasIcon()) + { + m_hEffectDisplayA[i]->setIcon(mobEffect->getIcon()); + } + + wstring effectString = app.GetString( effect->getDescriptionId() );//I18n.get(effect.getDescriptionId()).trim(); + if (effect->getAmplifier() > 0) + { + wstring potencyString = L""; + switch(effect->getAmplifier()) + { + case 1: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_1 ); + break; + case 2: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_2 ); + break; + case 3: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_3 ); + break; + default: + potencyString = app.GetString( IDS_POTION_POTENCY_0 ); + break; + } + effectString += potencyString; + } + m_hEffectDisplayA[i]->setName(effectString); + + wstring durationString = MobEffect::formatDuration(effect); + m_hEffectDisplayA[i]->setDuration(durationString); + + ++it; + } + else + { + m_hEffectDisplayA[i]->SetShow(FALSE); + } + } + delete activeEffects; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.h b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.h new file mode 100644 index 0000000..2a7b305 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory.h @@ -0,0 +1,89 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_inventory.h" +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_CustomMessages.h" +#include "..\UI\IUIScene_InventoryMenu.h" + +#define INVENTORY_UPDATE_EFFECTS_TIMER_ID (10) +#define INVENTORY_UPDATE_EFFECTS_TIMER_TIME (1000) // 1 second + +class CXuiCtrlMobEffect; + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneInventory : public CXuiSceneAbstractContainer, public IUIScene_InventoryMenu +{ +private: + static const int MAX_EFFECTS = 10; +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneInventory, L"CXuiSceneInventory", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_OVERRIDE(IDC_Armor, m_armorGroup) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_EffectsGroup, m_effectsGroup) + BEGIN_MAP_CHILD_CONTROLS( m_effectsGroup ) + MAP_OVERRIDE(IDC_Effect1, m_hEffectDisplayA[0]) + MAP_OVERRIDE(IDC_Effect2, m_hEffectDisplayA[1]) + MAP_OVERRIDE(IDC_Effect3, m_hEffectDisplayA[2]) + MAP_OVERRIDE(IDC_Effect4, m_hEffectDisplayA[3]) + MAP_OVERRIDE(IDC_Effect5, m_hEffectDisplayA[4]) + MAP_OVERRIDE(IDC_Effect6, m_hEffectDisplayA[5]) + MAP_OVERRIDE(IDC_Effect7, m_hEffectDisplayA[6]) + MAP_OVERRIDE(IDC_Effect8, m_hEffectDisplayA[7]) + MAP_OVERRIDE(IDC_Effect9, m_hEffectDisplayA[8]) + MAP_OVERRIDE(IDC_Effect10, m_hEffectDisplayA[9]) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + //HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) {return S_OK;} + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); + + virtual HRESULT handleCustomTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + +private: + float m_effectDisplayHeight; + float m_effectDisplaySpacing; + float m_effectAreaHeight; + + CXuiCtrlSlotList* m_armorGroup; + + CXuiControl m_sceneGroup; + + CXuiControl m_effectsGroup; + CXuiCtrlMobEffect *m_hEffectDisplayA[MAX_EFFECTS]; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); + + void updateEffectsDisplay(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.cpp new file mode 100644 index 0000000..0793fc9 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.cpp @@ -0,0 +1,209 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" + +#include "..\..\..\Minecraft.World\Container.h" +#include "..\..\..\Minecraft.World\Slot.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.entity.player.h" + +#include "..\..\..\Minecraft.World\net.minecraft.stats.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Minecraft.h" + +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_Ctrl_SlotItem.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Ctrl_SlotItemListItem.h" + +#include "..\..\Common\Potion_macros.h" + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneInventoryCreative::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneInventoryCreative::OnInit( XUIMessageInit *pInitData, BOOL &bHandled ) +{ + D3DXVECTOR3 vec; + MapChildControls(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + InventoryScreenInput *initData = (InventoryScreenInput *)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + } + +#ifdef _XBOX + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Creative_Inventory_Menu, this); + } +#endif + + // 4J JEV - Does this still count as opening the inventory? + initData->player->awardStat(GenericStats::openInventory(), GenericStats::param_noArgs()); + + // 4J JEV - Item Picker Menu + shared_ptr creativeContainer = shared_ptr(new SimpleContainer( 0, TabSpec::MAX_SIZE + 9 )); + itemPickerMenu = new ItemPickerMenu(creativeContainer, initData->player->inventory); + + // 4J JEV - InitDataAssociations. + m_containerControl->SetData( initData->iPad, itemPickerMenu, TabSpec::rows, TabSpec::columns, 0, TabSpec::MAX_SIZE ); + m_useRowControl->SetData( initData->iPad, itemPickerMenu, 1, 9, TabSpec::MAX_SIZE, TabSpec::MAX_SIZE + 9 ); + m_pointerControl->SetUserIndex(m_pointerControl->m_hObj, initData->iPad); + + // Initialize superclass. + CXuiSceneAbstractContainer::Initialize( initData->iPad, itemPickerMenu, false, -1, eSectionInventoryCreativeUsing, eSectionInventoryCreativeMax, initData->bNavigateBack ); + + delete initData; + + // Change the point at which the cursor stops so we can't move the pointer over the tabs + D3DXVECTOR3 containerPos; + m_containerControl->GetPosition(&containerPos); + m_fPointerMinY += containerPos.y; + + // 4J JEV - Settup Tabs + for (int i = 0; i < eCreativeInventoryTab_COUNT; i++) + { + m_hTabGroupA[i].SetShow(FALSE); + } + + m_curTab = eCreativeInventoryTab_COUNT; + switchTab(eCreativeInventoryTab_BuildingBlocks); + + return S_OK; +} + +HRESULT CXuiSceneInventoryCreative::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +////////////////////////////////////////////////////////////////////////// +// +// OnTransitionEnd +// +////////////////////////////////////////////////////////////////////////// +HRESULT CXuiSceneInventoryCreative::OnTransitionEnd( XUIMessageTransition *pTransData, BOOL& bHandled ) +{ + // are we being destroyed? If so, don't do anything + if(pTransData->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) + { + return S_OK; + } + + // Fix for issue caused by autosave while crafting is up + if(pTransData->dwTransType == XUI_TRANSITION_TO || pTransData->dwTransType == XUI_TRANSITION_BACKTO) + { + for(int i=0;im_icon,NULL,specs[i]->m_icon); + XuiElementSetShow(m_hGroupIconA[i].m_hObj,TRUE); + } + } + + return S_OK; +} + +CXuiControl* CXuiSceneInventoryCreative::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryCreativeUsing: + return (CXuiControl *)m_useRowControl; + break; + case eSectionInventoryCreativeSelector: + return (CXuiControl *)m_containerControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneInventoryCreative::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionInventoryCreativeUsing: + return m_useRowControl; + break; + case eSectionInventoryCreativeSelector: + return m_containerControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +void CXuiSceneInventoryCreative::updateTabHighlightAndText(ECreativeInventoryTabs tab) +{ + if (m_curTab < eCreativeInventoryTab_COUNT) + { + m_hTabGroupA[m_curTab].SetShow(FALSE); + } + + m_hTabGroupA[tab].SetShow(TRUE); + wstring wsText=app.GetString(specs[tab]->m_descriptionId); + m_GroupDescription.SetText(wsText.c_str()); + m_GroupDescription.SetShow(TRUE); +} + +void CXuiSceneInventoryCreative::updateScrollCurrentPage(int currentPage, int pageCount) +{ + m_pageSlider.SetEnable(pageCount > 1); + + if(pageCount == 1) + { + m_pageSlider.SetRange(0,1); + m_pageSlider.SetValue(0); + } + else + { + m_pageSlider.SetRange(0,pageCount - 1); + m_pageSlider.SetValue(currentPage - 1); + } + + m_scrollUp.SetShow(currentPage > 1); + m_scrollUp.PlayOptionalVisual(L"ScrollMore",L"EndScrollMore"); + + + m_scrollDown.SetShow(currentPage < pageCount); + m_scrollDown.PlayOptionalVisual(L"ScrollMore",L"EndScrollMore"); + + //wchar_t pageNum[10]; + //swprintf(pageNum,10,L"%d/%d",currentPage,pageCount); + //m_pageNumber.SetText(pageNum); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.h b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.h new file mode 100644 index 0000000..bb3ff97 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Inventory_Creative.h @@ -0,0 +1,104 @@ +#pragma once +using namespace std; + +#include "..\Media\xuiscene_inventory_creative.h" +#include "XUI_Scene_AbstractContainer.h" +#include "XUI_CustomMessages.h" +#include "..\..\..\Minecraft.World\AbstractContainerMenu.h" +#include "..\..\..\Minecraft.World\SimpleContainer.h" + +#include "..\UI\IUIScene_CreativeMenu.h" + +#include + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneInventoryCreative : public CXuiSceneAbstractContainer, public IUIScene_CreativeMenu +{ +public: + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneInventoryCreative, L"CXuiSceneInventoryCreative", XUI_CLASS_SCENE ) + +protected: + + CXuiControl m_hTabGroupA[eCreativeInventoryTab_COUNT]; + CXuiControl m_hGroupIconA[eCreativeInventoryTab_COUNT]; + CXuiElement m_Group; + CXuiControl m_GroupDescription; + + CXuiSlider m_pageSlider; + CXuiControl m_scrollUp, m_scrollDown;//, m_pageNumber; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_TRANSITION_END( OnTransitionEnd) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + // Common to all abstract container scenes + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_OVERRIDE(IDC_Container, m_containerControl) + + MAP_CONTROL(IDC_XuiSlider,m_pageSlider) + //MAP_CONTROL(IDC_PageNumber,m_pageNumber) + MAP_CONTROL(IDC_ScrollDown,m_scrollDown) + MAP_CONTROL(IDC_ScrollUp,m_scrollUp) + + MAP_CONTROL(IDC_InventoryText,m_GroupDescription) + + // 4J JEV - Tabs + MAP_CONTROL(IDC_Group_Tab_Icons, m_Group) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_Icon_1,m_hGroupIconA[0]) + MAP_CONTROL(IDC_Icon_2,m_hGroupIconA[1]) + MAP_CONTROL(IDC_Icon_3,m_hGroupIconA[2]) + MAP_CONTROL(IDC_Icon_4,m_hGroupIconA[3]) + MAP_CONTROL(IDC_Icon_5,m_hGroupIconA[4]) + MAP_CONTROL(IDC_Icon_6,m_hGroupIconA[5]) + MAP_CONTROL(IDC_Icon_7,m_hGroupIconA[6]) + MAP_CONTROL(IDC_Icon_8,m_hGroupIconA[7]) + END_MAP_CHILD_CONTROLS() + MAP_CONTROL(IDC_Group_Tab_Images, m_Group) + BEGIN_MAP_CHILD_CONTROLS(m_Group) + MAP_CONTROL(IDC_TabImage1,m_hTabGroupA[0]) + MAP_CONTROL(IDC_TabImage2,m_hTabGroupA[1]) + MAP_CONTROL(IDC_TabImage3,m_hTabGroupA[2]) + MAP_CONTROL(IDC_TabImage4,m_hTabGroupA[3]) + MAP_CONTROL(IDC_TabImage5,m_hTabGroupA[4]) + MAP_CONTROL(IDC_TabImage6,m_hTabGroupA[5]) + MAP_CONTROL(IDC_TabImage7,m_hTabGroupA[6]) + MAP_CONTROL(IDC_TabImage8,m_hTabGroupA[7]) + END_MAP_CHILD_CONTROLS() + + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnTransitionEnd( XUIMessageTransition *pTransData, BOOL& bHandled); + //HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) {return S_OK;} + +private: + CXuiControl m_sceneGroup; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); + + CXuiCtrlSlotList* m_containerControl; + +private: + // IUIScene_CreativeMenu + void updateTabHighlightAndText(ECreativeInventoryTabs tab); + void updateScrollCurrentPage(int currentPage, int pageCount); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Trading.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Trading.cpp new file mode 100644 index 0000000..fec1346 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Trading.cpp @@ -0,0 +1,320 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\..\..\Minecraft.World\net.minecraft.world.item.trading.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" +#include "..\..\Minecraft.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_Trading.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\JavaMath.h" + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneTrading::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneTrading::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + //XuiControlSetText(m_villagerText,app.GetString(IDS_VILLAGER)); + XuiControlSetText(m_inventoryLabel,app.GetString(IDS_INVENTORY)); + XuiControlSetText(m_requiredLabel,app.GetString(IDS_REQUIRED_ITEMS_FOR_TRADE)); + + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + TradingScreenInput* initData = (TradingScreenInput *)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + m_merchant = initData->trader; + + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Trading_Menu, this); + } + + m_menu = new MerchantMenu( initData->inventory, initData->trader, initData->level ); + Minecraft::GetInstance()->localplayers[m_iPad]->containerMenu = m_menu; + + // TODO Inventory dimensions need defined as constants + m_inventoryControl->SetData( m_iPad, m_menu, 3, 9, MerchantMenu::INV_SLOT_START, MerchantMenu::INV_SLOT_END ); + + // TODO Inventory dimensions need defined as constants + m_useRowControl->SetData( m_iPad, m_menu, 1, 9, MerchantMenu::USE_ROW_SLOT_START, MerchantMenu::USE_ROW_SLOT_END ); + + delete initData; + + D3DXVECTOR3 vec; + // store the slot 0 position + m_tradeHSlots[0]->GetPosition(&m_vTradeSlot0Pos); + m_tradeHSlots[1]->GetPosition(&vec); + m_fSlotSize=vec.x-m_vTradeSlot0Pos.x; + + // store the slot 0 highlight position + m_tradingSelector.GetPosition(&m_vSelectorInitialPos); + + //app.SetRichPresenceContextValue(m_iPad,CONTEXT_GAME_STATE_FORGING); + + XuiSetTimer(m_hObj,TRADING_UPDATE_TIMER_ID,TRADING_UPDATE_TIMER_TIME); + + ui.SetTooltips(m_iPad, -1, IDS_TOOLTIPS_EXIT); + + return S_OK; +} + +HRESULT CXuiSceneTrading::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +HRESULT CXuiSceneTrading::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) + { + HXUIOBJ hObj=NULL; + HRESULT hr=XuiControlGetVisual(m_offerInfoControl.m_hObj,&hObj); + hr=XuiElementGetChildById(hObj,L"text_measurer",&m_hOfferInfoTextMeasurer); + hr=XuiElementGetChildById(hObj,L"text_name",&m_hOfferInfoText); + hr=XuiElementGetChildById(hObj,L"text_panel",&m_hOfferInfoTextBkg); + } + + return S_OK; +} + +HRESULT CXuiSceneTrading::OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled) +{ + bHandled = handleKeyDown(pInputData->UserIndex, mapVKToAction(pInputData->dwKeyCode), (pInputData->dwFlags & XUI_INPUT_FLAG_REPEAT) != 0); + + return S_OK; +} + +HRESULT CXuiSceneTrading::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +HRESULT CXuiSceneTrading::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + if(pTimer->nId == TRADING_UPDATE_TIMER_ID) + { + handleTick(); + bHandled = TRUE; + } + return S_OK; +} + +int CXuiSceneTrading::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: + case VK_PAD_LTHUMB_LEFT: + action = ACTION_MENU_LEFT; + break; + case VK_PAD_DPAD_RIGHT: + case VK_PAD_LTHUMB_RIGHT: + action = ACTION_MENU_RIGHT; + break; + case VK_PAD_LTHUMB_UP: + case VK_PAD_DPAD_UP: + action = ACTION_MENU_UP; + break; + case VK_PAD_LTHUMB_DOWN: + 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 CXuiSceneTrading::showScrollRightArrow(bool show) +{ + m_scrollRight.SetShow(show?TRUE:FALSE); + m_scrollRight.PlayOptionalVisual(L"ScrollMore",L"EndScrollMore"); +} + +void CXuiSceneTrading::showScrollLeftArrow(bool show) +{ + m_scrollLeft.SetShow(show?TRUE:FALSE); + m_scrollLeft.PlayOptionalVisual(L"ScrollMore",L"EndScrollMore"); +} + +void CXuiSceneTrading::moveSelector(bool right) +{ + D3DXVECTOR3 vec; + + vec.z=0.0f; + vec.x=m_vSelectorInitialPos.x + (m_selectedSlot*m_fSlotSize); + vec.y=m_vSelectorInitialPos.y; + m_tradingSelector.SetPosition(&vec); +} + +void CXuiSceneTrading::setTitle(const wstring &name) +{ + XuiControlSetText(m_villagerText,name.c_str()); +} + +void CXuiSceneTrading::setRequest1Name(const wstring &name) +{ + m_request1Label.SetText(name.c_str()); +} + +void CXuiSceneTrading::setRequest2Name(const wstring &name) +{ + m_request2Label.SetText(name.c_str()); +} + +void CXuiSceneTrading::setRequest1RedBox(bool show) +{ + m_request1Control->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneTrading::setRequest2RedBox(bool show) +{ + m_request2Control->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneTrading::setTradeRedBox(int index, bool show) +{ + m_tradeHSlots[index]->SetRedBox(show?TRUE:FALSE); +} + +void CXuiSceneTrading::setRequest1Item(shared_ptr item) +{ + m_request1Control->SetIcon(getPad(), item, 12, 31, true); +} + +void CXuiSceneTrading::setRequest2Item(shared_ptr item) +{ + m_request2Control->SetIcon(getPad(), item, 12, 31, true); +} + +void CXuiSceneTrading::setTradeItem(int index, shared_ptr item) +{ + m_tradeHSlots[index]->SetIcon(getPad(), item, 12, 31, true); +} + +void CXuiSceneTrading::setOfferDescription(const wstring &name, vector &unformattedStrings) +{ + if(name.empty()) + { + m_offerInfoControl.SetText(L""); + m_offerInfoControl.SetShow(FALSE); + return; + } + + bool smallPointer = m_bSplitscreen || (!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()); + wstring desc = L"(smallPointer ? 12 :14) + L"\">" + name + L""; + + XUIRect tempXuiRect, xuiRect; + HRESULT hr; + xuiRect.right = 0; + + for(AUTO_VAR(it, unformattedStrings.begin()); it != unformattedStrings.end(); ++it) + { + XuiTextPresenterMeasureText(m_hOfferInfoTextMeasurer, (*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_hOfferInfoTextBkg,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_hOfferInfoText,xuiRect.right+4.0f+4.0f,xuiRect.bottom); // edge graphics are 8 pixels, text is centred + + XuiHtmlSetText(m_hOfferInfoText, desc.c_str() ); + + // Check if we need to resize the box + XUIContentDims contentDims; + XuiHtmlGetContentDims(m_hOfferInfoText,&contentDims); + xuiRect.bottom = contentDims.nContentHeight; + + // Set the new height + float backgroundWidth = xuiRect.right+4.0f+4.0f+4.0f; + XuiElementSetBounds(m_hOfferInfoTextBkg,backgroundWidth,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_hOfferInfoText,xuiRect.right+4.0f+4.0f,xuiRect.bottom+4.0f+4.0f); // edge graphics are 8 pixels, text is centred + + m_offerInfoControl.SetShow(TRUE); + + D3DXVECTOR3 highlightPos, offerInfoPos; + float highlightWidth, highlightHeight; + m_tradingSelector.GetPosition(&highlightPos); + m_tradingSelector.GetBounds(&highlightWidth,&highlightHeight); + m_offerInfoControl.GetPosition(&offerInfoPos); + + if(m_selectedSlot < DISPLAY_TRADES_COUNT/2) + { + // Display on the right + offerInfoPos.x = Math::round(highlightPos.x + highlightWidth * 1.1); + } + else + { + // Display on the left + offerInfoPos.x = Math::round(highlightPos.x - backgroundWidth - highlightWidth * 0.1); + } + m_offerInfoControl.SetPosition(&offerInfoPos); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Trading.h b/Minecraft.Client/Common/XUI/XUI_Scene_Trading.h new file mode 100644 index 0000000..72194cb --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Trading.h @@ -0,0 +1,128 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_trading.h" +#include "..\UI\IUIScene_TradingMenu.h" + +#define TRADING_UPDATE_TIMER_ID (10) +#define TRADING_UPDATE_TIMER_TIME (50) + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneTrading : public CXuiSceneImpl, public IUIScene_TradingMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneTrading, L"CXuiSceneTrading", XUI_CLASS_SCENE ) + +private: + CXuiCtrlCraftIngredientSlot *m_request1Control; + CXuiCtrlCraftIngredientSlot *m_request2Control; + + CXuiCtrlCraftIngredientSlot *m_tradeHSlots[7]; + + CXuiControl m_request1Label, m_request2Label; + CXuiControl m_inventoryLabel, m_requiredLabel; + + CXuiControl m_villagerText; + CXuiControl m_scrollLeft, m_scrollRight, m_tradingSelector; + + CXuiControl m_sceneGroup; + CXuiElement m_hGridInventory; + + CXuiCtrlSlotList* m_inventoryControl; + CXuiCtrlSlotList* m_useRowControl; + + CXuiControl m_offerInfoControl; + HXUIOBJ m_hOfferInfoTextMeasurer; + HXUIOBJ m_hOfferInfoText; + HXUIOBJ m_hOfferInfoTextBkg; + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + MAP_OVERRIDE(IDC_Request1, m_request1Control) + MAP_OVERRIDE(IDC_Request2, m_request2Control) + + MAP_OVERRIDE(IDC_TradingBar0, m_tradeHSlots[0]) + MAP_OVERRIDE(IDC_TradingBar1, m_tradeHSlots[1]) + MAP_OVERRIDE(IDC_TradingBar2, m_tradeHSlots[2]) + MAP_OVERRIDE(IDC_TradingBar3, m_tradeHSlots[3]) + MAP_OVERRIDE(IDC_TradingBar4, m_tradeHSlots[4]) + MAP_OVERRIDE(IDC_TradingBar5, m_tradeHSlots[5]) + MAP_OVERRIDE(IDC_TradingBar6, m_tradeHSlots[6]) + + MAP_CONTROL(IDC_Offer1Label, m_request1Label) + MAP_CONTROL(IDC_Offer2Label, m_request2Label) + + MAP_CONTROL(IDC_VillagerText,m_villagerText) + MAP_CONTROL(IDC_InventoryLabel,m_inventoryLabel) + MAP_CONTROL(IDC_RequiredLabel,m_requiredLabel) + + MAP_CONTROL(IDC_ScrollLeftArrow,m_scrollLeft) + MAP_CONTROL(IDC_ScrollRightArrow,m_scrollRight) + MAP_CONTROL(IDC_TradingSelector,m_tradingSelector) + + MAP_CONTROL(IDC_HtmlTextPanel,m_offerInfoControl) + + MAP_CONTROL(IDC_InventoryGrid, m_hGridInventory) + BEGIN_MAP_CHILD_CONTROLS(m_hGridInventory) + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + END_MAP_CHILD_CONTROLS() + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnDestroy(); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + +protected: + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + bool m_bSplitscreen; + +public: + int getPad() { return m_iPad; } + +private: + + float m_fSlotSize; + D3DXVECTOR3 m_vTradeSlot0Pos; + D3DXVECTOR3 m_vSelectorInitialPos; + + int mapVKToAction(int vk); +protected: + virtual void showScrollRightArrow(bool show); + virtual void showScrollLeftArrow(bool show); + virtual void moveSelector(bool right); + virtual void setRequest1Name(const wstring &name); + virtual void setRequest2Name(const wstring &name); + virtual void setTitle(const wstring &name); + + virtual void setRequest1RedBox(bool show); + virtual void setRequest2RedBox(bool show); + virtual void setTradeRedBox(int index, bool show); + + virtual void setRequest1Item(shared_ptr item); + virtual void setRequest2Item(shared_ptr item); + virtual void setTradeItem(int index, shared_ptr item); + + virtual void setOfferDescription(const wstring &name, vector &unformattedStrings); +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Trap.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Trap.cpp new file mode 100644 index 0000000..99dc6e4 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Trap.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" + +#include "..\..\..\Minecraft.World\DispenserTileEntity.h" +#include "..\..\..\Minecraft.World\TrapMenu.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "XUI_Ctrl_SlotList.h" +#include "XUI_Scene_Trap.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Common\Tutorial\TutorialEnum.h" + +//-------------------------------------------------------------------------------------- +// Name: CXuiSceneTrap::OnInit +// Desc: Message handler for XM_INIT +//-------------------------------------------------------------------------------------- +HRESULT CXuiSceneTrap::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + D3DXVECTOR3 vec; + MapChildControls(); + + XuiControlSetText(m_DispenserText,app.GetString(IDS_DISPENSER)); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + TrapScreenInput* initData = (TrapScreenInput*)pInitData->pvInitData; + m_iPad=initData->iPad; + m_bSplitscreen=initData->bSplitscreen; + +#ifdef _XBOX + if( pMinecraft->localgameModes[initData->iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[initData->iPad]; + m_previousTutorialState = gameMode->getTutorial()->getCurrentState(); + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Trap_Menu, this); + } +#endif + // if we are in splitscreen, then we need to figure out if we want to move this scene + + if(m_bSplitscreen) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + TrapMenu* menu = new TrapMenu( initData->inventory, initData->trap ); + + InitDataAssociations(initData->iPad, menu); + + CXuiSceneAbstractContainer::Initialize( initData->iPad, menu, true, initData->trap->getContainerSize(), eSectionTrapUsing, eSectionTrapMax ); + + delete initData; + + return S_OK; +} + +HRESULT CXuiSceneTrap::OnDestroy() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + +#ifdef _XBOX + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(gameMode != NULL) gameMode->getTutorial()->changeTutorialState(m_previousTutorialState); + } +#endif + + // 4J Stu - Fix for #11302 - TCR 001: Network Connectivity: Host crashed after being killed by the client while accessing a chest during burst packet loss. + // We need to make sure that we call closeContainer() anytime this menu is closed, even if it is forced to close by some other reason (like the player dying) + if(Minecraft::GetInstance()->localplayers[m_iPad] != NULL) Minecraft::GetInstance()->localplayers[m_iPad]->closeContainer(); + return S_OK; +} + +CXuiControl* CXuiSceneTrap::GetSectionControl( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionTrapTrap: + return (CXuiControl *)m_trapControl; + break; + case eSectionTrapInventory: + return (CXuiControl *)m_inventoryControl; + break; + case eSectionTrapUsing: + return (CXuiControl *)m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +CXuiCtrlSlotList* CXuiSceneTrap::GetSectionSlotList( ESceneSection eSection ) +{ + switch( eSection ) + { + case eSectionTrapTrap: + return m_trapControl; + break; + case eSectionTrapInventory: + return m_inventoryControl; + break; + case eSectionTrapUsing: + return m_useRowControl; + break; + default: + assert( false ); + break; + } + return NULL; +} + +// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back +void CXuiSceneTrap::InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex /*= 0*/) +{ + int containerSize = menu->getSize() - (27 + 9); + + // TODO Inventory dimensions need defined as constants + m_trapControl->SetData( iPad, menu, 3, 3, 0 ); + + CXuiSceneAbstractContainer::InitDataAssociations(iPad, menu, containerSize); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Trap.h b/Minecraft.Client/Common/XUI/XUI_Scene_Trap.h new file mode 100644 index 0000000..3008a7c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Trap.h @@ -0,0 +1,62 @@ +#pragma once +using namespace std; +#include "..\Media\xuiscene_trap.h" +#include "XUI_Scene_AbstractContainer.h" +#include "..\UI\IUIScene_DispenserMenu.h" +#include "XUI_CustomMessages.h" + +class Container; +class DispenserTileEntity; + +//-------------------------------------------------------------------------------------- +// Scene implementation class. +//-------------------------------------------------------------------------------------- +class CXuiSceneTrap : public CXuiSceneAbstractContainer, public IUIScene_DispenserMenu +{ +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CXuiSceneTrap, L"CXuiSceneTrap", XUI_CLASS_SCENE ) + +protected: + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_TIMER( OnTimer ) // Poll stick input on a timer. + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + // Common to all abstract container scenes + MAP_CONTROL(IDC_Group, m_sceneGroup) + BEGIN_MAP_CHILD_CONTROLS( m_sceneGroup ) + + MAP_OVERRIDE(IDC_Inventory, m_inventoryControl) + MAP_OVERRIDE(IDC_UseRow, m_useRowControl) + MAP_OVERRIDE(IDC_Pointer, m_pointerControl) + MAP_CONTROL(IDC_InventoryText,m_InventoryText) + + MAP_CONTROL(IDC_DispenserText,m_DispenserText) + MAP_OVERRIDE(IDC_Trap, m_trapControl) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + //HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) {return S_OK;} + + virtual void InitDataAssociations(int iPad, AbstractContainerMenu *menu, int startIndex = 0); +private: + CXuiCtrlSlotList* m_trapControl; + CXuiControl m_sceneGroup; + CXuiControl m_DispenserText; + + virtual CXuiControl* GetSectionControl( ESceneSection eSection ); + virtual CXuiCtrlSlotList* GetSectionSlotList( ESceneSection eSection ); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Win.cpp b/Minecraft.Client/Common/XUI/XUI_Scene_Win.cpp new file mode 100644 index 0000000..0f0c678 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Win.cpp @@ -0,0 +1,302 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\..\Minecraft.h" +#include "..\..\Common\Tutorial\TutorialMode.h" +#include "..\..\Font.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\..\Minecraft.World\SharedConstants.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "XUI_Scene_Win.h" + +BYTE CScene_Win::s_winUserIndex = 0; + +const float CScene_Win::AUTO_SCROLL_SPEED = 1.0f; +const float CScene_Win::PLAYER_SCROLL_SPEED = 3.0f; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Win::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + m_bIgnoreInput = false; + + MapChildControls(); + + // Display the tooltips + ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_CONTINUE); + + wstring halfScreenLineBreaks; + + if(RenderManager.IsHiDef()) + { + // HD - 17 line page + halfScreenLineBreaks = L"










"; + } + else + { + // 480 - 14 line page + halfScreenLineBreaks = L"







"; + } + + noNoiseString = L""; + noNoiseString.append(halfScreenLineBreaks); + noNoiseString.append(halfScreenLineBreaks); + noNoiseString.append( app.GetString(IDS_WIN_TEXT) ); + noNoiseString.append( app.GetString(IDS_WIN_TEXT_PART_2) ); + noNoiseString.append( app.GetString(IDS_WIN_TEXT_PART_3) ); + + noNoiseString.append(halfScreenLineBreaks); + + noNoiseString.append( L"" ); + + noNoiseString = app.FormatHTMLString(m_iPad, noNoiseString, 0xff000000); + + Random random(8124371); + int found=(int)noNoiseString.find_first_of(L"{"); + int length; + while (found!=string::npos) + { + length = random.nextInt(4) + 3; + m_noiseLengths.push_back(length); + found=(int)noNoiseString.find_first_of(L"{",found+1); + } + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if(pMinecraft->localplayers[s_winUserIndex] != NULL) + { + noNoiseString = replaceAll(noNoiseString,L"{*PLAYER*}",pMinecraft->localplayers[s_winUserIndex]->name); + } + else + { + noNoiseString = replaceAll(noNoiseString,L"{*PLAYER*}",pMinecraft->localplayers[ProfileManager.GetPrimaryPad()]->name); + } + + updateNoise(); + + m_htmlControl.SetText(noiseString.c_str()); + + //wstring result = m_htmlControl.GetText(); + + //wcout << result.c_str(); + + m_scrollDir = 1; + HRESULT hr = XuiHtmlControlSetSmoothScroll(m_htmlControl.m_hObj, XUI_SMOOTHSCROLL_VERTICAL,TRUE,AUTO_SCROLL_SPEED,1.0f,AUTO_SCROLL_SPEED); + XuiHtmlControlVScrollBy(m_htmlControl.m_hObj,m_scrollDir * 1000); + + SetTimer(0,200); + + return S_OK; +} + +HRESULT CScene_Win::OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled) +{ + if(!TreeHasFocus()) return S_OK; + + // This is required to animate the "noise" strings in the text, however this makes scrolling really jumpy + // as well as causing line lengths to change as we do not have a monspaced font. Disabling for now. +#if 0 + updateNoise(); + + XUIHtmlScrollInfo scrollInfo; + HRESULT hr = m_htmlControl.GetVScrollInfo(&scrollInfo); + m_htmlControl.SetText(noiseString.c_str()); + //wcout << noiseString.c_str(); + //hr = m_htmlControl.SetVScrollPos(scrollInfo.nPos+2); + hr = m_htmlControl.SetVScrollPos(scrollInfo.nPos); +#endif + XuiHtmlControlVScrollBy(m_htmlControl.m_hObj,m_scrollDir * 1000); + + return S_OK; +} + +HRESULT CScene_Win::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_START: + case VK_ESCAPE: + { + KillTimer(0); + m_bIgnoreInput = true; + Minecraft *pMinecraft = Minecraft::GetInstance(); + app.CloseAllPlayersXuiScenes(); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if(pMinecraft->localplayers[i] != NULL) + { + app.SetAction(i,eAppAction_Respawn); + } + } + + // Show the other players scenes + CXuiSceneBase::ShowOtherPlayersBaseScene(pInputData->UserIndex, true); + // This just allows it to be shown + if(pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) pMinecraft->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(true); + ui.UpdatePlayerBasePositions(); + + rfHandled = TRUE; + } + break; + case VK_PAD_RTHUMB_UP: + case VK_PAD_LTHUMB_UP: + m_scrollDir = -1; + // Fall through + case VK_PAD_RTHUMB_DOWN: + case VK_PAD_LTHUMB_DOWN: + { + HRESULT hr = XuiHtmlControlSetSmoothScroll(m_htmlControl.m_hObj, XUI_SMOOTHSCROLL_VERTICAL,TRUE,AUTO_SCROLL_SPEED,1.0f,PLAYER_SCROLL_SPEED); + } + break; + } + + return S_OK; +} + +HRESULT CScene_Win::OnKeyUp(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + switch(pInputData->dwKeyCode) + { + case VK_PAD_RTHUMB_UP: + case VK_PAD_LTHUMB_UP: + case VK_PAD_RTHUMB_DOWN: + case VK_PAD_LTHUMB_DOWN: + { + m_scrollDir = 1; + HRESULT hr = XuiHtmlControlSetSmoothScroll(m_htmlControl.m_hObj, XUI_SMOOTHSCROLL_VERTICAL,TRUE,AUTO_SCROLL_SPEED,1.0f,AUTO_SCROLL_SPEED); + } + break; + } + + return S_OK; +} + +HRESULT CScene_Win::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +void CScene_Win::updateNoise() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + noiseString = noNoiseString; + + int length = 0; + wchar_t replacements[64]; + wstring replaceString = L""; + wchar_t randomChar = L'a'; + Random *random = pMinecraft->font->random; + + bool darken = false; + + wstring tag = L"{*NOISE*}"; + + AUTO_VAR(it, m_noiseLengths.begin()); + int found=(int)noiseString.find_first_of(L"{"); + while (found!=string::npos && it != m_noiseLengths.end() ) + { + length = *it; + ++it; + + replaceString = L""; + for(int i = 0; i < length; ++i) + { + randomChar = SharedConstants::acceptableLetters[random->nextInt((int)SharedConstants::acceptableLetters.length())]; + + wstring randomCharStr = L""; + randomCharStr.push_back(randomChar); + if(randomChar == L'<') + { + randomCharStr = L"<"; + } + else if (randomChar == L'>' ) + { + randomCharStr = L">"; + } + else if(randomChar == L'"') + { + randomCharStr = L"""; + } + else if(randomChar == L'&') + { + randomCharStr = L"&"; + } + else if(randomChar == L'\\') + { + randomCharStr = L"\\\\"; + } + else if(randomChar == L'{') + { + randomCharStr = L"}"; + } + + int randomVal = random->nextInt(2); + eMinecraftColour colour = eHTMLColor_8; + if(randomVal == 1) colour = eHTMLColor_9; + else if(randomVal == 2) colour = eHTMLColor_a; + ZeroMemory(replacements,64*sizeof(wchar_t)); + swprintf(replacements,64,L"%ls",app.GetHTMLColour(colour),randomCharStr.c_str()); + replaceString.append(replacements); + } + + noiseString.replace( found, tag.length(), replaceString ); + + //int pos = 0; + //do { + // pos = random->nextInt(SharedConstants::acceptableLetters.length()); + //} while (pMinecraft->font->charWidths[ch + 32] != pMinecraft->font->charWidths[pos + 32]); + //ib.put(listPos + 256 + random->nextInt(2) + 8 + (darken ? 16 : 0)); + //ib.put(listPos + pos + 32); + + found=(int)noiseString.find_first_of(L"{",found+1); + } +} + +HRESULT CScene_Win::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + SetTimer(0,200); + + // turn off the gamertags in splitscreen for the primary player, since they are about to be made fullscreen + CXuiSceneBase::HideAllGameUIElements(); + + // Hide the other players scenes + CXuiSceneBase::ShowOtherPlayersBaseScene(ProfileManager.GetPrimaryPad(), false); + + // This just allows it to be shown + if(Minecraft::GetInstance()->localgameModes[ProfileManager.GetPrimaryPad()] != NULL) Minecraft::GetInstance()->localgameModes[ProfileManager.GetPrimaryPad()]->getTutorial()->showTutorialPopup(false); + + // Temporarily make this scene fullscreen + CXuiSceneBase::SetPlayerBaseScenePosition( ProfileManager.GetPrimaryPad(), CXuiSceneBase::e_BaseScene_Fullscreen ); + + // Display the tooltips + ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_CONTINUE); + + XuiHtmlControlVScrollBy(m_htmlControl.m_hObj,m_scrollDir * 1000); + + return S_OK; +} + +HRESULT CScene_Win::OnNavForward(XUIMessageNavForward *pNavForwardData, BOOL& bHandled) +{ + XuiHtmlControlVScrollBy(m_htmlControl.m_hObj,0); + XUIHtmlScrollInfo scrollInfo; + HRESULT hr = m_htmlControl.GetVScrollInfo(&scrollInfo); + hr = m_htmlControl.SetVScrollPos(scrollInfo.nPos); + KillTimer(0); + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Scene_Win.h b/Minecraft.Client/Common/XUI/XUI_Scene_Win.h new file mode 100644 index 0000000..7a5e9da --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Scene_Win.h @@ -0,0 +1,58 @@ +#pragma once + +#include "../media/xuiscene_Win.h" +#include "XUI_CustomMessages.h" + +class CScene_Win : public CXuiSceneImpl +{ +private: + static const float AUTO_SCROLL_SPEED; + static const float PLAYER_SCROLL_SPEED; +protected: + CXuiHtmlControl m_htmlControl; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TIMER( OnTimer ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_KEYUP(OnKeyUp) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_NAV_FORWARD(OnNavForward) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_HtmlControl, m_htmlControl) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnTimer( XUIMessageTimer *pXUIMessageTimer, BOOL &bHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnKeyUp(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnNavForward(XUIMessageNavForward *pNavForwardData, BOOL& bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Win, L"CScene_Win", XUI_CLASS_SCENE ) + +private: + bool m_bIgnoreInput; + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + wstring noNoiseString; + wstring noiseString; + int m_scrollDir; + + vector m_noiseLengths; + + void updateNoise(); + +public: + static BYTE s_winUserIndex; + static void setWinUserIndex(BYTE winUserIndex) { s_winUserIndex = winUserIndex; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsAll.cpp b/Minecraft.Client/Common/XUI/XUI_SettingsAll.cpp new file mode 100644 index 0000000..c6dc711 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsAll.cpp @@ -0,0 +1,227 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\XUI\XUI_SettingsAll.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsAll::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + //WCHAR TempString[256]; + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + MapChildControls(); + + XuiControlSetText(m_Buttons[BUTTON_ALL_OPTIONS],app.GetString(IDS_OPTIONS)); + XuiControlSetText(m_Buttons[BUTTON_ALL_AUDIO],app.GetString(IDS_AUDIO)); + XuiControlSetText(m_Buttons[BUTTON_ALL_CONTROL],app.GetString(IDS_CONTROL)); + XuiControlSetText(m_Buttons[BUTTON_ALL_GRAPHICS],app.GetString(IDS_GRAPHICS)); + XuiControlSetText(m_Buttons[BUTTON_ALL_UI],app.GetString(IDS_USER_INTERFACE)); + XuiControlSetText(m_Buttons[BUTTON_ALL_RESETTODEFAULTS],app.GetString(IDS_RESET_TO_DEFAULTS)); + + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + D3DXVECTOR3 vec,vecControl; + m_Buttons[BUTTON_ALL_AUDIO].SetShow(FALSE); + m_Buttons[BUTTON_ALL_AUDIO].SetEnable(FALSE); + m_Buttons[BUTTON_ALL_GRAPHICS].SetShow(FALSE); + m_Buttons[BUTTON_ALL_GRAPHICS].SetEnable(FALSE); + + float fButtonWidth, fButtonHeight; + m_Buttons[BUTTON_ALL_GRAPHICS].GetPosition(&vecControl); + m_Buttons[BUTTON_ALL_RESETTODEFAULTS].GetBounds(&fButtonWidth, &fButtonHeight); + m_Buttons[BUTTON_ALL_RESETTODEFAULTS].SetPosition(&vecControl); + + m_Buttons[BUTTON_ALL_CONTROL].GetPosition(&vec); + m_Buttons[BUTTON_ALL_UI].SetPosition(&vec); + + m_Buttons[BUTTON_ALL_AUDIO].GetPosition(&vec); + m_Buttons[BUTTON_ALL_CONTROL].SetPosition(&vec); + + // Resize the whole scene + float fWidth, fHeight; + GetBounds(&fWidth, &fHeight); + SetBounds(fWidth, vecControl.y+fButtonHeight); + } + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + + + + +HRESULT CScene_SettingsAll::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +HRESULT CScene_SettingsAll::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsAll::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //HRESULT hr; + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + } + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsAll::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + unsigned int uiButtonCounter=0; + + while((uiButtonCounterUserIndex,eUIScene_SettingsOptionsMenu); + break; + case BUTTON_ALL_AUDIO: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SettingsAudioMenu); + break; + case BUTTON_ALL_CONTROL: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SettingsControlMenu); + break; + case BUTTON_ALL_GRAPHICS: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SettingsGraphicsMenu); + break; + case BUTTON_ALL_UI: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_SettingsUIMenu); + break; + case BUTTON_ALL_RESETTODEFAULTS: + { + // check they really want to do this + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + + StorageManager.RequestMessageBox(IDS_DEFAULTS_TITLE, IDS_DEFAULTS_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_SettingsAll::ResetDefaultsDialogReturned,this, app.GetStringTable()); + } + break; + + } + + rfHandled=TRUE; + return S_OK; +} + +HRESULT CScene_SettingsAll::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + if(!RenderManager.IsHiDef() || app.GetLocalPlayerCount()>1) + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsAll::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +int CScene_SettingsAll::ResetDefaultsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + //CScene_SettingsAll* pClass = (CScene_SettingsAll*)pParam; + + // results switched for this dialog + if(result==C4JStorage::EMessage_ResultDecline) + { + app.SetDefaultOptions(ProfileManager.GetDashboardProfileSettings(iPad),iPad); + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + app.CheckGameSettingsChanged(true,iPad); + } + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsAll.h b/Minecraft.Client/Common/XUI/XUI_SettingsAll.h new file mode 100644 index 0000000..217f6c7 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsAll.h @@ -0,0 +1,59 @@ +#pragma once + +#include "../media/xuiscene_settings_all.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_CustomMessages.h" + +#define BUTTON_ALL_OPTIONS 0 +#define BUTTON_ALL_AUDIO 1 +#define BUTTON_ALL_CONTROL 2 +#define BUTTON_ALL_GRAPHICS 4 +#define BUTTON_ALL_UI 5 +#define BUTTON_ALL_RESETTODEFAULTS 6 +#define BUTTONS_ALL_MAX BUTTON_ALL_RESETTODEFAULTS + 1 + +class CScene_SettingsAll : public CXuiSceneImpl +{ +protected: + + CXuiControl m_Buttons[BUTTONS_ALL_MAX]; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiButtonOptions, m_Buttons[BUTTON_ALL_OPTIONS]) + MAP_CONTROL(IDC_XuiButtonAudio, m_Buttons[BUTTON_ALL_AUDIO]) + MAP_CONTROL(IDC_XuiButtonControl, m_Buttons[BUTTON_ALL_CONTROL]) + MAP_CONTROL(IDC_XuiButtonGraphics, m_Buttons[BUTTON_ALL_GRAPHICS]) + MAP_CONTROL(IDC_XuiButtonUI, m_Buttons[BUTTON_ALL_UI]) + MAP_CONTROL(IDC_XuiButtonResetToDefaults, m_Buttons[BUTTON_ALL_RESETTODEFAULTS]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + static int ResetDefaultsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + int m_iPad; + + D3DXVECTOR3 m_OriginalPosition; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SettingsAll, L"CScene_SettingsAll", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsAudio.cpp b/Minecraft.Client/Common/XUI/XUI_SettingsAudio.cpp new file mode 100644 index 0000000..0f0fe2e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsAudio.cpp @@ -0,0 +1,263 @@ +#include "stdafx.h" +#include "..\XUI\XUI_SettingsAudio.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsAudio::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + WCHAR TempString[256]; + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + MapChildControls(); + + // Display the tooltips + HRESULT hr = S_OK; + HXUIOBJ hSlider; + + m_SliderA[SLIDER_SETTINGS_MUSIC].SetValue(app.GetGameSettings(m_iPad,eGameSetting_MusicVolume)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_MUSIC ),app.GetGameSettings(m_iPad,eGameSetting_MusicVolume)); + m_SliderA[SLIDER_SETTINGS_MUSIC].SetText(TempString); + + m_SliderA[SLIDER_SETTINGS_SOUND].SetValue(app.GetGameSettings(m_iPad,eGameSetting_SoundFXVolume)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SOUND ),app.GetGameSettings(m_iPad,eGameSetting_SoundFXVolume)); + m_SliderA[SLIDER_SETTINGS_SOUND].SetText(TempString); + + // only the primary player gets to change the music and sound settings + // only the primary player gets to change the gamma and splitscreen settings + // only the primary player gets to change the difficulty settings + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + D3DXVECTOR3 vec; + + m_SliderA[SLIDER_SETTINGS_MUSIC].GetPosition(&vec); + m_SliderA[SLIDER_SETTINGS_MUSIC].SetEnable(FALSE); + m_SliderA[SLIDER_SETTINGS_MUSIC].SetShow(FALSE); + hr=XuiElementGetChildById(m_SliderA[SLIDER_SETTINGS_MUSIC].m_hObj,L"XuiSlider",&hSlider); + XuiElementSetShow(hSlider,FALSE); + XuiControlSetEnable(hSlider,FALSE); + m_SliderA[SLIDER_SETTINGS_SOUND].SetEnable(FALSE); + m_SliderA[SLIDER_SETTINGS_SOUND].SetShow(FALSE); + hr=XuiElementGetChildById(m_SliderA[SLIDER_SETTINGS_SOUND].m_hObj,L"XuiSlider",&hSlider); + XuiElementSetShow(hSlider,FALSE); + XuiControlSetEnable(hSlider,FALSE); + } + + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsAudio::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderA[SLIDER_SETTINGS_MUSIC].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_MusicVolume,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_MUSIC ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_MUSIC].SetText(TempString); + } + else if(hObjSource==m_SliderA[SLIDER_SETTINGS_SOUND].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_SoundFXVolume,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SOUND ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_SOUND].SetText(TempString); + } + + + return S_OK; +} + + +HRESULT CScene_SettingsAudio::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + // Not in this scene - app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsAudio::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SettingsAudio::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsAudio::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + HRESULT hr; + WCHAR TempString[256]; + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fMaxLen=0.0f; + + // sliders first + HXUIOBJ hSlider,hVisual,hText;//,hCheckboxText; + XUIRect xuiRect; + float fWidth,fHeight,fTemp; + D3DXVECTOR3 vec,vecCheckboxText,vSlider,vecCheckbox; + + // don't display values on these - we handle that + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + if(fWidth>fMaxLen) fMaxLen=fWidth; + } + + if(fMaxLen1) + { + // scene width needs to be more that the text width on buttons + fWidth=vSlider.x; + vec.x=floorf((640.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + else + { + fWidth=vSlider.x; + vec.x=floorf((1280.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + // Need to refresh the scenes visual since the object size has now changed + XuiControlAttachVisual(m_hObj); + + // centre is vec.x+(fWidth/2) + for(int i=0;ilevel==NULL); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + return S_OK; +} + +HRESULT CScene_SettingsAudio::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsAudio.h b/Minecraft.Client/Common/XUI/XUI_SettingsAudio.h new file mode 100644 index 0000000..c77f579 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsAudio.h @@ -0,0 +1,53 @@ +#pragma once + +#include "../media/xuiscene_settings_audio.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_CustomMessages.h" + +#define SLIDER_SETTINGS_MUSIC 0 +#define SLIDER_SETTINGS_SOUND 1 + +#define SLIDER_SETTINGS_AUDIO_MAX SLIDER_SETTINGS_SOUND + 1 +class CScene_SettingsAudio : public CXuiSceneImpl +{ +protected: + CXuiCtrlSliderWrapper m_SliderA[SLIDER_SETTINGS_AUDIO_MAX]; + + CXuiControl m_ButtonOptions; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiSliderMusic, m_SliderA[SLIDER_SETTINGS_MUSIC]) + MAP_CONTROL(IDC_XuiSliderSound, m_SliderA[SLIDER_SETTINGS_SOUND]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SettingsAudio, L"CScene_SettingsAudio", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsControl.cpp b/Minecraft.Client/Common/XUI/XUI_SettingsControl.cpp new file mode 100644 index 0000000..7e2835a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsControl.cpp @@ -0,0 +1,241 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\XUI\XUI_SettingsControl.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsControl::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + WCHAR TempString[256]; + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + MapChildControls(); + + // Display the tooltips + + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INGAME].SetValue(app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INGAME ),app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame)); + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INGAME].SetText(TempString); + + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INMENU].SetValue(app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INMENU ),app.GetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu)); + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INMENU].SetText(TempString); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsControl::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INGAME].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Sensitivity_InGame,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INGAME ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INGAME].SetText(TempString); + } + else if(hObjSource==m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INMENU].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Sensitivity_InMenu,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_SENSITIVITY_INMENU ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INMENU].SetText(TempString); + } + + return S_OK; +} + + +HRESULT CScene_SettingsControl::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + // not in this scene - app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsControl::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SettingsControl::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsControl::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + HRESULT hr; + WCHAR TempString[256]; + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fMaxLen=0.0f; + + // sliders first + HXUIOBJ hSlider,hVisual,hText;//,hCheckboxText; + XUIRect xuiRect; + float fWidth,fHeight,fTemp; + D3DXVECTOR3 vec,vecCheckboxText,vSlider,vecCheckbox; + + // don't display values on these - we handle that + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + if(fWidth>fMaxLen) fMaxLen=fWidth; + } + + if(fMaxLen1) + { + // scene width needs to be more that the text width on buttons + fWidth=vSlider.x; + vec.x=floorf((640.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + else + { + fWidth=vSlider.x; + vec.x=floorf((1280.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + + // centre is vec.x+(fWidth/2) + for(int i=0;ilevel==NULL); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + return S_OK; +} + +HRESULT CScene_SettingsControl::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsControl.h b/Minecraft.Client/Common/XUI/XUI_SettingsControl.h new file mode 100644 index 0000000..67e4369 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsControl.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../media/xuiscene_settings_control.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_CustomMessages.h" + +#define SLIDER_SETTINGS_SENSITIVITY_INGAME 0 +#define SLIDER_SETTINGS_SENSITIVITY_INMENU 1 + +#define SLIDER_SETTINGS_CONTROL_MAX SLIDER_SETTINGS_SENSITIVITY_INMENU + 1 +class CScene_SettingsControl : public CXuiSceneImpl +{ +protected: + CXuiCtrlSliderWrapper m_SliderA[SLIDER_SETTINGS_CONTROL_MAX]; + + CXuiControl m_ButtonOptions; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiSliderSensitivityInGame, m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INGAME]) + MAP_CONTROL(IDC_XuiSliderSensitivityInMenu, m_SliderA[SLIDER_SETTINGS_SENSITIVITY_INMENU]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SettingsControl, L"CScene_SettingsControl", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.cpp b/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.cpp new file mode 100644 index 0000000..48a933e --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.cpp @@ -0,0 +1,333 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\XUI\XUI_SettingsGraphics.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsGraphics::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + WCHAR TempString[256]; + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bIsPrimaryPad=(ProfileManager.GetPrimaryPad()==m_iPad); + + MapChildControls(); + + // Display the tooltips + + m_Clouds.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_Clouds)!=0)?TRUE:FALSE); + m_Clouds.SetText(app.GetString( IDS_CHECKBOX_RENDER_CLOUDS )); + + m_SliderA[SLIDER_SETTINGS_GAMMA].SetValue(app.GetGameSettings(m_iPad,eGameSetting_Gamma)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),app.GetGameSettings(m_iPad,eGameSetting_Gamma)); + m_SliderA[SLIDER_SETTINGS_GAMMA].SetText(TempString); + + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].SetValue(app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),app.GetGameSettings(m_iPad,eGameSetting_InterfaceOpacity)); + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].SetText(TempString); + + m_BedrockFog.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_BedrockFog)!=0)?TRUE:FALSE); + m_BedrockFog.SetText(app.GetString( IDS_CHECKBOX_RENDER_BEDROCKFOG )); + + m_CustomSkinAnim.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_CustomSkinAnim)!=0)?TRUE:FALSE); + m_CustomSkinAnim.SetText(app.GetString( IDS_CHECKBOX_CUSTOM_SKIN_ANIM )); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + // If the game has started, then you need to be the host to change the in-game gamertags + if(bIsPrimaryPad) + { + // are we the game host? If not, we need to remove the bedrockfog setting + if(!g_NetworkManager.IsHost()) + { + // we are the primary player on this machine, but not the game host + D3DXVECTOR3 vec; + // hide the in-game bedrock fog setting + m_BedrockFog.SetShow(FALSE); + + // m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY] -> m_SliderA[SLIDER_SETTINGS_GAMMA] + // m_SliderA[SLIDER_SETTINGS_GAMMA] -> m_CustomSkinAnim + // m_CustomSkinAnim -> m_BedrockFog + + XuiElementGetPosition(m_SliderA[SLIDER_SETTINGS_GAMMA],&vec); + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].SetPosition(&vec); + + XuiElementGetPosition(m_CustomSkinAnim,&vec); + m_SliderA[SLIDER_SETTINGS_GAMMA].SetPosition(&vec); + + XuiElementGetPosition(m_BedrockFog,&vec); + m_CustomSkinAnim.SetPosition(&vec); + + // reduce the size of the background + float fWidth, fHeight, fbgnWidth, fBgnHeight; + m_BedrockFog.GetBounds(&fWidth, &fHeight); + GetBounds(&fbgnWidth, &fBgnHeight); + fBgnHeight-=fHeight; + SetBounds(fbgnWidth, fBgnHeight); + } + } + else + { + // We shouldn't have the bedrock fog option, or the m_CustomSkinAnim option + m_BedrockFog.SetShow(FALSE); + m_CustomSkinAnim.SetShow(FALSE); + + D3DXVECTOR3 vec,vecGamma,vecOpacity; + float fSliderYDiff; + + // m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY] -> m_BedrockFog + // m_SliderA[SLIDER_SETTINGS_GAMMA] -> m_BedrockFog + + m_SliderA[SLIDER_SETTINGS_GAMMA].GetPosition(&vecGamma); + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].GetPosition(&vecOpacity); + fSliderYDiff=vecOpacity.y-vecGamma.y; + + XuiElementGetPosition(m_BedrockFog,&vec); + m_SliderA[SLIDER_SETTINGS_GAMMA].SetPosition(&vec); + vec.y+=fSliderYDiff; + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].SetPosition(&vec); + + // reduce the size of the background + float fWidth, fHeight, fbgnWidth, fBgnHeight; + GetBounds(&fbgnWidth, &fBgnHeight); + m_BedrockFog.GetBounds(&fWidth, &fHeight); + fBgnHeight-=fHeight; + m_CustomSkinAnim.GetBounds(&fWidth, &fHeight); + fBgnHeight-=fHeight; + SetBounds(fbgnWidth, fBgnHeight); + } + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsGraphics::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderA[SLIDER_SETTINGS_GAMMA].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Gamma,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_GAMMA ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_GAMMA].SetText(TempString); + } + else if(hObjSource==m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_InterfaceOpacity,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d%%", app.GetString( IDS_SLIDER_INTERFACEOPACITY ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY].SetText(TempString); + } + + return S_OK; +} + + +HRESULT CScene_SettingsGraphics::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + // not in this scene - app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_Clouds,m_Clouds.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_BedrockFog,m_BedrockFog.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_CustomSkinAnim,m_CustomSkinAnim.IsChecked()?1:0); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsGraphics::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SettingsGraphics::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsGraphics::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + HRESULT hr; + WCHAR TempString[256]; + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fMaxLen=0.0f; + + // sliders first + HXUIOBJ hSlider,hVisual,hText,hCheckboxText; + XUIRect xuiRect; + float fWidth,fHeight,fTemp; + D3DXVECTOR3 vec,vecCheckboxText,vSlider,vecCheckbox; + + // don't display values on these - we handle that + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + if(fWidth>fMaxLen) fMaxLen=fWidth; + } + + // now the clouds checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hText, m_Clouds.GetText(), &xuiRect); + hr=XuiTextPresenterMeasureText(hCheckboxText, m_Clouds.GetText(), &xuiRect); + m_Clouds.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + if(fMaxLen1) + { + // scene width needs to be more that the text width on buttons + fWidth=vSlider.x; + vec.x=floorf((640.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + else + { + fWidth=vSlider.x; + vec.x=floorf((1280.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + // Need to refresh the scenes visual since the object size has now changed + XuiControlAttachVisual(m_hObj); + + // centre is vec.x+(fWidth/2) + for(int i=0;ilevel==NULL); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + return S_OK; +} + +HRESULT CScene_SettingsGraphics::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.h b/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.h new file mode 100644 index 0000000..224dedd --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsGraphics.h @@ -0,0 +1,60 @@ +#pragma once + +#include "../media/xuiscene_settings_graphics.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_CustomMessages.h" + + +#define SLIDER_SETTINGS_GAMMA 0 +#define SLIDER_SETTINGS_INTERFACE_OPACITY 1 + + +#define SLIDER_SETTINGS_GRAPHICS_MAX SLIDER_SETTINGS_INTERFACE_OPACITY + 1 +class CScene_SettingsGraphics : public CXuiSceneImpl +{ +protected: + CXuiCtrlSliderWrapper m_SliderA[SLIDER_SETTINGS_GRAPHICS_MAX]; + CXuiCheckbox m_Clouds; + CXuiCheckbox m_BedrockFog; + CXuiCheckbox m_CustomSkinAnim; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiClouds, m_Clouds) + MAP_CONTROL(IDC_XuiBedrockFog, m_BedrockFog) + MAP_CONTROL(IDC_XuiCustomSkinAnim, m_CustomSkinAnim) + MAP_CONTROL(IDC_XuiSliderGamma, m_SliderA[SLIDER_SETTINGS_GAMMA]) + MAP_CONTROL(IDC_XuiSliderInterfaceOpacity, m_SliderA[SLIDER_SETTINGS_INTERFACE_OPACITY]) + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SettingsGraphics, L"CScene_SettingsGraphics", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsOptions.cpp b/Minecraft.Client/Common/XUI/XUI_SettingsOptions.cpp new file mode 100644 index 0000000..526847c --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsOptions.cpp @@ -0,0 +1,508 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "..\XUI\XUI_SettingsOptions.h" + +int CScene_SettingsOptions::m_iDifficultySettingA[4]= +{ + IDS_DIFFICULTY_PEACEFUL, + IDS_DIFFICULTY_EASY, + IDS_DIFFICULTY_NORMAL, + IDS_DIFFICULTY_HARD +}; + +int CScene_SettingsOptions::m_iDifficultyTitleSettingA[4]= +{ + IDS_DIFFICULTY_TITLE_PEACEFUL, + IDS_DIFFICULTY_TITLE_EASY, + IDS_DIFFICULTY_TITLE_NORMAL, + IDS_DIFFICULTY_TITLE_HARD +}; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsOptions::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + WCHAR TempString[256]; + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bPrimaryPlayer = ProfileManager.GetPrimaryPad()==m_iPad; + + MapChildControls(); + + XuiControlSetText(m_ViewBob,app.GetString(IDS_VIEW_BOBBING)); + XuiControlSetText(m_Hints,app.GetString(IDS_HINTS)); + XuiControlSetText(m_Tooltips,app.GetString(IDS_IN_GAME_TOOLTIPS)); + XuiControlSetText(m_InGameGamertags,app.GetString(IDS_IN_GAME_GAMERTAGS)); + XuiControlSetText(m_MashUpWorlds,app.GetString(IDS_UNHIDE_MASHUP_WORLDS)); + + // check if we should display the mash-up option + if(bNotInGame && app.GetMashupPackWorlds(m_iPad)!=0xFFFFFFFF) + { + // the mash-up option is needed + m_bMashUpWorldsUnhideOption=true; + m_MashUpWorlds.SetShow(TRUE); + } + else + { + m_bMashUpWorldsUnhideOption=false; + m_MashUpWorlds.SetShow(FALSE); + } + + // Display the tooltips + HRESULT hr = S_OK; + HXUIOBJ hSlider; + + unsigned char ucValue=app.GetGameSettings(m_iPad,eGameSetting_Autosave); + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetValue(ucValue); + if(ucValue==0) + { + swprintf( (WCHAR *)TempString, 256, L"%ls", app.GetString( IDS_SLIDER_AUTOSAVE_OFF )); + } + else + { + swprintf( (WCHAR *)TempString, 256, L"%ls: %d %ls", app.GetString( IDS_SLIDER_AUTOSAVE ),ucValue*15, app.GetString( IDS_MINUTES )); + } + + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetText(TempString); + + + m_ViewBob.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_ViewBob)!=0)?TRUE:FALSE); + m_InGameGamertags.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_GamertagsVisible)!=0)?TRUE:FALSE); + m_Hints.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_Hints)!=0)?TRUE:FALSE); + m_Tooltips.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_Tooltips)!=0)?TRUE:FALSE); + + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetValue(app.GetGameSettings(m_iPad,eGameSetting_Difficulty)); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)])); + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetText(TempString); + + + wstring wsText=app.GetString(m_iDifficultySettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)]); + int size = 14; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = 12; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White),size); + wsText= startTags + wsText; + + m_DifficultyText.SetText(wsText.c_str()); + + + // If you are in-game, only the game host can change in-game gamertags, and you can't change difficulty + // only the primary player gets to change the autosave and difficulty settings + + bool bRemoveDifficulty=false; + bool bRemoveAutosave=false; + bool bRemoveInGameGamertags=false; + float fRemoveHeight=0.0f,fWidth,fHeight; + float fSlidersMoveUp=0.0f; + + if(!bPrimaryPlayer) + { + bRemoveDifficulty=true; + bRemoveAutosave=true; + bRemoveInGameGamertags=true; + } + + if(!bNotInGame) // in the game + { + bRemoveDifficulty=true; + if(!g_NetworkManager.IsHost()) + { + bRemoveAutosave=true; + bRemoveInGameGamertags=true; + } + } + + D3DXVECTOR3 vec1,vec2; + XuiElementGetPosition(m_ViewBob,&vec1); + XuiElementGetPosition(m_Hints,&vec2); + + float fCheckboxHeight=vec2.y-vec1.y; + + if(bRemoveDifficulty) + { + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetEnable(FALSE); + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetShow(FALSE); + m_DifficultyText.SetShow(FALSE); + hr=XuiElementGetChildById(m_SliderA[SLIDER_SETTINGS_DIFFICULTY].m_hObj,L"XuiSlider",&hSlider); + XuiElementSetShow(hSlider,FALSE); + XuiControlSetEnable(hSlider,FALSE); + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].GetBounds(&fWidth,&fHeight); + fRemoveHeight+=fHeight+4.0f; // add padding + } + else + { + wstring wsText=app.GetString(m_iDifficultySettingA[app.GetGameSettings(m_iPad,eGameSetting_Difficulty)]); + int size = 14; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = 12; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White),size); + wsText= startTags + wsText; + + m_DifficultyText.SetText(wsText.c_str()); + } + + if(bRemoveAutosave) + { + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetEnable(FALSE); + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetShow(FALSE); + hr=XuiElementGetChildById(m_SliderA[SLIDER_SETTINGS_AUTOSAVE].m_hObj,L"XuiSlider",&hSlider); + XuiElementSetShow(hSlider,FALSE); + XuiControlSetEnable(hSlider,FALSE); + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].GetBounds(&fWidth,&fHeight); + fRemoveHeight+=fHeight+4.0f; // add padding + } + + if(bRemoveInGameGamertags) + { + m_InGameGamertags.SetShow(FALSE); + m_InGameGamertags.SetEnable(FALSE); + m_InGameGamertags.GetBounds(&fWidth, &fHeight); + + // move the mash-up worlds option up + if(m_bMashUpWorldsUnhideOption) + { + D3DXVECTOR3 vec; + XuiElementGetPosition(m_InGameGamertags,&vec); + m_MashUpWorlds.SetPosition(&vec); + } + fRemoveHeight+=fCheckboxHeight;//fHeight+4.0f; // add padding + fSlidersMoveUp+=fCheckboxHeight; + } + + if(!m_bMashUpWorldsUnhideOption) + { + m_MashUpWorlds.SetShow(FALSE); + m_MashUpWorlds.SetEnable(FALSE); + m_MashUpWorlds.GetBounds(&fWidth, &fHeight); + fRemoveHeight+=fCheckboxHeight; + fSlidersMoveUp+=fCheckboxHeight; + } + + + if(fRemoveHeight>0.0f) + { + D3DXVECTOR3 vec; + // autosave should move up by the height of the number of checkboxes hidden + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].GetPosition(&vec); + vec.y-=fSlidersMoveUp; + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetPosition(&vec); + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].GetPosition(&vec); + vec.y-=fSlidersMoveUp; + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetPosition(&vec); + m_DifficultyText.GetPosition(&vec); + vec.y-=fSlidersMoveUp; + m_DifficultyText.SetPosition(&vec); + + float fbgnWidth, fBgnHeight; + GetBounds(&fbgnWidth, &fBgnHeight); + fBgnHeight-=fRemoveHeight; + SetBounds(fbgnWidth, fBgnHeight); + } + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + // can't show the logo for 480 mode + if(!RenderManager.IsHiDef()) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + } + else + { + // can't show the logo for 480 mode + if(!RenderManager.IsHiDef()) + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + } + + return S_OK; +} + +HRESULT CScene_SettingsOptions::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderA[SLIDER_SETTINGS_AUTOSAVE].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Autosave,pNotifyValueChanged->nValue); + // Update the autosave timer + app.SetAutosaveTimerTime(); + + if(pNotifyValueChanged->nValue==0) + { + swprintf( (WCHAR *)TempString, 256, L"%ls", app.GetString( IDS_SLIDER_AUTOSAVE_OFF )); + } + else + { + app.SetAutosaveTimerTime(); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d %ls", app.GetString( IDS_SLIDER_AUTOSAVE ),pNotifyValueChanged->nValue*15, app.GetString( IDS_MINUTES )); + } + m_SliderA[SLIDER_SETTINGS_AUTOSAVE].SetText(TempString); + } + else if(hObjSource==m_SliderA[SLIDER_SETTINGS_DIFFICULTY].GetSlider() ) + { + app.SetGameSettings(m_iPad,eGameSetting_Difficulty,pNotifyValueChanged->nValue); + swprintf( (WCHAR *)TempString, 256, L"%ls: %ls", app.GetString( IDS_SLIDER_DIFFICULTY ),app.GetString(m_iDifficultyTitleSettingA[pNotifyValueChanged->nValue])); + m_SliderA[SLIDER_SETTINGS_DIFFICULTY].SetText(TempString); + + wstring wsText=app.GetString(m_iDifficultySettingA[pNotifyValueChanged->nValue]); + int size = 14; + if(!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) + { + size = 12; + } + wchar_t startTags[64]; + swprintf(startTags,64,L"",app.GetHTMLColour(eHTMLColor_White),size); + wsText= startTags + wsText; + m_DifficultyText.SetText(wsText.c_str()); + } + return S_OK; +} + + +HRESULT CScene_SettingsOptions::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_ViewBob,m_ViewBob.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_GamertagsVisible,m_InGameGamertags.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_Hints,m_Hints.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_Tooltips,m_Tooltips.IsChecked()?1:0); + + // the mashup option will only be shown if some worlds have been previously hidden + if(m_bMashUpWorldsUnhideOption && m_MashUpWorlds.IsChecked()) + { + // unhide all worlds + app.EnableMashupPackWorlds(m_iPad); + } + + // 4J-PB - don't action changes here or we might write to the profile on backing out here and then get a change in the settings all, and write again on backing out there + //app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsOptions::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SettingsOptions::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsOptions::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + HRESULT hr; + WCHAR TempString[256]; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fMaxLen=0.0f; + + + // sliders first + HXUIOBJ hSlider,hVisual,hText,hCheckboxText; + XUIRect xuiRect; + float fWidth,fHeight;//,fTemp; + D3DXVECTOR3 vec,vSlider,vecCheckboxText,vecCheckbox; + + // don't display values on these - we handle that + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + if(fWidth>fMaxLen) fMaxLen=fWidth; + } + + // now the VisibleOnMaps checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hCheckboxText, m_InGameGamertags.GetText(), &xuiRect); + m_InGameGamertags.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + // now the m_Hints checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hCheckboxText, m_Hints.GetText(), &xuiRect); + m_Hints.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + // now the m_Tooltips checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hCheckboxText, m_Tooltips.GetText(), &xuiRect); + m_Tooltips.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + if(fMaxLen1) + { + // scene width needs to be more that the text width on buttons + fWidth=vSlider.x; + vec.x=floorf((640.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + else + { + fWidth=vSlider.x; + vec.x=floorf((1280.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + // Need to refresh the scenes visual since the object size has now changed + XuiControlAttachVisual(m_hObj); + + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + if(bNotInGame) + { + float fDiffTextW; + // reposition the difficulty text - this is positioned from the scene, so will be a negative value + m_DifficultyText.GetPosition(&vec); + m_DifficultyText.GetBounds(&fDiffTextW,&fHeight); + vec.x=floor((fMaxTextLen+(fWidth*2.0f)-fDiffTextW)/2.0f); + m_DifficultyText.SetPosition(&vec); + } + + // centre is vec.x+(fWidth/2) + //for(int i=0;ipvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + bool bPrimaryPlayer = ProfileManager.GetPrimaryPad()==m_iPad; + + MapChildControls(); + + m_SplitScreen.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_SplitScreenVertical)!=0)?TRUE:FALSE); + m_SplitScreen.SetText(app.GetString( IDS_CHECKBOX_VERTICAL_SPLIT_SCREEN ) ); + + m_SplitScreenGamertags.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_DisplaySplitscreenGamertags)!=0)?TRUE:FALSE); + m_SplitScreenGamertags.SetText(app.GetString( IDS_CHECKBOX_DISPLAY_SPLITSCREENGAMERTAGS )); + + m_SliderA[SLIDER_SETTINGS_UISIZE].SetValue(app.GetGameSettings(m_iPad,eGameSetting_UISize)+1); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZE ),app.GetGameSettings(m_iPad,eGameSetting_UISize)+1); + m_SliderA[SLIDER_SETTINGS_UISIZE].SetText(TempString); + + m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].SetValue(app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1); + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZESPLITSCREEN ),app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1); + m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].SetText(TempString); + + m_DisplayHUD.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_DisplayHUD)!=0)?TRUE:FALSE); + m_DisplayHUD.SetText(app.GetString( IDS_CHECKBOX_DISPLAY_HUD )); + + m_DisplayHand.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_DisplayHand)!=0)?TRUE:FALSE); + m_DisplayHand.SetText(app.GetString( IDS_CHECKBOX_DISPLAY_HAND )); + + m_DeathMessages.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_DeathMessages)!=0)?TRUE:FALSE); + m_DeathMessages.SetText(app.GetString( IDS_CHECKBOX_DEATH_MESSAGES )); + + m_AnimatedCharacter.SetCheck( (app.GetGameSettings(m_iPad,eGameSetting_AnimatedCharacter)!=0)?TRUE:FALSE); + m_AnimatedCharacter.SetText(app.GetString( IDS_CHECKBOX_ANIMATED_CHARACTER )); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + // If the game has started, then you need to be the host to change the in-game gamertags + if(!bPrimaryPlayer) + { + // hide things we don't want the splitscreen player changing + m_SplitScreen.SetShow(FALSE); + m_SplitScreen.SetEnable(FALSE); + m_SplitScreenGamertags.SetShow(FALSE); + m_SplitScreenGamertags.SetEnable(FALSE); + + // move the sliders up, and resize the scene + float fRemoveHeight=0.0f, fHeight, fWidth; + D3DXVECTOR3 vec; + + m_SplitScreen.GetBounds(&fWidth, &fHeight); + fRemoveHeight+=fHeight+4.0f; // add padding + m_SplitScreenGamertags.GetBounds(&fWidth, &fHeight); + fRemoveHeight+=fHeight+4.0f; // add padding + + m_SliderA[SLIDER_SETTINGS_UISIZE].GetPosition(&vec); + vec.y-=fRemoveHeight; + m_SliderA[SLIDER_SETTINGS_UISIZE].SetPosition(&vec); + m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].GetPosition(&vec); + vec.y-=fRemoveHeight; + m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].SetPosition(&vec); + + GetBounds(&fWidth, &fHeight); + fHeight-=fRemoveHeight; + SetBounds(fWidth, fHeight); + } + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + } + + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, TRUE ); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsUI::OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ) +{ + WCHAR TempString[256]; + + if(hObjSource==m_SliderA[SLIDER_SETTINGS_UISIZE].GetSlider() ) + { + // slider is 1 to 3 + + // is this different from the current value? + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZE ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_UISIZE].SetText(TempString); + if(pNotifyValueChanged->nValue != app.GetGameSettings(m_iPad,eGameSetting_UISize)+1) + { + app.SetGameSettings(m_iPad,eGameSetting_UISize,pNotifyValueChanged->nValue-1); + // Apply the changes to the selected text position + CXuiSceneBase::UpdateSelectedItemPos(m_iPad); + } + } + else if(hObjSource==m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].GetSlider() ) + { + swprintf( (WCHAR *)TempString, 256, L"%ls: %d", app.GetString( IDS_SLIDER_UISIZESPLITSCREEN ),pNotifyValueChanged->nValue); + m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].SetText(TempString); + + if(pNotifyValueChanged->nValue != app.GetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen)+1) + { + // slider is 1 to 3 + app.SetGameSettings(m_iPad,eGameSetting_UISizeSplitscreen,pNotifyValueChanged->nValue-1); + // Apply the changes to the selected text position + CXuiSceneBase::UpdateSelectedItemPos(m_iPad); + } + } + + return S_OK; +} + + +HRESULT CScene_SettingsUI::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + + // not in this scene - app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + // check the checkboxes + app.SetGameSettings(m_iPad,eGameSetting_DisplayHUD,m_DisplayHUD.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DisplayHand,m_DisplayHand.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DisplaySplitscreenGamertags,m_SplitScreenGamertags.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_DeathMessages,m_DeathMessages.IsChecked()?1:0); + app.SetGameSettings(m_iPad,eGameSetting_AnimatedCharacter,m_AnimatedCharacter.IsChecked()?1:0); + + // if the splitscreen vertical/horizontal has changed, need to update the scenes + if(app.GetGameSettings(m_iPad,eGameSetting_SplitScreenVertical)!=(m_SplitScreen.IsChecked()?1:0)) + { + // changed + app.SetGameSettings(m_iPad,eGameSetting_SplitScreenVertical,m_SplitScreen.IsChecked()?1:0); + + // close the xui scenes, so we don't have the navigate backed to menu at the wrong place + if(app.GetLocalPlayerCount()==2) + { + app.CloseAllPlayersXuiScenes(); + } + else + { + app.NavigateBack(pInputData->UserIndex); + } + } + else + { + app.NavigateBack(pInputData->UserIndex); + } + + + rfHandled = TRUE; + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SettingsUI::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SettingsUI::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + // added so we can skip greyed out items + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest!=NULL) + { + bHandled=TRUE; + } + + return S_OK; +} + +HRESULT CScene_SettingsUI::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + HRESULT hr; + WCHAR TempString[256]; + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO) + { + // 4J-PB - Going to resize buttons if the text is too big to fit on any of them (Br-pt problem with the length of Unlock Full Game) + + float fMaxTextLen=0.0f; + float fMaxLen=0.0f; + + // sliders first + HXUIOBJ hSlider,hVisual,hText,hCheckboxText; + XUIRect xuiRect; + float fWidth,fHeight;//,fTemp; + D3DXVECTOR3 vec,vecCheckboxText,vSlider,vecCheckbox; + + hr=XuiControlGetVisual(m_SplitScreen.m_hObj,&hVisual); + hr=XuiElementGetChildById(hVisual,L"text_Button",&hCheckboxText); + hr=XuiElementGetPosition(hCheckboxText,&vecCheckboxText); + hr=XuiElementGetPosition(m_SplitScreen.m_hObj,&vecCheckbox); + hr=XuiElementGetChildById(m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN].m_hObj,L"XuiSlider",&hSlider); + hr=XuiControlGetVisual(hSlider,&hVisual); + hr=XuiElementGetChildById(hVisual,L"text_Label",&hText); + hr=XuiElementGetPosition(m_SliderA[SLIDER_SETTINGS_UISIZE].m_hObj,&vSlider); + + + // don't display values on these - we handle that + for(int i=0;ifMaxTextLen) fMaxTextLen=xuiRect.right; + if(fWidth>fMaxLen) fMaxLen=fWidth; + } + + // now the m_SplitScreen checkbox - let's just use the visual we already have... + + hr=XuiTextPresenterMeasureText(hCheckboxText, m_SplitScreen.GetText(), &xuiRect); + m_SplitScreen.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + // now the m_SplitScreenGamertags checkbox - let's just use the visual we already have... + + hr=XuiTextPresenterMeasureText(hCheckboxText, m_SplitScreenGamertags.GetText(), &xuiRect); + m_SplitScreenGamertags.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + // now the m_DisplayHUD checkbox - let's just use the visual we already have... + + hr=XuiTextPresenterMeasureText(hCheckboxText, m_DisplayHUD.GetText(), &xuiRect); + m_DisplayHUD.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + + // now the m_DisplayHand checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hCheckboxText, m_DisplayHand.GetText(), &xuiRect); + m_DisplayHand.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + + // now the m_DeathMessages checkbox - let's just use the visual we already have... + hr=XuiTextPresenterMeasureText(hCheckboxText, m_DeathMessages.GetText(), &xuiRect); + m_DeathMessages.GetBounds(&fWidth,&fHeight); + // need to add the size of the checkbox graphic + if((xuiRect.right+vecCheckbox.x+vecCheckboxText.x)>fMaxTextLen) fMaxTextLen=xuiRect.right+vecCheckbox.x+vecCheckboxText.x; + if(fWidth>fMaxLen) fMaxLen=fWidth; + + if(fMaxLen1) + { + // scene width needs to be more that the text width on buttons + fWidth=vSlider.x; + vec.x=floorf((640.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + else + { + fWidth=vSlider.x; + vec.x=floorf((1280.0f-(fMaxTextLen+fWidth))/2.0f); + XuiElementSetPosition(m_hObj,&vec); + XuiElementSetBounds(m_hObj,fMaxTextLen+(fWidth*2.0f),fHeight); + } + + m_SplitScreen.SetBounds(fMaxTextLen,fHeight); + m_SplitScreenGamertags.SetBounds(fMaxTextLen,fHeight); + m_DisplayHUD.SetBounds(fMaxTextLen,fHeight); + m_DisplayHand.SetBounds(fMaxTextLen,fHeight); + m_DeathMessages.SetBounds(fMaxTextLen,fHeight); + + // Need to refresh the scenes visual since the object size has now changed + XuiControlAttachVisual(m_hObj); + } + } + + return S_OK; +} + +HRESULT CScene_SettingsUI::OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled) +{ + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + } + + return S_OK; +} + +HRESULT CScene_SettingsUI::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + diff --git a/Minecraft.Client/Common/XUI/XUI_SettingsUI.h b/Minecraft.Client/Common/XUI/XUI_SettingsUI.h new file mode 100644 index 0000000..12bffea --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SettingsUI.h @@ -0,0 +1,65 @@ +#pragma once + +#include "../media/xuiscene_settings_UI.h" +#include "XUI_Ctrl_SliderWrapper.h" +#include "XUI_CustomMessages.h" + + +#define SLIDER_SETTINGS_UISIZE 0 +#define SLIDER_SETTINGS_UISIZESPLITSCREEN 1 + + +#define SLIDER_SETTINGS_UI_MAX SLIDER_SETTINGS_UISIZESPLITSCREEN + 1 +class CScene_SettingsUI : public CXuiSceneImpl +{ +protected: + CXuiCtrlSliderWrapper m_SliderA[SLIDER_SETTINGS_UI_MAX]; + CXuiCheckbox m_DisplayHUD; + CXuiCheckbox m_DisplayHand; + CXuiCheckbox m_DeathMessages; + CXuiCheckbox m_AnimatedCharacter; + CXuiCheckbox m_SplitScreen; + CXuiCheckbox m_SplitScreenGamertags; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_VALUE_CHANGED( OnNotifyValueChanged ) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_NAV_RETURN(OnNavReturn) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiSplitScreen, m_SplitScreen) + MAP_CONTROL(IDC_XuiShowSplitscreenGamertags, m_SplitScreenGamertags) + MAP_CONTROL(IDC_XuiDisplayHUD, m_DisplayHUD) + MAP_CONTROL(IDC_XuiDisplayHand, m_DisplayHand) + MAP_CONTROL(IDC_XuiDisplayDeathMessages, m_DeathMessages) + MAP_CONTROL(IDC_XuiShowAnimatedCharacter, m_AnimatedCharacter) + MAP_CONTROL(IDC_XuiSliderUISize, m_SliderA[SLIDER_SETTINGS_UISIZE]) + MAP_CONTROL(IDC_XuiSliderUISizeSplitscreen, m_SliderA[SLIDER_SETTINGS_UISIZESPLITSCREEN]) + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnNotifyValueChanged( HXUIOBJ hObjSource, XUINotifyValueChanged* pNotifyValueChanged, BOOL& bHandled ); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SettingsUI, L"CScene_SettingsUI", XUI_CLASS_SCENE ) +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SignEntry.cpp b/Minecraft.Client/Common/XUI/XUI_SignEntry.cpp new file mode 100644 index 0000000..378ac14 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SignEntry.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "..\..\..\MultiplayerLevel.h" +#include "..\..\..\Minecraft.World\SignTileEntity.h" +#include "..\..\..\Minecraft.World\Entity.h" +#include "..\..\..\Minecraft.World\Level.h" +#include "..\..\..\Minecraft.Client\LocalPlayer.h" +#include "..\..\..\Minecraft.Client\ClientConnection.h" +#include "..\..\..\Minecraft.Client\MultiPlayerLocalPlayer.h" +#include "XUI_SignEntry.h" + +HRESULT CScene_SignEntry::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + MapChildControls(); + + XuiControlSetText(m_ButtonDone,app.GetString(IDS_DONE)); + XuiControlSetText(m_labelEditSign,app.GetString(IDS_EDIT_SIGN_MESSAGE)); + + SignEntryScreenInput* initData = (SignEntryScreenInput*)pInitData->pvInitData; + m_sign = initData->sign; + + CXuiSceneBase::ShowDarkOverlay( initData->iPad, TRUE ); + CXuiSceneBase::ShowLogo( initData->iPad, FALSE); + ui.SetTooltips( initData->iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,initData->iPad); + } + + + for(unsigned int i = 0; iGetMessage(i).c_str() ); + m_signRows[i].SetTextLimit(15); + // Set the title and desc for the edit keyboard popup + m_signRows[i].SetTitleAndText(IDS_SIGN_TITLE,IDS_SIGN_TITLE_TEXT); + } + + + return S_OK; +} + +HRESULT CScene_SignEntry::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_ButtonDone) + { + // Set the sign text here so we on;y call the verify once it has been set, not while we're typing in to it + for(int i=0;i<4;i++) + { + wstring temp=m_signRows[i].GetText(); + m_sign->SetMessage(i,temp); + } + + m_sign->setChanged(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + // need to send the new data + if (pMinecraft->level->isClientSide) + { + shared_ptr player = pMinecraft->localplayers[pNotifyPressData->UserIndex]; + if(player != NULL && player->connection && player->connection->isStarted()) + { + player->connection->send( shared_ptr( new SignUpdatePacket(m_sign->x, m_sign->y, m_sign->z, m_sign->IsVerified(), m_sign->IsCensored(), m_sign->GetMessages()) ) ); + } + } + app.CloseXuiScenes(pNotifyPressData->UserIndex); + } + return S_OK; +} + +HRESULT CScene_SignEntry::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + // user backed out, so wipe the sign + wstring temp=L""; + + for(int i=0;i<4;i++) + { + m_sign->SetMessage(i,temp); + } + + app.CloseXuiScenes(pInputData->UserIndex); + rfHandled = TRUE; + + CXuiSceneBase::PlayUISFX(eSFX_Back); + break; + } + + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SignEntry.h b/Minecraft.Client/Common/XUI/XUI_SignEntry.h new file mode 100644 index 0000000..4f8c44d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SignEntry.h @@ -0,0 +1,49 @@ +#pragma once +#include "../media/xuiscene_signentry.h" +#include "XUI_Ctrl_4JEdit.h" +#include "XUI_CustomMessages.h" + +#define SIGN_ENTRY_ROWS_MAX 4 + +class SignTileEntity; + +class CScene_SignEntry : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiCtrl4JEdit m_signRows[4]; + CXuiControl m_ButtonDone; + CXuiControl m_labelEditSign; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_EditSignMessage, m_labelEditSign) + MAP_CONTROL(IDC_ButtonDone, m_ButtonDone) + MAP_CONTROL(IDC_EditLineOne, m_signRows[0]) + MAP_CONTROL(IDC_EditLineTwo, m_signRows[1]) + MAP_CONTROL(IDC_EditLineThree, m_signRows[2]) + MAP_CONTROL(IDC_EditLineFour, m_signRows[3]) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SignEntry, L"CScene_SignEntry", XUI_CLASS_SCENE ) + +private: + shared_ptr m_sign; + D3DXVECTOR3 m_OriginalPosition; + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp b/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp new file mode 100644 index 0000000..ee70661 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SkinSelect.cpp @@ -0,0 +1,1444 @@ +#include "stdafx.h" +#include "XUI_SkinSelect.h" +#include "XUI_Ctrl_MinecraftSkinPreview.h" + +#define SKIN_SELECT_PACK_DEFAULT 0 +#define SKIN_SELECT_PACK_FAVORITES 1 +//#define SKIN_SELECT_PACK_PLAYER_CUSTOM 1 +#define SKIN_SELECT_MAX_DEFAULTS 2 + +WCHAR *CScene_SkinSelect::wchDefaultNamesA[]= +{ + L"USE LOCALISED VERSION", // Server selected + L"Steve", + L"Tennis Steve", + L"Tuxedo Steve", + L"Athlete Steve", + L"Scottish Steve", + L"Prisoner Steve", + L"Cyclist Steve", + L"Boxer Steve", +}; + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SkinSelect::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad=*(int *)pInitData->pvInitData; + // if we're not in the game, we need to use basescene 0 + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + m_bIgnoreInput=false; + + // 4J Stu - Added this so that we have skins loaded + // 4J-PB - Need to check for installed DLC + //if( (!app.DLCInstalled() || app.m_dlcManager.NeedsUpdated()) && !app.DLCInstallPending()) app.StartInstallDLCProcess(m_iPad); + + // StartInstallDLCProcess will check for all conditions within the call + MapChildControls(); + + m_selectedText.SetText( app.GetString( IDS_SELECTED ) ); + + updateClipping(); + + m_packIndex = SKIN_SELECT_PACK_DEFAULT; + m_skinIndex = 0; + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); + m_currentPack = NULL; + m_bSlidingSkins = false; + m_bAnimatingMove = false; + currentPackCount = 0; + + m_currentNavigation = eSkinNavigation_Skin; + m_normalTabs.SetShow( TRUE ); + m_selectedTabs.SetShow( FALSE ); + m_packLeft.SetEnable(FALSE); + m_packRight.SetEnable(FALSE); + + + for(BYTE i = 0; i < sidePreviewControls; ++i) + { + //m_previewNextControl->SetAutoRotate(true); + m_previewNextControls[i]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Left); + //m_previewPreviousControl->SetAutoRotate(true); + m_previewPreviousControls[i]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Right); + } + + // block input if we're waiting for DLC to install. The end of dlc mounting custom message will fill the save list + if(app.StartInstallDLCProcess(m_iPad)) + { + // DLC mounting in progress, so disable input + m_bIgnoreInput=true; + m_timer.SetShow( TRUE ); + m_charactersGroup.SetShow( FALSE ); + m_skinDetails.SetShow( FALSE ); + m_imagePadlock.SetShow( FALSE ); + m_selectedGroup.SetShow( FALSE ); + } + else + { + m_timer.SetShow( FALSE ); + + if(app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin)>0) + { + // Change to display the favorites if there are any. The current skin will be in there (probably) - need to check for it + m_currentPack = app.m_dlcManager.getPackContainingSkin(m_currentSkinPath); + bool bFound; + if(m_currentPack != NULL) + { + m_packIndex = app.m_dlcManager.getPackIndex(m_currentPack,bFound,DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + } + } + + // If we have any favourites, set this to the favourites + // first validate the favorite skins - we might have uninstalled the DLC needed for them + app.ValidateFavoriteSkins(m_iPad); + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + m_packIndex = SKIN_SELECT_PACK_FAVORITES; + } + + handlePackIndexChanged(); + updateCurrentFocus(); + } + + // Display the tooltips + + // if we're not in the game, we need to use basescene 0 + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + } + + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad, false); + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + else + { + if(bNotInGame) + { + CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, FALSE ); + } + else + { + CXuiSceneBase::ShowLogo( m_iPad, FALSE ); + } + } + + return S_OK; +} + + +HRESULT CScene_SkinSelect::OnKeyUp(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + switch(pInputData->dwKeyCode) + { + case VK_PAD_RTHUMB_LEFT: + m_previewControl->m_incYRot = false; + break; + case VK_PAD_RTHUMB_RIGHT: + m_previewControl->m_decYRot = false; + break; + case VK_PAD_RTHUMB_UP: + //m_previewControl->m_incXRot = false; + break; + case VK_PAD_RTHUMB_DOWN: + //m_previewControl->m_decXRot = false; + break; + case VK_PAD_RTHUMB_UPLEFT: + m_previewControl->m_incYRot = false; + //m_previewControl->m_incXRot = false; + break; + case VK_PAD_RTHUMB_UPRIGHT: + m_previewControl->m_decYRot = false; + //m_previewControl->m_incXRot = false; + break; + case VK_PAD_RTHUMB_DOWNRIGHT: + m_previewControl->m_decYRot = false; + //m_previewControl->m_decXRot = false; + break; + case VK_PAD_RTHUMB_DOWNLEFT: + m_previewControl->m_incYRot = false; + //m_previewControl->m_decXRot = false; + break; + } + return S_OK; +} + + +HRESULT CScene_SkinSelect::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // 4J Stu - We don't want the press anim to play for the scrolling unless we are actually scrolling + //ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // ignore any key press if we are animating a move + //if(m_bAnimatingMove) return S_OK; + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_A: + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + // if the profile data has been changed, then force a profile write + // It seems we're allowed to break the 5 minute rule if it's the result of a user action + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + app.SetPlayerSkin(pInputData->UserIndex, m_skinIndex); + app.SetPlayerCape(pInputData->UserIndex, 0); + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); + m_selectedGroup.SetShow( TRUE ); + CXuiSceneBase::PlayUISFX(eSFX_Press); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + // get the pack number from the skin id + wchar_t chars[256]; + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,m_skinIndex)); + + DLCPack *Pack=app.m_dlcManager.getPackContainingSkin(chars); + + if(Pack) + { + DLCSkinFile *skinFile = Pack->getSkinFile(chars); + app.SetPlayerSkin(pInputData->UserIndex, skinFile->getPath()); + app.SetPlayerCape(pInputData->UserIndex, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + m_selectedGroup.SetShow( TRUE ); + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); + app.SetPlayerFavoriteSkinsPos(m_iPad,m_skinIndex); + } + } + break; + default: + if( m_currentPack != NULL ) + { + DLCSkinFile *skinFile = m_currentPack->getSkinFile(m_skinIndex); + + // Is this a free skin? + + if(!skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free )) + { + // do we have a license? + if(!m_currentPack->hasPurchasedFile( DLCManager::e_DLCType_Skin, skinFile->getPath() )) + { + // no + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + + // We need to upsell the full version + if(ProfileManager.IsGuest(m_iPad)) + { + // can't buy + StorageManager.RequestMessageBox(IDS_PRO_GUESTPROFILE_TITLE, IDS_PRO_GUESTPROFILE_TEXT, uiIDA, 1); + } + // are we online? + else if(!ProfileManager.IsSignedInLive(pInputData->UserIndex)) + { + // need to be signed in to live + StorageManager.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1); + } + else + { + // upsell + + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(m_currentPack->getPurchaseOfferId()); + ULONGLONG ullOfferID_Full; + + if(pDLCInfo!=NULL) + { + ullOfferID_Full=pDLCInfo->ullOfferID_Full; + } + else + { + ullOfferID_Full=m_currentPack->getPurchaseOfferId(); + } + + // tell sentient about the upsell of the full version of the skin pack + TelemetryManager->RecordUpsellPresented(pInputData->UserIndex, eSet_UpsellID_Skin_DLC, ullOfferID_Full & 0xFFFFFFFF); + + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_OK; + uiIDA[1]=IDS_CONFIRM_CANCEL; + + StorageManager.RequestMessageBox(IDS_UNLOCK_DLC_TITLE, IDS_UNLOCK_DLC_SKIN, uiIDA, 2, pInputData->UserIndex,&CScene_SkinSelect::UnlockSkinReturned,this,app.GetStringTable()); + } + } + else + { + app.SetPlayerSkin(pInputData->UserIndex, skinFile->getPath()); + app.SetPlayerCape(pInputData->UserIndex, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + m_selectedGroup.SetShow( TRUE ); + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); + + // push this onto the favorite list + AddFavoriteSkin(m_iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + } + } + else + { + app.SetPlayerSkin(pInputData->UserIndex, skinFile->getPath()); + app.SetPlayerCape(pInputData->UserIndex, skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape)); + m_selectedGroup.SetShow( TRUE ); + m_currentSkinPath = app.GetPlayerSkinName(m_iPad); + m_originalSkinId = app.GetPlayerSkinId(m_iPad); + + // push this onto the favorite list + AddFavoriteSkin(m_iPad,GET_DLC_SKIN_ID_FROM_BITMASK(m_originalSkinId)); + + } + } + + CXuiSceneBase::PlayUISFX(eSFX_Press); + break; + } + + break; + case VK_PAD_B: + case VK_ESCAPE: + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + app.CheckGameSettingsChanged(true,pInputData->UserIndex); + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; +#if 0 + case VK_PAD_RSHOULDER: + { + DWORD startingIndex = m_packIndex; + m_packIndex = getNextPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + break; + case VK_PAD_LSHOULDER: + { + DWORD startingIndex = m_packIndex; + m_packIndex = getPreviousPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + break; +#endif + case VK_PAD_DPAD_UP: + case VK_PAD_LTHUMB_UP: + case VK_PAD_DPAD_DOWN: + case VK_PAD_LTHUMB_DOWN: + { + if(m_packIndex==SKIN_SELECT_PACK_FAVORITES) + { + if(app.GetPlayerFavoriteSkinsCount(m_iPad)==0) + { + // ignore this, since there are no skins being displayed + break; + } + } + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + switch(m_currentNavigation) + { + case eSkinNavigation_Pack: + m_currentNavigation = eSkinNavigation_Skin; + break; + case eSkinNavigation_Skin: + m_currentNavigation = eSkinNavigation_Pack; + break; + }; + updateCurrentFocus(); + } + break; + case VK_PAD_DPAD_LEFT: + case VK_PAD_LTHUMB_LEFT: + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + if(!m_bAnimatingMove) + { + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + + m_skinIndex = getPreviousSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_previewControl->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Left, true); + m_previewPreviousControls[0]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Forward, true); + + int startFrame, endFrame; + HRESULT hr = m_charactersGroup.FindNamedFrame(L"CycleRight", &startFrame); + hr = m_charactersGroup.FindNamedFrame( L"EndCycleRight", &endFrame); + hr = m_charactersGroup.PlayTimeline(startFrame, startFrame,endFrame,FALSE,FALSE); + } + } + else if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getPreviousPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + } + break; + case VK_PAD_DPAD_RIGHT: + case VK_PAD_LTHUMB_RIGHT: + { + if( m_currentNavigation == eSkinNavigation_Skin ) + { + if(!m_bAnimatingMove) + { + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + m_skinIndex = getNextSkinIndex(m_skinIndex); + //handleSkinIndexChanged(); + + m_bSlidingSkins = true; + m_bAnimatingMove = true; + + m_previewControl->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Right, true); + m_previewNextControls[0]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Forward, true); + + int startFrame, endFrame; + HRESULT hr = m_charactersGroup.FindNamedFrame(L"CycleLeft", &startFrame); + hr = m_charactersGroup.FindNamedFrame( L"EndCycleLeft", &endFrame); + hr = m_charactersGroup.PlayTimeline(startFrame, startFrame,endFrame,FALSE,FALSE); + } + } + else if( m_currentNavigation == eSkinNavigation_Pack ) + { + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + DWORD startingIndex = m_packIndex; + m_packIndex = getNextPackIndex(m_packIndex); + if(startingIndex != m_packIndex) + { + handlePackIndexChanged(); + } + } + } + break; + case VK_PAD_RTHUMB_PRESS: + CXuiSceneBase::PlayUISFX(eSFX_Press); + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->ResetRotation(); + } + break; + case VK_PAD_RTHUMB_LEFT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_incYRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_RIGHT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_decYRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_UP: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + //m_previewControl->m_incXRot = true; + m_previewControl->CyclePreviousAnimation(); + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_DOWN: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + //m_previewControl->m_decXRot = true; + m_previewControl->CycleNextAnimation(); + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_UPLEFT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_incYRot = true; + //m_previewControl->m_incXRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_UPRIGHT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_decYRot = true; + //m_previewControl->m_incXRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_DOWNRIGHT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_decYRot = true; + //m_previewControl->m_decXRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + case VK_PAD_RTHUMB_DOWNLEFT: + if( m_currentNavigation == eSkinNavigation_Skin ) + { + m_previewControl->m_incYRot = true; + //m_previewControl->m_decXRot = true; + } + else + { + CXuiSceneBase::PlayUISFX(eSFX_Scroll); + } + break; + } + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SkinSelect::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + return S_OK; +} + +HRESULT CScene_SkinSelect::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) + { + + } + + return S_OK; +} + +HRESULT CScene_SkinSelect::OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled) +{ + if( hObjSource == m_charactersGroup ) + { + if(m_bSlidingSkins) + { + m_bSlidingSkins = false; + + int startFrame, endFrame; + HRESULT hr = m_charactersGroup.FindNamedFrame(L"Normal", &startFrame); + hr = m_charactersGroup.FindNamedFrame( L"Normal", &endFrame); + hr = m_charactersGroup.PlayTimeline(startFrame, startFrame,endFrame,FALSE,FALSE); + } + else + { + m_previewControl->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Forward, false); + m_previewNextControls[0]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Left, false); + m_previewPreviousControls[0]->SetFacing(CXuiCtrlMinecraftSkinPreview::e_SkinPreviewFacing_Right, false); + + handleSkinIndexChanged(); + + m_bAnimatingMove = false; + + bHandled = TRUE; + } + } + return S_OK; +} + + +HRESULT CScene_SkinSelect::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining,false); +} + + + + +HRESULT CScene_SkinSelect::OnBasePositionChanged() +{ + updateClipping(); + + return S_OK; +} + +void CScene_SkinSelect::handleSkinIndexChanged() +{ + BOOL showPrevious = FALSE, showNext = FALSE; + DWORD previousIndex = 0, nextIndex = 0; + wstring skinName = L""; + wstring skinOrigin = L""; + bool bSkinIsFree=false; + bool bLicensed=false; + DLCSkinFile *skinFile=NULL; + DLCPack *Pack=NULL; + BYTE sidePreviewControlsL,sidePreviewControlsR; + bool bNoSkinsToShow=false; + + TEXTURE_NAME backupTexture = TN_MOB_CHAR; + m_selectedGroup.SetShow( FALSE ); + m_skinDetails.SetShow( FALSE ); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(m_skinIndex); + m_selectedSkinPath = skinFile->getPath(); + m_selectedCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + m_vAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + + skinName = skinFile->getParameterAsString( DLCManager::e_DLCParamType_DisplayName ); + skinOrigin = skinFile->getParameterAsString( DLCManager::e_DLCParamType_ThemeName ); + + if( m_selectedSkinPath.compare( m_currentSkinPath ) == 0 ) + { + m_selectedGroup.SetShow( TRUE ); + } + else + { + m_selectedGroup.SetShow( FALSE ); + } + + bSkinIsFree = skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ); + bLicensed = m_currentPack->hasPurchasedFile( DLCManager::e_DLCType_Skin, m_selectedSkinPath ); + + m_imagePadlock.SetShow( (bSkinIsFree || bLicensed) ?FALSE:TRUE ); + m_previewControl->SetShow(TRUE); + m_skinDetails.SetShow( TRUE ); + } + else + { + m_selectedSkinPath = L""; + m_selectedCapePath = L""; + m_vAdditionalSkinBoxes = NULL; + + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(m_skinIndex); + + if( m_skinIndex == eDefaultSkins_ServerSelected ) + { + skinName = app.GetString(IDS_DEFAULT_SKINS); + } + else + { + skinName = wchDefaultNamesA[m_skinIndex]; + } + + if( m_originalSkinId == m_skinIndex ) + { + m_selectedGroup.SetShow( TRUE ); + } + else + { + m_selectedGroup.SetShow( FALSE ); + } + m_imagePadlock.SetShow( FALSE ); + m_previewControl->SetShow(TRUE); + m_skinDetails.SetShow( TRUE ); + + break; + case SKIN_SELECT_PACK_FAVORITES: + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + // get the pack number from the skin id + wchar_t chars[256]; + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,m_skinIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + m_selectedSkinPath = skinFile->getPath(); + m_selectedCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + m_vAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + + skinName = skinFile->getParameterAsString( DLCManager::e_DLCParamType_DisplayName ); + skinOrigin = skinFile->getParameterAsString( DLCManager::e_DLCParamType_ThemeName ); + + if( m_selectedSkinPath.compare( m_currentSkinPath ) == 0 ) + { + m_selectedGroup.SetShow( TRUE ); + } + else + { + m_selectedGroup.SetShow( FALSE ); + } + + bSkinIsFree = skinFile->getParameterAsBool( DLCManager::e_DLCParamType_Free ); + bLicensed = Pack->hasPurchasedFile( DLCManager::e_DLCType_Skin, m_selectedSkinPath ); + + m_imagePadlock.SetShow( (bSkinIsFree || bLicensed) ?FALSE:TRUE ); + m_skinDetails.SetShow( TRUE ); + } + else + { + m_selectedGroup.SetShow( FALSE ); + m_imagePadlock.SetShow( FALSE ); + } + } + else + { + //disable the display + m_previewControl->SetShow(FALSE); + // change the tooltips + bNoSkinsToShow=true; + } + break; + + } + } + m_text.SetText(skinName.c_str()); + m_originText.SetText(skinOrigin.c_str()); + + if(m_vAdditionalSkinBoxes && m_vAdditionalSkinBoxes->size()!=0) + { + // add the boxes to the humanoid model, but only if we've not done this already + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),m_vAdditionalSkinBoxes); + } + } + + if(skinFile!=NULL) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + + m_previewControl->SetTexture(m_selectedSkinPath, backupTexture); + m_previewControl->SetCapeTexture(m_selectedCapePath); + + showNext = TRUE; + showPrevious = TRUE; + nextIndex = getNextSkinIndex(m_skinIndex); + previousIndex = getPreviousSkinIndex(m_skinIndex); + + wstring otherSkinPath = L""; + wstring otherCapePath = L""; + vector *othervAdditionalSkinBoxes=NULL; + wchar_t chars[256]; + + // turn off all displays + for(BYTE i = 0; i < sidePreviewControls; ++i) + { + m_previewNextControls[i]->SetShow(FALSE); + m_previewPreviousControls[i]->SetShow(FALSE); + } + + unsigned int uiCurrentFavoriteC=app.GetPlayerFavoriteSkinsCount(m_iPad); + + if(m_packIndex==SKIN_SELECT_PACK_FAVORITES) + { + // might not be enough to cycle through + if(uiCurrentFavoriteC<((sidePreviewControls*2)+1)) + { + if(uiCurrentFavoriteC==0) + { + sidePreviewControlsL=sidePreviewControlsR=0; + } + // might be an odd number + else if((uiCurrentFavoriteC-1)%2==1) + { + sidePreviewControlsL=1+(uiCurrentFavoriteC-1)/2; + sidePreviewControlsR=(uiCurrentFavoriteC-1)/2; + } + else + { + sidePreviewControlsL=sidePreviewControlsR=(uiCurrentFavoriteC-1)/2; + } + } + else + { + sidePreviewControlsL=sidePreviewControlsR=sidePreviewControls; + } + } + else + { + sidePreviewControlsL=sidePreviewControlsR=sidePreviewControls; + } + + for(BYTE i = 0; i < sidePreviewControlsR; ++i) + { + if(showNext) + { + skinFile=NULL; + m_previewNextControls[i]->SetShow(TRUE); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(nextIndex); + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + else + { + otherSkinPath = L""; + otherCapePath = L""; + othervAdditionalSkinBoxes=NULL; + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(nextIndex); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(uiCurrentFavoriteC>0) + { + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,nextIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + } + break; + default: + break; + } + + } + if(othervAdditionalSkinBoxes && othervAdditionalSkinBoxes->size()!=0) + { + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),othervAdditionalSkinBoxes); + } + } + // 4J-PB - anim override needs set before SetTexture + if(skinFile!=NULL) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + m_previewNextControls[i]->SetTexture(otherSkinPath, backupTexture); + m_previewNextControls[i]->SetCapeTexture(otherCapePath); + + + } + + nextIndex = getNextSkinIndex(nextIndex); + } + + + + for(BYTE i = 0; i < sidePreviewControlsL; ++i) + { + if(showPrevious) + { + skinFile=NULL; + m_previewPreviousControls[i]->SetShow(TRUE); + + if( m_currentPack != NULL ) + { + skinFile = m_currentPack->getSkinFile(previousIndex); + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + else + { + otherSkinPath = L""; + otherCapePath = L""; + othervAdditionalSkinBoxes=NULL; + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + backupTexture = getTextureId(previousIndex); + break; + case SKIN_SELECT_PACK_FAVORITES: + if(uiCurrentFavoriteC>0) + { + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,previousIndex)); + + Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + skinFile = Pack->getSkinFile(chars); + + otherSkinPath = skinFile->getPath(); + otherCapePath = skinFile->getParameterAsString(DLCManager::e_DLCParamType_Cape); + othervAdditionalSkinBoxes = skinFile->getAdditionalBoxes(); + backupTexture = TN_MOB_CHAR; + } + } + + break; + default: + break; + } + } + if(othervAdditionalSkinBoxes && othervAdditionalSkinBoxes->size()!=0) + { + vector *pAdditionalModelParts = app.GetAdditionalModelParts(skinFile->getSkinID()); + if(pAdditionalModelParts==NULL) + { + pAdditionalModelParts = app.SetAdditionalSkinBoxes(skinFile->getSkinID(),othervAdditionalSkinBoxes); + } + } + // 4J-PB - anim override needs set before SetTexture + if(skinFile) + { + app.SetAnimOverrideBitmask(skinFile->getSkinID(),skinFile->getAnimOverrideBitmask()); + } + m_previewPreviousControls[i]->SetTexture(otherSkinPath, backupTexture); + m_previewPreviousControls[i]->SetCapeTexture(otherCapePath); + } + + previousIndex = getPreviousSkinIndex(previousIndex); + } + + // update the tooltips + bool bNotInGame=(Minecraft::GetInstance()->level==NULL); + + if(bNoSkinsToShow) + { + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + } + + } + else + { + if(bNotInGame) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( DEFAULT_XUI_MENU_USER, TRUE ); + } + else + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT_SKIN,IDS_TOOLTIPS_CANCEL,-1,-1,-1,-1,-1,-1,IDS_TOOLTIPS_NAVIGATE); + CXuiSceneBase::ShowBackground( m_iPad, FALSE ); + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + } + + } + + updateCurrentFocus(); +} + +void CScene_SkinSelect::handlePackIndexChanged() +{ + if(m_packIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + m_currentPack = app.m_dlcManager.getPack(m_packIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + } + else + { + m_currentPack = NULL; + } + m_skinIndex = 0; + if(m_currentPack != NULL) + { + bool found; + DWORD currentSkinIndex = m_currentPack->getSkinIndexAt(m_currentSkinPath, found); + if(found) m_skinIndex = currentSkinIndex; + } + else + { + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + if( !GET_IS_DLC_SKIN_FROM_BITMASK(m_originalSkinId) ) + { + DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(m_originalSkinId); + DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(m_originalSkinId); + if( ugcSkinIndex == 0 ) + { + m_skinIndex = (EDefaultSkins) defaultSkinIndex; + } + } + break; + case SKIN_SELECT_PACK_FAVORITES: + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + bool found; + wchar_t chars[256]; + // get the pack number from the skin id + swprintf(chars, 256, L"dlcskin%08d.png", app.GetPlayerFavoriteSkin(m_iPad,app.GetPlayerFavoriteSkinsPos(m_iPad))); + + DLCPack *Pack=app.m_dlcManager.getPackContainingSkin(chars); + if(Pack) + { + DWORD currentSkinIndex = Pack->getSkinIndexAt(m_currentSkinPath, found); + if(found) m_skinIndex = app.GetPlayerFavoriteSkinsPos(m_iPad); + } + } + break; + default: + break; + } + } + handleSkinIndexChanged(); + updatePackDisplay(); +} + +void CScene_SkinSelect::updatePackDisplay() +{ + currentPackCount = app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + + m_packLeft.SetShow(TRUE); + m_packRight.SetShow(TRUE); + + if(m_packIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(m_packIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + m_packCenter.SetText(thisPack->getName().c_str()); + } + else + { + switch(m_packIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + m_packCenter.SetText(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + m_packCenter.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + + int nextPackIndex = getNextPackIndex(m_packIndex); + if(nextPackIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(nextPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + m_packRight.SetText(thisPack->getName().c_str()); + } + else + { + switch(nextPackIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + m_packRight.SetText(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + m_packRight.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + + int previousPackIndex = getPreviousPackIndex(m_packIndex); + if(previousPackIndex >= SKIN_SELECT_MAX_DEFAULTS) + { + DLCPack *thisPack = app.m_dlcManager.getPack(previousPackIndex - SKIN_SELECT_MAX_DEFAULTS, DLCManager::e_DLCType_Skin); + m_packLeft.SetText(thisPack->getName().c_str()); + } + else + { + switch(previousPackIndex) + { + case SKIN_SELECT_PACK_DEFAULT: + m_packLeft.SetText(app.GetString(IDS_NO_SKIN_PACK)); + break; + case SKIN_SELECT_PACK_FAVORITES: + m_packLeft.SetText(app.GetString(IDS_FAVORITES_SKIN_PACK)); + break; + } + } + +} + +void CScene_SkinSelect::updateCurrentFocus() +{ + XUITimeline *timeline; + XUINamedFrame *startFrame; + XuiElementGetTimeline( m_skinDetails.m_hObj, &timeline); + switch(m_currentNavigation) + { + case eSkinNavigation_Pack: + XuiElementSetUserFocus( m_packCenter.m_hObj, m_iPad ); + startFrame = timeline->FindNamedFrame( L"Unselected" ); + m_normalTabs.SetShow( FALSE ); + m_selectedTabs.SetShow( TRUE ); + m_packLeft.SetEnable(TRUE); + m_packRight.SetEnable(TRUE); + break; + case eSkinNavigation_Skin: + XuiElementSetUserFocus( m_skinDetails.m_hObj, m_iPad ); + startFrame = timeline->FindNamedFrame( L"Selected" ); + m_normalTabs.SetShow( TRUE ); + m_selectedTabs.SetShow( FALSE ); + m_packLeft.SetEnable(FALSE); + m_packRight.SetEnable(FALSE); + break; + }; + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, startFrame->m_dwFrame, FALSE, FALSE ); +} + +TEXTURE_NAME CScene_SkinSelect::getTextureId(int skinIndex) +{ + TEXTURE_NAME texture = TN_MOB_CHAR; + switch(skinIndex) + { + case eDefaultSkins_ServerSelected: + case eDefaultSkins_Skin0: + texture = TN_MOB_CHAR; + break; + case eDefaultSkins_Skin1: + texture = TN_MOB_CHAR1; + break; + case eDefaultSkins_Skin2: + texture = TN_MOB_CHAR2; + break; + case eDefaultSkins_Skin3: + texture = TN_MOB_CHAR3; + break; + case eDefaultSkins_Skin4: + texture = TN_MOB_CHAR4; + break; + case eDefaultSkins_Skin5: + texture = TN_MOB_CHAR5; + break; + case eDefaultSkins_Skin6: + texture = TN_MOB_CHAR6; + break; + case eDefaultSkins_Skin7: + texture = TN_MOB_CHAR7; + break; + }; + + return texture; +} + +int CScene_SkinSelect::getNextSkinIndex(DWORD sourceIndex) +{ + int nextSkin = sourceIndex; + + // special case for favourites + switch(m_packIndex) + { + + case SKIN_SELECT_PACK_FAVORITES: + ++nextSkin; + if(nextSkin>=app.GetPlayerFavoriteSkinsCount(m_iPad)) + { + nextSkin=0; + } + + break; + default: + ++nextSkin; + + if(m_packIndex == SKIN_SELECT_PACK_DEFAULT && nextSkin >= eDefaultSkins_Count) + { + nextSkin = eDefaultSkins_ServerSelected; + } + else if(m_currentPack != NULL && nextSkin>=m_currentPack->getSkinCount()) + { + nextSkin = 0; + } + break; + } + + + return nextSkin; +} + +int CScene_SkinSelect::getPreviousSkinIndex(DWORD sourceIndex) +{ + int previousSkin = sourceIndex; + switch(m_packIndex) + { + + case SKIN_SELECT_PACK_FAVORITES: + if(previousSkin==0) + { + previousSkin = app.GetPlayerFavoriteSkinsCount(m_iPad) - 1; + } + else + { + --previousSkin; + } + break; + default: + if(previousSkin==0) + { + if(m_packIndex == SKIN_SELECT_PACK_DEFAULT) + { + previousSkin = eDefaultSkins_Count - 1; + } + else if(m_currentPack != NULL) + { + previousSkin = m_currentPack->getSkinCount()-1; + } + } + else + { + --previousSkin; + } + break; + } + + + return previousSkin; +} + +int CScene_SkinSelect::getNextPackIndex(DWORD sourceIndex) +{ + int nextPack = sourceIndex; + ++nextPack; + if(nextPack > app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) - 1 + SKIN_SELECT_MAX_DEFAULTS) + { + nextPack = SKIN_SELECT_PACK_DEFAULT; + } + + return nextPack; +} + +int CScene_SkinSelect::getPreviousPackIndex(DWORD sourceIndex) +{ + int previousPack = sourceIndex; + if(previousPack == SKIN_SELECT_PACK_DEFAULT) + { + if(app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) > 0) + { + previousPack = app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin) - 1 + SKIN_SELECT_MAX_DEFAULTS; + } + else + { + previousPack = SKIN_SELECT_MAX_DEFAULTS - 1; + } + } + else + { + --previousPack; + } + + return previousPack; +} + +void CScene_SkinSelect::updateClipping() +{ + DWORD dwPropertyId; + XUIElementPropVal propertyVal; + propertyVal.Clear(); + HRESULT hr = XuiObjectGetPropertyId( m_charactersGroup.m_hObj, L"ClipChildren", &dwPropertyId); + switch( CXuiSceneBase::GetPlayerBasePosition(m_iPad) ) + { + case CXuiSceneBase::e_BaseScene_Left: + case CXuiSceneBase::e_BaseScene_Right: + case CXuiSceneBase::e_BaseScene_Top_Left: + case CXuiSceneBase::e_BaseScene_Top_Right: + case CXuiSceneBase::e_BaseScene_Bottom_Left: + case CXuiSceneBase::e_BaseScene_Bottom_Right: + case CXuiSceneBase::e_BaseScene_Top: + case CXuiSceneBase::e_BaseScene_Bottom: + propertyVal.SetBoolVal(TRUE); + break; + case CXuiSceneBase::e_BaseScene_Fullscreen: + default: + propertyVal.SetBoolVal(FALSE); + break; + }; + hr = XuiObjectSetProperty(m_charactersGroup.m_hObj,dwPropertyId,0,&propertyVal); +} + +int CScene_SkinSelect::UnlockSkinReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CScene_SkinSelect* pScene = (CScene_SkinSelect*)pParam; +#ifdef _XBOX + if(result==C4JStorage::EMessage_ResultAccept) + { + if(ProfileManager.IsSignedIn(iPad)) + { + ULONGLONG ullIndexA[1]; + DLC_INFO *pDLCInfo = app.GetDLCInfoForTrialOfferID(pScene->m_currentPack->getPurchaseOfferId()); + + if(pDLCInfo!=NULL) + { + ullIndexA[0]=pDLCInfo->ullOfferID_Full; + } + else + { + ullIndexA[0]=pScene->m_currentPack->getPurchaseOfferId(); + } + + // If we're in-game, then we need to enable DLC downloads. They'll be set back to Auto on leaving the pause menu + if(Minecraft::GetInstance()->level!=NULL) + { + // need to allow downloads here, or the player would need to quit the game to let the download of a skin pack happen. This might affect the network traffic, since the download could take all the bandwidth... + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + } + + StorageManager.InstallOffer(1,ullIndexA,NULL,NULL); + + // the license change coming in when the offer has been installed will cause this scene to refresh + } + } + else + { + TelemetryManager->RecordUpsellResponded(iPad, eSet_UpsellID_Skin_DLC, ( pScene->m_currentPack->getPurchaseOfferId() & 0xFFFFFFFF ), eSen_UpsellOutcome_Declined); + } +#endif + return 0; +} + +HRESULT CScene_SkinSelect::OnCustomMessage_DLCInstalled() +{ + // mounted DLC may have changed + if(app.StartInstallDLCProcess(m_iPad)==false) + { + // not doing a mount, so re-enable input + m_bIgnoreInput=false; + } + else + { + m_bIgnoreInput=true; + m_timer.SetShow( TRUE ); + m_charactersGroup.SetShow( FALSE ); + m_skinDetails.SetShow( FALSE ); + } + + // this will send a CustomMessage_DLCMountingComplete when done + return S_OK; +} + +HRESULT CScene_SkinSelect::OnCustomMessage_DLCMountingComplete() +{ + + m_timer.SetShow( FALSE ); + m_charactersGroup.SetShow( TRUE ); + m_skinDetails.SetShow( TRUE ); + m_packIndex = SKIN_SELECT_PACK_DEFAULT; + + if(app.m_dlcManager.getPackCount(DLCManager::e_DLCType_Skin)>0) + { + m_currentPack = app.m_dlcManager.getPackContainingSkin(m_currentSkinPath); + if(m_currentPack != NULL) + { + bool bFound = false; + m_packIndex = app.m_dlcManager.getPackIndex(m_currentPack,bFound,DLCManager::e_DLCType_Skin) + SKIN_SELECT_MAX_DEFAULTS; + } + } + + // If we have any favourites, set this to the favourites + // first validate the favorite skins - we might have uninstalled the DLC needed for them + app.ValidateFavoriteSkins(m_iPad); + + if(app.GetPlayerFavoriteSkinsCount(m_iPad)>0) + { + m_packIndex = SKIN_SELECT_PACK_FAVORITES; + } + + handlePackIndexChanged(); + updateCurrentFocus(); + m_bIgnoreInput=false; + app.m_dlcManager.checkForCorruptDLCAndAlert(); + bool bInGame=(Minecraft::GetInstance()->level!=NULL); + + if(bInGame) XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + + return S_OK; +} + +void CScene_SkinSelect::AddFavoriteSkin(int iPad,int iSkinID) +{ + // Is this favorite skin already in the array? + unsigned int uiCurrentFavoriteSkinsCount=app.GetPlayerFavoriteSkinsCount(m_iPad); + + for(int i=0;i0) + { + ucPos++; + } + else + { + ucPos=0; + } + } + + app.SetPlayerFavoriteSkin(iPad,(int)ucPos,iSkinID); + app.SetPlayerFavoriteSkinsPos(m_iPad,ucPos); + +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SkinSelect.h b/Minecraft.Client/Common/XUI/XUI_SkinSelect.h new file mode 100644 index 0000000..dbbb388 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SkinSelect.h @@ -0,0 +1,150 @@ +#pragma once + +#include "../media/xuiscene_skinselect.h" +#include "XUI_CustomMessages.h" +#include "..\..\..\Minecraft.World\Definitions.h" +#include "..\..\Textures.h" + +class DLCPack; + +class CXuiCtrlMinecraftSkinPreview; + +class CScene_SkinSelect : public CXuiSceneImpl +{ +private: + static WCHAR *wchDefaultNamesA[eDefaultSkins_Count]; + + // 4J Stu - How many to show on each side of the main control + static const BYTE sidePreviewControls = 4; + + enum ESkinSelectNavigation + { + eSkinNavigation_Pack, + eSkinNavigation_Skin, + + eSkinNavigation_Count, + }; + +protected: + CXuiControl m_skinDetails, m_text, m_originText; + CXuiCtrlMinecraftSkinPreview *m_previewControl; + CXuiCtrlMinecraftSkinPreview *m_previewPreviousControls[sidePreviewControls]; + CXuiCtrlMinecraftSkinPreview *m_previewNextControls[sidePreviewControls]; + CXuiControl m_packGroup, m_charactersGroup; + CXuiControl m_packLeft, m_packRight, m_packCenter; + CXuiImageElement m_imagePadlock; + CXuiElement m_selectedGroup; + CXuiControl m_selectedText; + CXuiControl m_timer; + CXuiElement m_tabGroup; + CXuiElement m_normalTabs, m_selectedTabs; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN( OnKeyDown ) + XUI_ON_XM_KEYUP( OnKeyUp ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_TIMELINE_END( OnTimelineEnd ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_ON_XM_DLCLOADED_MESSAGE(OnCustomMessage_DLCMountingComplete) + XUI_ON_XM_BASE_POSITION_CHANGED_MESSAGE(OnBasePositionChanged) + XUI_ON_XM_DLCINSTALLED_MESSAGE(OnCustomMessage_DLCInstalled) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Timer, m_timer) + + MAP_CONTROL(IDC_PackGroup, m_packGroup) + BEGIN_MAP_CHILD_CONTROLS( m_packGroup) + MAP_CONTROL(IDC_Left, m_packLeft) + MAP_CONTROL(IDC_Center, m_packCenter) + MAP_CONTROL(IDC_Right, m_packRight) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_TabBar, m_tabGroup) + BEGIN_MAP_CHILD_CONTROLS( m_tabGroup ) + MAP_CONTROL(IDC_Selected, m_selectedTabs ) + MAP_CONTROL(IDC_Normal, m_normalTabs ) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_SelectedGroup, m_selectedGroup) + BEGIN_MAP_CHILD_CONTROLS( m_selectedGroup ) + MAP_CONTROL(IDC_SelectedText, m_selectedText) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_Locked, m_imagePadlock) + + MAP_CONTROL(IDC_SkinDetails, m_skinDetails) + BEGIN_MAP_CHILD_CONTROLS( m_skinDetails) + MAP_CONTROL(IDC_SkinName, m_text) + MAP_CONTROL(IDC_OriginName, m_originText) + END_MAP_CHILD_CONTROLS() + + MAP_CONTROL(IDC_Characters, m_charactersGroup) + BEGIN_MAP_CHILD_CONTROLS( m_charactersGroup ) + MAP_OVERRIDE(IDC_Character, m_previewControl) + MAP_OVERRIDE(IDC_CharacterPrevious1, m_previewPreviousControls[0]) + MAP_OVERRIDE(IDC_CharacterPrevious2, m_previewPreviousControls[1]) + MAP_OVERRIDE(IDC_CharacterPrevious3, m_previewPreviousControls[2]) + MAP_OVERRIDE(IDC_CharacterPrevious4, m_previewPreviousControls[3]) + MAP_OVERRIDE(IDC_CharacterNext1, m_previewNextControls[0]) + MAP_OVERRIDE(IDC_CharacterNext2, m_previewNextControls[1]) + MAP_OVERRIDE(IDC_CharacterNext3, m_previewNextControls[2]) + MAP_OVERRIDE(IDC_CharacterNext4, m_previewNextControls[3]) + END_MAP_CHILD_CONTROLS() + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyUp(XUIMessageInput *pInputData, BOOL& bHandled); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + HRESULT OnCustomMessage_DLCInstalled(); + HRESULT OnCustomMessage_DLCMountingComplete(); + HRESULT OnBasePositionChanged(); + + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + wstring m_currentSkinPath, m_selectedSkinPath, m_selectedCapePath; + vector *m_vAdditionalSkinBoxes; + //vector *m_vAdditionalModelParts; + DWORD m_originalSkinId; + + DLCPack *m_currentPack; + DWORD m_packIndex, m_skinIndex; +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SkinSelect, L"CScene_SkinSelect", XUI_CLASS_SCENE ) + +private: + void handleSkinIndexChanged(); + void handlePackIndexChanged(); + void updatePackDisplay(); + void updateCurrentFocus(); + TEXTURE_NAME getTextureId(int skinIndex); + + int getNextSkinIndex(DWORD sourceIndex); + int getPreviousSkinIndex(DWORD sourceIndex); + + int getNextPackIndex(DWORD sourceIndex); + int getPreviousPackIndex(DWORD sourceIndex); + + void updateClipping(); + + static int UnlockSkinReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + void AddFavoriteSkin(int iPad,int iSkinID); + + bool m_bSlidingSkins, m_bAnimatingMove; + + DWORD currentPackCount; + + ESkinSelectNavigation m_currentNavigation; + bool m_bIgnoreInput; +}; diff --git a/Minecraft.Client/Common/XUI/XUI_SocialPost.cpp b/Minecraft.Client/Common/XUI/XUI_SocialPost.cpp new file mode 100644 index 0000000..f237e7d --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SocialPost.cpp @@ -0,0 +1,147 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\XUI\XUI_SocialPost.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\SharedConstants.h" +#include "..\..\..\Minecraft.World\Random.h" +#include "..\..\..\Minecraft.Client\SurvivalMode.h" +#include "..\..\..\Minecraft.Client\CreateWorldScreen.h" +#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h" +#include "..\..\..\Minecraft.World\AABB.h" +#include "..\..\..\Minecraft.World\Vec3.h" +#include "..\..\LocalPlayer.h" + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_SocialPost::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad = *(int *)pInitData->pvInitData; + + MapChildControls(); + + XuiControlSetText(m_text,app.GetString(IDS_SOCIAL_TEXT)); + XuiControlSetText(m_LabelCaption,app.GetString(IDS_SOCIAL_LABEL_CAPTION)); + XuiControlSetText(m_EditCaption,app.GetString(IDS_SOCIAL_DEFAULT_CAPTION)); + XuiControlSetText(m_LabelDescription,app.GetString(IDS_SOCIAL_LABEL_DESCRIPTION)); + XuiControlSetText(m_EditDesc,app.GetString(IDS_SOCIAL_DEFAULT_DESCRIPTION)); + XuiControlSetText(m_OK,app.GetString(IDS_CONFIRM_OK)); + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + + m_EditCaption.SetTextLimit(MAX_SOCIALPOST_CAPTION); + m_EditDesc.SetTextLimit(MAX_SOCIALPOST_DESC); + + // Hardcoded so posts will have this in + m_wTitle = L"Minecraft: Xbox 360 Edition"; + + m_EditCaption.SetTitleAndText(IDS_NAME_CAPTION,IDS_NAME_CAPTION_TEXT); + m_EditDesc.SetTitleAndText(IDS_NAME_DESC,IDS_NAME_DESC_TEXT); + + wstring wCaption = m_EditCaption.GetText(); + wstring wDesc = m_EditDesc.GetText(); + + // set the caret to the end of the default text + m_EditCaption.SetCaretPosition((int)wCaption.length()); + m_EditDesc.SetCaretPosition((int)wDesc.length()); + + BOOL bHasAllText = /*( wTitle.length()!=0) && */(wCaption.length()!=0) && (wDesc.length()!=0); + + m_OK.SetEnable(bHasAllText); + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad,false); + } + + TelemetryManager->RecordMenuShown(m_iPad, eUIScene_SocialPost, 0); + + return S_OK; +} + + +HRESULT CScene_SocialPost::OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled) +{ wstring wCaption = m_EditCaption.GetText(); +wstring wDesc = m_EditDesc.GetText(); + + + if(/*(hObjSource == m_EditTitle) || */(hObjSource == m_EditCaption) || (hObjSource == m_EditDesc)) + { + // Enable the done button when we have all of the necessary information + //wstring wTitle = m_EditTitle.GetText(); + wstring wCaption = m_EditCaption.GetText(); + wstring wDesc = m_EditDesc.GetText(); + + BOOL bHasAllText = /*( wTitle.length()!=0) &&*/ (wCaption.length()!=0) && (wDesc.length()!=0); + m_OK.SetEnable(bHasAllText); + } + + return S_OK; +} + +HRESULT CScene_SocialPost::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + pControlNavigateData->hObjDest=XuiControlGetNavigation(pControlNavigateData->hObjSource,pControlNavigateData->nControlNavigate,TRUE,TRUE); + + if(pControlNavigateData->hObjDest==NULL) + { + pControlNavigateData->hObjDest=pControlNavigateData->hObjSource; + } + + bHandled=TRUE; + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_SocialPost::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_OK) + { + CSocialManager::Instance()->SetSocialPostText(m_wTitle.c_str(),m_EditCaption.GetText(),m_EditDesc.GetText()); + + CSocialManager::Instance()->PostImageToSocialNetwork( eFacebook, pNotifyPressData->UserIndex, false); + + app.NavigateBack(pNotifyPressData->UserIndex); + } + + return S_OK; +} + + +HRESULT CScene_SocialPost::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + //HXUIOBJ hFocus=XuiElementGetFocus(); + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + + break; + } + + + return S_OK; +} + + +HRESULT CScene_SocialPost::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining,false); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_SocialPost.h b/Minecraft.Client/Common/XUI/XUI_SocialPost.h new file mode 100644 index 0000000..0dd37fa --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_SocialPost.h @@ -0,0 +1,59 @@ +#pragma once + +#include "../media\xuiscene_socialpost.h" +#include "XUI_Ctrl_4JEdit.h" +#include "XUI_CustomMessages.h" + + +class CScene_SocialPost : public CXuiSceneImpl +{ + protected: + // Control and Element wrapper objects. + CXuiControl m_OK; + CXuiCtrl4JEdit m_EditCaption; + CXuiCtrl4JEdit m_EditDesc; + CXuiControl m_text, m_LabelCaption, m_LabelDescription; + wstring m_wTitle; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_ON_XM_CONTROL_NAVIGATE( OnControlNavigate ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + //MAP_CONTROL(IDC_XuiEditTitle, m_EditTitle) + MAP_CONTROL(IDC_XuiLabelText, m_text) + MAP_CONTROL(IDC_XuiLabelCaption, m_LabelCaption) + MAP_CONTROL(IDC_XuiLabelDescription, m_LabelDescription) + MAP_CONTROL(IDC_XuiEditCaption, m_EditCaption) + MAP_CONTROL(IDC_XuiEditDescription, m_EditDesc) + MAP_CONTROL(IDC_XuiOK, m_OK) + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled); + HRESULT OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + D3DXVECTOR3 m_OriginalPosition; + int m_iPad; + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_SocialPost, L"CScene_SocialPost", XUI_CLASS_SCENE ) + + + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_Teleport.cpp b/Minecraft.Client/Common/XUI/XUI_Teleport.cpp new file mode 100644 index 0000000..1d6ac3b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Teleport.cpp @@ -0,0 +1,312 @@ +#include "stdafx.h" + +#include +#include "XUI_Teleport.h" +#include "..\..\ServerPlayer.h" +#include "..\..\PlayerConnection.h" +#include "..\..\PlayerList.h" +#include "..\..\MinecraftServer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\PlayerRenderer.h" +#include "XUI_InGamePlayerOptions.h" +#include "..\..\Minecraft.h" +#include "..\..\MultiPlayerLocalPlayer.h" +#include "..\..\ClientConnection.h" +#include "..\..\..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\..\Xbox\Network\NetworkPlayerXbox.h" +#include "TeleportCommand.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Teleport::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + TeleportMenuInitData *initParam = (TeleportMenuInitData *)pInitData->pvInitData; + + m_iPad = initParam->iPad; + m_teleportToPlayer = initParam->teleportToPlayer; + + delete initParam; + + MapChildControls(); + + if(m_teleportToPlayer) + { + m_title.SetText(app.GetString(IDS_TELEPORT_TO_PLAYER)); + } + else + { + m_title.SetText(app.GetString(IDS_TELEPORT_TO_ME)); + } + + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + DWORD playerCount = g_NetworkManager.GetPlayerCount(); + + m_playersCount = 0; + for(DWORD i = 0; i < playerCount; ++i) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerByIndex( i ); + + if( player != NULL && !(player->IsLocal() && player->GetUserIndex() == m_iPad) ) + { + m_players[m_playersCount] = player->GetSmallId(); + ++m_playersCount; + } + } + + g_NetworkManager.RegisterPlayerChangedCallback(m_iPad, &CScene_Teleport::OnPlayerChanged, this); + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK); + + CXuiSceneBase::ShowDarkOverlay( m_iPad, TRUE ); + + return S_OK; +} + +HRESULT CScene_Teleport::OnDestroy() +{ + g_NetworkManager.UnRegisterPlayerChangedCallback(m_iPad, &CScene_Teleport::OnPlayerChanged, this); + return S_OK; +} + +HRESULT CScene_Teleport::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr = S_OK; + switch(pInputData->dwKeyCode) + { + + case VK_PAD_B: + case VK_PAD_BACK: + case VK_ESCAPE: + CXuiSceneBase::PlayUISFX(eSFX_Back); + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + + break; + } + + + return hr; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_Teleport::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if( hObjPressed == playersList ) + { + INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId( m_players[ playersList.GetCurSel() ] ); + INetworkPlayer *thisPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(m_iPad); + + shared_ptr packet; + if(m_teleportToPlayer) + { + packet = TeleportCommand::preparePacket(thisPlayer->GetUID(),selectedPlayer->GetUID()); + } + else + { + packet = TeleportCommand::preparePacket(selectedPlayer->GetUID(),thisPlayer->GetUID()); + } + ClientConnection *conn = Minecraft::GetInstance()->getConnection(m_iPad); + conn->send( packet ); + } + return S_OK; +} + +void CScene_Teleport::OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving) +{ + CScene_Teleport *scene = (CScene_Teleport *)callbackParam; + bool playerFound = false; + + for(int i = 0; i < scene->m_playersCount; ++i) + { + if(playerFound) + { + scene->m_players[i-1] = scene->m_players[i]; + } + else if( scene->m_players[i] == pPlayer->GetSmallId() ) + { + if( scene->playersList.GetCurSel() == scene->playersList.GetItemCount() - 1 ) + { + scene->playersList.SetCurSel( scene->playersList.GetItemCount() - 2 ); + } + // Player removed + playerFound = true; + } + } + + if( playerFound ) + { + --scene->m_playersCount; + scene->playersList.DeleteItems( scene->playersList.GetItemCount() - 1, 1 ); + } + + if( !playerFound ) + { + // Player added + scene->m_players[scene->m_playersCount] = pPlayer->GetSmallId(); + ++scene->m_playersCount; + scene->playersList.InsertItems( scene->playersList.GetItemCount(), 1 ); + } +} + +HRESULT CScene_Teleport::OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled) +{ + if( pGetSourceTextData->bItemData ) + { + if( pGetSourceTextData->iItem < m_playersCount ) + { + INetworkPlayer *player = g_NetworkManager.GetPlayerBySmallId( m_players[pGetSourceTextData->iItem] ); + if( player != NULL ) + { +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && (app.GetGameSettingsDebugMask()&(1L<szText = L"WWWWWWWWWWWWWWWW"; + } + else +#endif + { + pGetSourceTextData->szText = player->GetOnlineName(); + } + } + else + { + pGetSourceTextData->szText = L""; + } + + HRESULT hr; + HXUIOBJ hButton, hVisual, hPlayerIcon, hVoiceIcon; + hButton = playersList.GetItemControl(pGetSourceTextData->iItem); + hr=XuiControlGetVisual(hButton,&hVisual); + + // Set the players icon + hr=XuiElementGetChildById(hVisual,L"IconGroup",&hPlayerIcon); + short colourIndex = app.GetPlayerColour( m_players[pGetSourceTextData->iItem] ); + int playFrame = 0; + switch(colourIndex) + { + case 1: + XuiElementFindNamedFrame(hPlayerIcon, L"P1", &playFrame); + break; + case 2: + XuiElementFindNamedFrame(hPlayerIcon, L"P2", &playFrame); + break; + case 3: + XuiElementFindNamedFrame(hPlayerIcon, L"P3", &playFrame); + break; + case 4: + XuiElementFindNamedFrame(hPlayerIcon, L"P4", &playFrame); + break; + case 5: + XuiElementFindNamedFrame(hPlayerIcon, L"P5", &playFrame); + break; + case 6: + XuiElementFindNamedFrame(hPlayerIcon, L"P6", &playFrame); + break; + case 7: + XuiElementFindNamedFrame(hPlayerIcon, L"P7", &playFrame); + break; + case 8: + XuiElementFindNamedFrame(hPlayerIcon, L"P8", &playFrame); + break; + case 9: + XuiElementFindNamedFrame(hPlayerIcon, L"P9", &playFrame); + break; + case 10: + XuiElementFindNamedFrame(hPlayerIcon, L"P10", &playFrame); + break; + case 11: + XuiElementFindNamedFrame(hPlayerIcon, L"P11", &playFrame); + break; + case 12: + XuiElementFindNamedFrame(hPlayerIcon, L"P12", &playFrame); + break; + case 13: + XuiElementFindNamedFrame(hPlayerIcon, L"P13", &playFrame); + break; + case 14: + XuiElementFindNamedFrame(hPlayerIcon, L"P14", &playFrame); + break; + case 15: + XuiElementFindNamedFrame(hPlayerIcon, L"P15", &playFrame); + break; + case 0: + default: + XuiElementFindNamedFrame(hPlayerIcon, L"P0", &playFrame); + break; + }; + if(playFrame < 0) playFrame = 0; + XuiElementPlayTimeline(hPlayerIcon,playFrame,playFrame,playFrame,FALSE,FALSE); + + // Set the voice icon + hr=XuiElementGetChildById(hVisual,L"VoiceGroup",&hVoiceIcon); + + playFrame = -1; + if(player != NULL && player->HasVoice() ) + { + if( player->IsMutedByLocalUser(m_iPad) ) + { + // Muted image + XuiElementFindNamedFrame(hVoiceIcon, L"Muted", &playFrame); + } + else if( player->IsTalking() ) + { + // Talking image + XuiElementFindNamedFrame(hVoiceIcon, L"Speaking", &playFrame); + } + else + { + // Not talking image + XuiElementFindNamedFrame(hVoiceIcon, L"NotSpeaking", &playFrame); + } + } + + if(playFrame < 0) + { + XuiElementFindNamedFrame(hVoiceIcon, L"Normal", &playFrame); + } + XuiElementPlayTimeline(hVoiceIcon,playFrame,playFrame,playFrame,FALSE,FALSE); + + pGetSourceTextData->bDisplay = TRUE; + + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_Teleport::OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled) +{ + if( pGetSourceImageData->bItemData ) + { + if( pGetSourceImageData->iItem < m_playersCount ) + { + bHandled = TRUE; + } + } + return S_OK; +} + +HRESULT CScene_Teleport::OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled) +{ + pGetItemCountData->cItems = m_playersCount; + bHandled = TRUE; + return S_OK; +} + +HRESULT CScene_Teleport::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_Teleport.h b/Minecraft.Client/Common/XUI/XUI_Teleport.h new file mode 100644 index 0000000..cf60eab --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_Teleport.h @@ -0,0 +1,64 @@ +#pragma once +using namespace std; +#include "../media/xuiscene_teleportmenu.h" +#include "XUI_CustomMessages.h" + +class INetworkPlayer; + +#define VOICE_ICON_DATA_ID 0 +#define MAP_ICON_DATA_ID 1 +#define OPS_ICON_DATA_ID 2 + +class CScene_Teleport : public CXuiSceneImpl +{ +protected: + // Control and Element wrapper objects. + CXuiList playersList; + CXuiControl m_title; + + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_KEYDOWN(OnKeyDown) + + XUI_ON_XM_GET_SOURCE_TEXT(OnGetSourceDataText) + XUI_ON_XM_GET_SOURCE_IMAGE(OnGetSourceDataImage) + XUI_ON_XM_GET_ITEMCOUNT_ALL(OnGetItemCountAll) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_GamePlayers, playersList) + MAP_CONTROL(IDC_Title, m_title) + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); + + HRESULT OnGetSourceDataText(XUIMessageGetSourceText *pGetSourceTextData, BOOL& bHandled); + HRESULT OnGetSourceDataImage(XUIMessageGetSourceImage *pGetSourceImageData,BOOL& bHandled); + HRESULT OnGetItemCountAll(XUIMessageGetItemCount *pGetItemCountData, BOOL& bHandled); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_Teleport, L"CScene_Teleport", XUI_CLASS_SCENE ) + + static void OnPlayerChanged(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + +private: + int m_iPad; + D3DXVECTOR3 m_OriginalPosition; + bool m_teleportToPlayer; + + int m_playersCount; + BYTE m_players[MINECRAFT_NET_MAX_PLAYERS]; // An array of QNet small-id's +}; diff --git a/Minecraft.Client/Common/XUI/XUI_TextEntry.cpp b/Minecraft.Client/Common/XUI/XUI_TextEntry.cpp new file mode 100644 index 0000000..8934350 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TextEntry.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" +#include "XUI_TextEntry.h" +#include "..\..\MultiplayerLocalPlayer.h" + + +CScene_TextEntry::CommandParams CScene_TextEntry::CommandA[CScene_TextEntry::eCommand_MAX]= +{ + { L"goto", L"%s%c%d%c%d" }, + { L"give", L"%s%c%s" } +}; + +HRESULT CScene_TextEntry::Init_Commands() +{ + for(int i=0;ipvInitData; + m_iPad=params->iPad; + m_wchInitialChar=params->wch; + delete params; + + WCHAR wchEditText[40]; + + Init_Commands(); + + ZeroMemory(wchEditText,sizeof(WCHAR)*40); + wchEditText[0]=tolower(m_wchInitialChar); + + m_EditText.SetTextLimit(40); + m_EditText.SetText(wchEditText); + // set the caret to the end of the default text + m_EditText.SetCaretPosition(1); + + m_EditText.SetTitleAndText(IDS_NAME_WORLD,IDS_NAME_WORLD_TEXT); + + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_EXECUTE_COMMAND, IDS_TOOLTIPS_BACK); + return S_OK; +} + +HRESULT CScene_TextEntry::OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled) +{ + // If the user presses return, interpret the string, and exit + if(pValueChangedData->nValue==10) + { + LPCWSTR pText = m_EditText.GetText(); + + if(pText) + { + wstring wText = pText; + InterpretString(wText); + } + + app.NavigateBack(m_iPad); + rfHandled = TRUE; + } + + return S_OK; +} + +HRESULT CScene_TextEntry::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_A: + { + LPCWSTR pText = m_EditText.GetText(); + + if(pText) + { + wstring wText = pText; + InterpretString(wText); + } + + app.NavigateBack(m_iPad); + rfHandled = TRUE; + } + break; + + case VK_PAD_B: + case VK_ESCAPE: + + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + } + return S_OK; +} + +HRESULT CScene_TextEntry::InterpretString(wstring &wsText) +{ + wstring wsFormat; + WCHAR wchCommand[40]; + int iCommand=-1; + WCHAR wchSep[2]; + +#ifdef __PS3__ + // 4J Stu - The Xbox version uses swscanf_s which isn't available in GCC. + swscanf(wsText.c_str(), L"%40s", wchCommand); +#else + swscanf_s(wsText.c_str(), L"%s", wchCommand,40); +#endif + + AUTO_VAR(it, m_CommandSet.find(wchCommand)); + if(it != m_CommandSet.end()) + { + // found it + + iCommand=it->second; + + switch(iCommand) + { + case eCommand_Teleport: + { + int x,z; + +#ifdef __PS3__ + // 4J Stu - The Xbox version uses swscanf_s which isn't available in GCC. + swscanf(wsText.c_str(), L"%40s%c%d%c%d", wchCommand,wchSep,&x,wchSep,&z); +#else + swscanf_s(wsText.c_str(), L"%s%c%d%c%d", wchCommand,40,wchSep,2,&x,wchSep,2, &z); +#endif + + app.DebugPrintf("eCommand_Teleport x=%d z=%d\n",x,z); + + // check the bounds + int iBound=54*16; + if( (x>-iBound) && (x-iBound) && (zlocalplayers[m_iPad]->teleportTo(x,pMinecraft->localplayers[m_iPad]->y,z); + } + } + break; + case eCommand_Give: + { + int iItem,iCount; + +#ifdef __PS3__ + // 4J Stu - The Xbox version uses swscanf_s which isn't available in GCC. + swscanf(wsText.c_str(), L"%40s%c%d%c%d", wchCommand,wchSep,&iItem,wchSep,&iCount); +#else + swscanf_s(wsText.c_str(), L"%s%c%d%c%d", wchCommand,40,wchSep,2,&iItem,wchSep,2, &iCount); +#endif + app.DebugPrintf("eCommand_Give, item=%d count=%d\n",iItem,iCount); + Minecraft *pMinecraft=Minecraft::GetInstance(); + for(int i=0;ilocalplayers[m_iPad]->drop(); // shared_ptr(new ItemInstance( iItem, 1, 0 )) ); + } + + break; + default: + app.DebugPrintf("Unknown command\n"); + break; + } + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_TextEntry.h b/Minecraft.Client/Common/XUI/XUI_TextEntry.h new file mode 100644 index 0000000..70dcf30 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TextEntry.h @@ -0,0 +1,64 @@ +#pragma once +#include "..\Media\xuiscene_text_entry.h" +#include "XUI_Ctrl_4JEdit.h" + + +class CScene_TextEntry : public CXuiSceneImpl +{ +public: + typedef struct _XuiTextInputParams + { + int iPad; + WCHAR wch; + } + XuiTextInputParams; + + typedef struct _CommamndParams + { + WCHAR wchCommand[40]; + WCHAR wchFormat[40]; + } + CommandParams; + + enum + { + eCommand_Teleport=0, + eCommand_Give, + eCommand_MAX + } + eCommands; + +protected: + CXuiCtrl4JEdit m_EditText; + + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_NOTIFY_VALUE_CHANGED(OnNotifyValueChanged) + XUI_ON_XM_KEYDOWN(OnKeyDown) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiEditText, m_EditText) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnNotifyValueChanged (HXUIOBJ hObjSource, XUINotifyValueChanged* pValueChangedData, BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + + HRESULT InterpretString(wstring &wsText); + HRESULT Init_Commands(); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_TextEntry, L"CScene_TextEntry", XUI_CLASS_SCENE ) + +private: + int m_iPad; + WCHAR m_wchInitialChar; + static CommandParams CommandA[eCommand_MAX]; + unordered_map m_CommandSet; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.cpp b/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.cpp new file mode 100644 index 0000000..5a2e67b --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.cpp @@ -0,0 +1,569 @@ +#include "stdafx.h" +#include +#include +#include + +#include "XUI_Ctrl_4JList.h" +#include "XUI_Ctrl_4JIcon.h" +#include "XUI_LoadSettings.h" +#include "..\..\ProgressRenderer.h" +#include "XUI_TransferToXboxOne.h" + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_TransferToXboxOne::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iX=-1; + m_params = (LoadMenuInitData *)pInitData->pvInitData; + + m_iPad=m_params->iPad; + + m_bRetrievingSaveInfo=false; + m_bIgnoreInput=false; + MapChildControls(); + + VOID *pObj; + XuiObjectFromHandle( m_SavesSlotList, &pObj ); + m_pSavesSlotList = (CXuiCtrl4JList *)pObj; + + m_pbImageData=NULL; + m_dwImageBytes=0; + + StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex,m_XContentData); + StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex,&m_pbImageData,&m_dwImageBytes); + + + m_SavesSlotListTimer.SetShow(TRUE); + + + XuiControlSetText(m_SavesSlotList,app.GetString(IDS_XBONE_SELECTSLOT)); + + // insert the current save slot names + m_MaxSlotC=app.m_uiTransferSlotC; + m_pSlotDataA = new SLOTDATA [m_MaxSlotC]; + ZeroMemory(m_pSlotDataA,sizeof(SLOTDATA)*m_MaxSlotC); + + + // saves will be called slot1 to slotx + // there will be a details file with the names and png of each slot + m_pbSlotListFile=NULL; + m_uiSlotListFileBytes=0; + + if(StorageManager.TMSPP_InFileList(C4JStorage::eGlobalStorage_TitleUser,m_iPad,L"XboxOne/SlotList")) + { + // there is a slot list file with details of the saves + C4JStorage::ETMSStatus status=StorageManager.TMSPP_ReadFile(m_iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,"XboxOne/SlotList",&CScene_TransferToXboxOne::TMSPPSlotListReturned,this); + m_iX=IDS_TOOLTIPS_CLEARSLOTS; + } + else + { + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + + // create dummy slots + for(int i=0;iAddData(ListInfo); + } + m_SavesSlotListTimer.SetShow(FALSE); + } + + + CXuiSceneBase::SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1,-1,-1,-1,-1,-1,true); + + return S_OK; +} + +//---------------------------------------------------------------------------------- +// TMSPPSlotListReturned callback +//---------------------------------------------------------------------------------- +int CScene_TransferToXboxOne::TMSPPWriteReturned(LPVOID pParam,int iPad,int iUserData) +{ + CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne *) pParam; + pClass->m_bWaitingForWrite=false; + + return 0; +} + +//---------------------------------------------------------------------------------- +// TMSPPSlotListReturned callback +//---------------------------------------------------------------------------------- +int CScene_TransferToXboxOne::TMSPPDeleteReturned(LPVOID pParam,int iPad,int iUserData) +{ + CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne *) pParam; + pClass->m_SavesSlotListTimer.SetShow(FALSE); + pClass->m_bIgnoreInput=false; + + // update the slots + delete pClass->m_pbSlotListFile; + pClass->m_pbSlotListFile=NULL; + pClass->m_uiSlotListFileBytes=0; + pClass->m_pSavesSlotList->RemoveAllData(); + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + // clear our slot info + ZeroMemory(pClass->m_pSlotDataA,sizeof(SLOTDATA)*pClass->m_MaxSlotC); + + for(int i=0;im_MaxSlotC;i++) + { + memcpy(pClass->m_pSlotDataA[i].wchSaveTitle,app.GetString(IDS_XBONE_EMPTYSLOT),sizeof(WCHAR)*XCONTENT_MAX_DISPLAYNAME_LENGTH); + ListInfo.pwszText = app.GetString(IDS_XBONE_EMPTYSLOT); + ListInfo.fEnabled = TRUE; + ListInfo.iData = -1; + pClass->m_pSavesSlotList->AddData(ListInfo); + } + + return 0; +} + +//---------------------------------------------------------------------------------- +// TMSPPSlotListReturned callback +//---------------------------------------------------------------------------------- +int CScene_TransferToXboxOne::TMSPPSlotListReturned(LPVOID pParam,int iPad,int iUserData,C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename) +{ + CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne *) pParam; + unsigned int uiSlotListFileSlots=*((unsigned int *)pFileData->pbData); + pClass->m_pbSlotListFile=pFileData->pbData; + pClass->m_uiSlotListFileBytes=pFileData->dwSize; + + // clear our slot info + ZeroMemory(pClass->m_pSlotDataA,sizeof(SLOTDATA)*pClass->m_MaxSlotC); + // set the empty slot strings + for(int i=0;im_MaxSlotC;i++) + { + memcpy(pClass->m_pSlotDataA[i].wchSaveTitle,app.GetString(IDS_XBONE_EMPTYSLOT),sizeof(WCHAR)*XCONTENT_MAX_DISPLAYNAME_LENGTH); + } + // update our slot info with the data from the file - might have less slots + unsigned int uiNewSlotsC=(pClass->m_MaxSlotCm_MaxSlotC:uiSlotListFileSlots; + memcpy(pClass->m_pSlotDataA,pClass->m_pbSlotListFile + sizeof(unsigned int),sizeof(SLOTDATA)*uiNewSlotsC); + + CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; + + ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); + PBYTE pbImageData=pClass->m_pbSlotListFile + sizeof(unsigned int) + sizeof(SLOTDATA)*uiSlotListFileSlots; + + // fill out the slot info + for(int i=0;im_MaxSlotC;i++) + { + if(im_pSlotDataA[i].wchSaveTitle; + ListInfo.fEnabled = TRUE; + ListInfo.iData = -1; + pClass->m_pSavesSlotList->AddData(ListInfo); + + if(pClass->m_pSlotDataA[i].uiImageLength!=0) + { + XuiCreateTextureBrushFromMemory(pbImageData,pClass->m_pSlotDataA[i].uiImageLength,&pClass->m_hXuiBrush); + pClass->m_pSavesSlotList->UpdateGraphic(i,pClass->m_hXuiBrush); + // increment the image data pointer + pbImageData+=pClass->m_pSlotDataA[i].uiImageLength; + } + } + else + { + // make it blank + ListInfo.pwszText = app.GetString(IDS_XBONE_EMPTYSLOT); + ListInfo.fEnabled = TRUE; + ListInfo.iData = -1; + pClass->m_pSavesSlotList->AddData(ListInfo); + } + } + pClass->m_SavesSlotListTimer.SetShow(FALSE); + return 0; + +} + +//---------------------------------------------------------------------------------- +// Handler for OnDestroy +//---------------------------------------------------------------------------------- +HRESULT CScene_TransferToXboxOne::OnDestroy() +{ + return S_OK; +} + +//---------------------------------------------------------------------------------- +// Handler for the button press message. +//---------------------------------------------------------------------------------- +HRESULT CScene_TransferToXboxOne::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // if we're retrieving save info, ignore key presses + if(m_bRetrievingSaveInfo) + { + return S_OK; + } + + // This assumes all buttons can only be pressed with the A button + ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A); + + if(hObjPressed==m_SavesSlotList) + { + m_bIgnoreInput=true; + + // update the info in the SlotList file + CXuiControl pItem; + int iIndex; + + // get the selected item + iIndex=m_SavesSlotList.GetCurSel(&pItem); + + // check if there is a save there + + CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesSlotList->GetData(iIndex); + if(info.pwszImage!=NULL) + { + // we have a save here + // Are you sure, etc. + } + + // update the data + memcpy(m_pSlotDataA[iIndex].wchSaveTitle,m_XContentData.szDisplayName,sizeof(WCHAR)*XCONTENT_MAX_DISPLAYNAME_LENGTH); + m_pSavesSlotList->UpdateText(iIndex,m_pSlotDataA[iIndex].wchSaveTitle); + + // if there is no thumbnail, retrieve the default one from the file. + // Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save + bool bHostOptionsRead = false; + unsigned int uiHostOptions = 0; + + XuiCreateTextureBrushFromMemory(m_pbImageData,m_dwImageBytes,&m_hXuiBrush); + m_pSavesSlotList->UpdateGraphic(iIndex,m_hXuiBrush); + + m_pSlotDataA[iIndex].uiImageLength=m_dwImageBytes; + + m_bIgnoreInput=false; + + // finished so navigate back + //app.NavigateBack(XUSER_INDEX_ANY); + BuildSlotFile(iIndex,m_pbImageData,m_dwImageBytes); + } + + return S_OK; +} + +HRESULT CScene_TransferToXboxOne::BuildSlotFile(int iIndexBeingUpdated,PBYTE pbImageData,DWORD dwImageBytes ) +{ + SLOTDATA *pCurrentSlotData=NULL; + PBYTE pbCurrentSlotDataPtr=NULL; + // there may be no slot file yet + if(m_pbSlotListFile!=NULL) + { + pCurrentSlotData=(SLOTDATA *)(m_pbSlotListFile+sizeof(unsigned int)); + pbCurrentSlotDataPtr=m_pbSlotListFile + sizeof(unsigned int) + sizeof(SLOTDATA)*m_MaxSlotC; + } + + m_uiSlotID=iIndexBeingUpdated; + + // memory required - first int is the number of slots in this file, in case that changes later + unsigned int uiBytesRequired=sizeof(unsigned int) + sizeof(SLOTDATA)*m_MaxSlotC; + for(int i=0;ifunc = &CScene_TransferToXboxOne::UploadSaveForXboxOneThreadProc; + loadingParams->lpParam = (LPVOID)this; + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + completionData->bShowBackground=TRUE; + completionData->bShowLogo=TRUE; + completionData->iPad = m_iPad; + completionData->type = e_ProgressCompletion_NavigateBackToScene; + completionData->scene = eUIScene_LoadMenu; + loadingParams->completionData = completionData; + + app.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); + + return S_OK; +} + +int CScene_TransferToXboxOne::UploadSaveForXboxOneThreadProc( LPVOID lpParameter ) +{ + HRESULT hr = S_OK; + char szFilename[32]; + CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne *) lpParameter; + Minecraft *pMinecraft = Minecraft::GetInstance(); + unsigned int uiComplete=0; + pClass->m_bWaitingForWrite=true; + pMinecraft->progressRenderer->progressStart(IDS_XBONE_UPLOAD_SAVE_TITLE); + pMinecraft->progressRenderer->progressStage( IDS_XBONE_UPLOAD_METADATA ); + // now write the new slot data file to global storage, and then write the save data + C4JStorage::ETMSStatus eStatus=StorageManager.TMSPP_WriteFile(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,C4JStorage::TMS_UGCTYPE_NONE,"XboxOne/SlotList",(PCHAR) pClass->m_pbSlotListFile, pClass->m_uiSlotListFileBytes, + &CScene_TransferToXboxOne::TMSPPWriteReturned,lpParameter, 0); + + if(eStatus!=C4JStorage::ETMSStatus_WriteInProgress) + { + // failed + pClass->m_bWaitingForWrite=false; + } + else + { + // loop waiting for the write to complete + uiComplete=0; + while(pClass->m_bWaitingForWrite && (hr == S_OK)) + { + Sleep(50); + uiComplete++; + if(uiComplete>100) uiComplete=100; + + pMinecraft->progressRenderer->progressStagePercentage(uiComplete); + + if(app.GetChangingSessionType()) + { + // 4J Stu - This causes the fullscreenprogress scene to ignore the action it was given + hr = ERROR_CANCELLED; + } + } + + if(hr!=S_OK) return -1; + + // finish the bar + for(int i=uiComplete;i<100;i++) + { + Sleep(5); + pMinecraft->progressRenderer->progressStagePercentage(i); + } + + + // now upload the save data + pMinecraft->progressRenderer->progressStage( IDS_XBONE_UPLOAD_SAVE ); + + // write the save file, and increment the progress percentage + pMinecraft->progressRenderer->progressStagePercentage(25); + pClass->m_bSaveDataReceived=false; + C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&pClass->m_XContentData,CScene_TransferToXboxOne::LoadSaveDataReturned,lpParameter); + + // sleep until we have the data + while(pClass->m_bSaveDataReceived==false) + { + Sleep(50); + } + + // write the save to user TMS + + // break the file up into 256K chunks + unsigned int uiChunkSize=262144; + unsigned int uiBytesLeft=pClass->m_uiStorageLength; + C4JStorage::ETMSStatus eStatus; + // max file size would be 100*256K + unsigned int uiPercentageChunk=100/(pClass->m_uiStorageLength/uiChunkSize); + uiComplete=0; + + if(uiPercentageChunk==0) uiPercentageChunk=1; + + for(int i=0;i<(pClass->m_uiStorageLength/uiChunkSize)+1;i++) + { + sprintf( szFilename, "XboxOne/Slot%.2d%.2d", pClass->m_uiSlotID,i ); + PCHAR pchData=((PCHAR)pClass->m_pvSaveMem)+i*uiChunkSize; + + pClass->m_bWaitingForWrite=true; + if(uiBytesLeft>=uiChunkSize) + { + eStatus=StorageManager.TMSPP_WriteFile(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,C4JStorage::TMS_UGCTYPE_NONE,szFilename,pchData, uiChunkSize, + &CScene_TransferToXboxOne::TMSPPWriteReturned,lpParameter, 0); + uiBytesLeft-=uiChunkSize; + } + else + { + // last bit of the file to upload is less than uiChunkSize + eStatus=StorageManager.TMSPP_WriteFile(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,C4JStorage::TMS_UGCTYPE_NONE,szFilename,pchData, uiBytesLeft, + &CScene_TransferToXboxOne::TMSPPWriteReturned,lpParameter, 0); + } + + // wait until + if(eStatus!=C4JStorage::ETMSStatus_WriteInProgress) + { + // failed + pClass->m_bWaitingForWrite=false; + } + else + { + // loop waiting for the write to complete + while(pClass->m_bWaitingForWrite && (hr == S_OK)) + { + Sleep(50); + } + uiComplete+=uiPercentageChunk; + if(uiComplete>100) uiComplete=100; + + // update the progress + pMinecraft->progressRenderer->progressStagePercentage(uiComplete); + } + } + + + + delete pClass->m_pvSaveMem; + } + return hr; +} + +int CScene_TransferToXboxOne::LoadSaveDataReturned(void *pParam,bool bContinue) +{ + CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)pParam; + + if(bContinue==true) + { + unsigned int uiFileSize = StorageManager.GetSaveSize(); + pClass->m_pvSaveMem = new BYTE [uiFileSize]; + pClass->m_uiStorageLength=0; + + StorageManager.GetSaveData( pClass->m_pvSaveMem, &pClass->m_uiStorageLength ); + + pClass->m_bSaveDataReceived=true; + } + + return 0; +} + +HRESULT CScene_TransferToXboxOne::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + if(m_bIgnoreInput) return S_OK; + + // if we're retrieving save info, ignore key presses + if(m_bRetrievingSaveInfo) + { + return S_OK; + } + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr = S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_B: + case VK_ESCAPE: + + app.NavigateBack(XUSER_INDEX_ANY); + rfHandled = TRUE; + break; + case VK_PAD_X: + // wipe the save slots + if(m_pbSlotListFile!=NULL) + { + m_SavesSlotListTimer.SetShow(TRUE); + m_bIgnoreInput=true; + + C4JStorage::ETMSStatus eStatus=StorageManager.TMSPP_DeleteFile(m_iPad,"XboxOne/SlotList",C4JStorage::TMS_FILETYPE_BINARY,&CScene_TransferToXboxOne::TMSPPDeleteReturned,this, 0); + + } + + break; + } + + return hr; +} + +HRESULT CScene_TransferToXboxOne::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled) +{ + + //if(m_bReady) + { + CXuiSceneBase::PlayUISFX(eSFX_Focus); + } + + return S_OK; +} + + +HRESULT CScene_TransferToXboxOne::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ) +{ + //if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK; + + if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY || + pTransition->dwTransType == XUI_TRANSITION_FROM || pTransition->dwTransType == XUI_TRANSITION_BACKFROM) + { + // 4J Stu - We may have had to unload our font renderer in this scene if one of the save files + // uses characters not in our font (eg asian chars) so restore our font renderer + // This will not do anything if our font renderer is already loaded + app.OverrideFontRenderer(true,true); + } + + return S_OK; +} + +HRESULT CScene_TransferToXboxOne::OnFontRendererChange() +{ + // update the tooltips + CXuiSceneBase::SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1,-1,-1,-1,-1,-1,true); + + return S_OK; +} + +HRESULT CScene_TransferToXboxOne::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + // update the tooltips + CXuiSceneBase::SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1,-1,-1,-1,-1,-1,true); + + return S_OK; +} + +HRESULT CScene_TransferToXboxOne::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled) +{ + return S_OK; +} + + diff --git a/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.h b/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.h new file mode 100644 index 0000000..f08de59 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TransferToXboxOne.h @@ -0,0 +1,88 @@ +#pragma once +using namespace std; +//#include + +#include "..\Media\xuiscene_TransferToXboxOne.h" + + +class CXuiCtrl4JList; + +class CScene_TransferToXboxOne : public CXuiSceneImpl +{ +protected: + + typedef struct + { + WCHAR wchSaveTitle[XCONTENT_MAX_DISPLAYNAME_LENGTH]; + unsigned int uiImageLength; + } + SLOTDATA; + + CXuiCtrl4JList *m_pSavesSlotList; + CXuiList m_SavesSlotList; + CXuiControl m_SavesSlotListTimer; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_DESTROY(OnDestroy) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_NOTIFY_PRESS_EX(OnNotifyPressEx) + XUI_ON_XM_NOTIFY_SELCHANGED(OnNotifySelChanged) + XUI_ON_XM_NOTIFY_SET_FOCUS(OnNotifySetFocus) + XUI_ON_XM_NOTIFY_KILL_FOCUS(OnNotifyKillFocus) + XUI_ON_XM_TRANSITION_START(OnTransitionStart) + XUI_ON_XM_FONTRENDERERCHANGE_MESSAGE(OnFontRendererChange) + + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_SavesSlotTimer, m_SavesSlotListTimer) + MAP_CONTROL(IDC_SavesSlotsList, m_SavesSlotList) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnDestroy(); + HRESULT OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData,BOOL& rfHandled); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnNavReturn(HXUIOBJ hObj,BOOL& rfHandled); + HRESULT OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled); + HRESULT OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + HRESULT OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled ); + HRESULT OnFontRendererChange(); + +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_TransferToXboxOne, L"CScene_TransferToXboxOne", XUI_CLASS_SCENE ) + + static int TMSPPSlotListReturned(LPVOID pParam,int iPad,int iUserData,C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename); + static int TMSPPWriteReturned(LPVOID pParam,int iPad,int iUserData); + static int TMSPPDeleteReturned(LPVOID pParam,int iPad,int iUserData); + static int UploadSaveForXboxOneThreadProc( LPVOID lpParameter ); + static int LoadSaveDataReturned(void *pParam,bool bContinue); +private: + HRESULT BuildSlotFile(int iIndexBeingUpdated,PBYTE pbImageData,DWORD dwImageBytes); + + bool m_bIgnoreInput; + bool m_bRetrievingSaveInfo; + int m_iPad; + int m_MaxSlotC; + int m_iX; // tooltip for clearing all slots if there are saves in them + LoadMenuInitData *m_params; + XCONTENT_DATA m_XContentData; + PBYTE m_pbImageData; + DWORD m_dwImageBytes; + HXUIBRUSH m_hXuiBrush; + PBYTE m_pbSlotListFile; + unsigned int m_uiSlotListFileBytes; + SLOTDATA *m_pSlotDataA; + bool m_bWaitingForWrite; + void *m_pvSaveMem; + unsigned int m_uiStorageLength; + bool m_bSaveDataReceived; + unsigned int m_uiSlotID; +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.cpp b/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.cpp new file mode 100644 index 0000000..5112109 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "XUI_TrialExitUpsell.h" + +// wchImages[TRIAL_EXIT_UPSELL_IMAGE_COUNT] +WCHAR *CScene_TrialExitUpsell::wchImages[]= +{ + L"Graphics/UpsellScreenshots/Screenshot1.png", + L"Graphics/UpsellScreenshots/Screenshot2.png", + L"Graphics/UpsellScreenshots/Screenshot3.png", + L"Graphics/UpsellScreenshots/Screenshot4.png", + L"Graphics/UpsellScreenshots/Screenshot5.png", + L"Graphics/UpsellScreenshots/Screenshot6.png", + L"Graphics/UpsellScreenshots/Screenshot7.png", + L"Graphics/UpsellScreenshots/Screenshot8.png" +}; + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_TrialExitUpsell::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad=*(int *)pInitData->pvInitData; + + MapChildControls(); + + m_bCanExit = true; + m_bFadeStarted = false; + m_bShowingImage1 = true; + m_imagesShown = 0; + + m_image1.SetImagePath( wchImages[m_imagesShown] ); + SetTimer( 0, TRIAL_EXIT_UPSELL_IMAGE_DISPLAY_TIME); + + CXuiSceneBase::ShowBackground(DEFAULT_XUI_MENU_USER , FALSE); + CXuiSceneBase::ShowLogo(DEFAULT_XUI_MENU_USER , FALSE); + + if( m_bCanExit ) + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_EXIT_GAME,IDS_TOOLTIPS_BACK, IDS_UNLOCK_TITLE); + } + else + { + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1,IDS_TOOLTIPS_BACK, IDS_UNLOCK_TITLE); + } + + return S_OK; +} + +HRESULT CScene_TrialExitUpsell::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled) +{ + // ignore repeats + if(pInputData->dwFlags&XUI_INPUT_FLAG_REPEAT) return S_OK; + + ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + + // Explicitly handle B button presses + switch(pInputData->dwKeyCode) + { + case VK_PAD_A: +#ifdef _XBOX + if( m_bCanExit ) + { + XLaunchNewImage(XLAUNCH_KEYWORD_DASH_ARCADE, 0); + } +#endif + break; + case VK_PAD_B: + case VK_ESCAPE: + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + break; + case VK_PAD_X: + if(ProfileManager.IsSignedIn(pInputData->UserIndex)) + { + TelemetryManager->RecordUpsellPresented(pInputData->UserIndex, eSen_UpsellID_Full_Version_Of_Game, app.m_dwOfferID); + ProfileManager.DisplayFullVersionPurchase(false,pInputData->UserIndex,eSen_UpsellID_Full_Version_Of_Game); + } + break; + } + + return hr; +} + +HRESULT CScene_TrialExitUpsell::OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled) +{ + if(m_bFadeStarted) + { + m_bFadeStarted = false; + m_bShowingImage1 = !m_bShowingImage1; + // We start a timer so we know when this image has been displayed for the required time + return SetTimer( 0, TRIAL_EXIT_UPSELL_IMAGE_DISPLAY_TIME); + } + else + { + return S_OK; + } +} + +HRESULT CScene_TrialExitUpsell::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ) +{ + int nStart, nEnd; + + KillTimer(0); + + ++m_imagesShown; + if( m_imagesShown >= TRIAL_EXIT_UPSELL_IMAGE_COUNT ) + { + m_imagesShown = 0; //Loop round to the start + m_bCanExit = true; + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_EXIT_GAME,IDS_TOOLTIPS_BACK, IDS_UNLOCK_TITLE); + } + + if( m_bShowingImage1 ) + { + m_image2.SetImagePath( wchImages[m_imagesShown] ); + + FindNamedFrame( L"Fade1to2", &nStart ); + FindNamedFrame( L"EndFade1to2", &nEnd ); + PlayTimeline( nStart, nStart, nEnd, FALSE, TRUE ); + m_bFadeStarted = true; + } + else + { + m_image1.SetImagePath( wchImages[m_imagesShown] ); + + FindNamedFrame( L"Fade2to1", &nStart ); + FindNamedFrame( L"EndFade2to1", &nEnd ); + PlayTimeline( nStart, nStart, nEnd, FALSE, TRUE ); + m_bFadeStarted = true; + } + + return S_OK; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.h b/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.h new file mode 100644 index 0000000..748b36a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TrialExitUpsell.h @@ -0,0 +1,46 @@ +#pragma once + +#include "../media/xuiscene_trialexitupsell.h" + +#define TRIAL_EXIT_UPSELL_IMAGE_DISPLAY_TIME 3000 + +#define TRIAL_EXIT_UPSELL_IMAGE_COUNT 8 + +class CScene_TrialExitUpsell : public CXuiSceneImpl +{ +private: + static WCHAR *wchImages[TRIAL_EXIT_UPSELL_IMAGE_COUNT]; + +protected: + CXuiImageElement m_image1, m_image2; + + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_KEYDOWN(OnKeyDown) + XUI_ON_XM_TIMELINE_END(OnTimelineEnd) + XUI_ON_XM_TIMER( OnTimer ) + XUI_END_MSG_MAP() + + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_XuiImage1, m_image1) + MAP_CONTROL(IDC_XuiImage2, m_image2) + END_CONTROL_MAP() + + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled); + HRESULT OnTimelineEnd(HXUIOBJ hObjSource, BOOL& bHandled); + HRESULT OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled ); + + int m_iPad; + bool m_bCanExit; + bool m_bFadeStarted; + bool m_bShowingImage1; + unsigned char m_imagesShown; +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_TrialExitUpsell, L"CScene_TrialExitUpsell", XUI_CLASS_SCENE ) + +}; diff --git a/Minecraft.Client/Common/XUI/XUI_TutorialPopup.cpp b/Minecraft.Client/Common/XUI/XUI_TutorialPopup.cpp new file mode 100644 index 0000000..98951d8 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TutorialPopup.cpp @@ -0,0 +1,603 @@ +#include "stdafx.h" +#include "..\..\Common\Tutorial\Tutorial.h" +#include "..\..\MultiplayerLocalPlayer.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "..\..\..\Minecraft.World\Tile.h" +#include "..\..\..\Minecraft.World\Item.h" +#include "XUI_Ctrl_CraftIngredientSlot.h" +#include "XUI_XZP_Icons.h" + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_TutorialPopup::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + HRESULT hr = S_OK; + + tutorial = (Tutorial *)pInitData->pvInitData; + m_iPad = tutorial->getPad(); + + MapChildControls(); + + // splitscreen? + // if we are in splitscreen, then we need to figure out if we want to move this scene + if(app.GetLocalPlayerCount()>1) + { + app.AdjustSplitscreenScene(m_hObj,&m_OriginalPosition,m_iPad); + } + + m_textFontSize = _fromString( m_fontSizeControl.GetText() ); + m_fontSizeControl.SetShow(false); + + m_interactScene = NULL; + m_lastSceneMovedLeft = false; + m_bAllowFade = false; + + SetShow(false); + XuiSetTimer(m_hObj,TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + + return hr; +} + +HRESULT CScene_TutorialPopup::OnDestroy() +{ + return S_OK; +} + +HRESULT CScene_TutorialPopup::OnTimer(XUIMessageTimer *pData,BOOL& rfHandled) +{ + if( pData->nId == TUTORIAL_POPUP_FADE_TIMER_ID ) + { + XuiKillTimer(m_hObj,TUTORIAL_POPUP_FADE_TIMER_ID); + SetShow( false ); + XuiSetTimer(m_hObj,TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + } + else if( pData->nId == TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID ) + { + UpdateInteractScenePosition(IsShown() == TRUE); + XuiKillTimer(m_hObj,TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID); + } + + return S_OK; +} + +void CScene_TutorialPopup::UpdateInteractScenePosition(bool visible) +{ + if( m_interactScene == NULL ) return; + + // 4J-PB - check this players screen section to see if we should allow the animation + bool bAllowAnim=false; + HXUICLASS sceneClass = XuiGetObjectClass( m_interactScene->m_hObj ); + + HXUICLASS inventoryClass = XuiFindClass( L"CXuiSceneInventory" ); + HXUICLASS furnaceClass = XuiFindClass( L"CXuiSceneFurnace" ); + HXUICLASS craftingClass = XuiFindClass( L"CXuiSceneCraftingPanel" ); + HXUICLASS trapClass = XuiFindClass( L"CXuiSceneTrap" ); + HXUICLASS containerClass = XuiFindClass( L"CXuiSceneContainer" ); + HXUICLASS creativeInventoryClass = XuiFindClass( L"CXuiSceneInventoryCreative" ); + HXUICLASS enchantingClass = XuiFindClass( L"CXuiSceneEnchant" ); + HXUICLASS brewingClass = XuiFindClass( L"CXuiSceneBrewingStand" ); + HXUICLASS anvilClass = XuiFindClass( L"CXuiSceneAnvil" ); + HXUICLASS tradingClass = XuiFindClass( L"CXuiSceneTrading" ); + BOOL isCraftingScene = XuiClassDerivesFrom( sceneClass, craftingClass ); + BOOL isCreativeScene = XuiClassDerivesFrom( sceneClass, creativeInventoryClass ); + + switch(Minecraft::GetInstance()->localplayers[m_iPad]->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + bAllowAnim=true; + break; + default: + // anim allowed for everything except the crafting 2x2 and 3x3, and the creative menu + if(!isCraftingScene && !isCreativeScene) + { + bAllowAnim=true; + } + break; + } + + if(bAllowAnim) + { + + XUITimeline *timeline; + XUINamedFrame *startFrame; + XUINamedFrame *endFrame; + bool movingLeft = false; + // Also returns TRUE if they are the same (which is what we want) + if( XuiClassDerivesFrom( sceneClass, inventoryClass ) || + XuiClassDerivesFrom( sceneClass, furnaceClass ) || + XuiClassDerivesFrom( sceneClass, trapClass ) || + XuiClassDerivesFrom( sceneClass, containerClass ) || + XuiClassDerivesFrom( sceneClass, enchantingClass ) || + XuiClassDerivesFrom( sceneClass, brewingClass ) || + XuiClassDerivesFrom( sceneClass, anvilClass ) || + XuiClassDerivesFrom( sceneClass, tradingClass ) || + isCreativeScene || + isCraftingScene == TRUE ) + { + XuiElementGetTimeline( m_interactScene->m_hObj, &timeline); + if(visible) + { + startFrame = timeline->FindNamedFrame( L"MoveLeft" ); + endFrame = timeline->FindNamedFrame( L"EndMoveLeft" ); + movingLeft = true; + } + else + { + startFrame = timeline->FindNamedFrame( L"MoveRight" ); + endFrame = timeline->FindNamedFrame( L"EndMoveRight" ); + } + + if( (m_lastInteractSceneMoved != m_interactScene && movingLeft) || ( m_lastInteractSceneMoved == m_interactScene && m_lastSceneMovedLeft != movingLeft ) ) + { + timeline->Play( startFrame->m_dwFrame, startFrame->m_dwFrame, endFrame->m_dwFrame, FALSE, FALSE ); + + m_lastInteractSceneMoved = m_interactScene; + m_lastSceneMovedLeft = movingLeft; + } + } + } + +} + +HRESULT CScene_TutorialPopup::_SetDescription(CXuiScene *interactScene, LPCWSTR desc, LPCWSTR title, bool allowFade, bool isReminder) +{ + HRESULT hr = S_OK; + m_interactScene = interactScene; + if( interactScene != m_lastInteractSceneMoved ) m_lastInteractSceneMoved = NULL; + if(desc == NULL) + { + SetShow( false ); + XuiSetTimer(m_hObj,TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + XuiKillTimer(m_hObj,TUTORIAL_POPUP_FADE_TIMER_ID); + } + else + { + SetShow( true ); + XuiSetTimer(m_hObj,TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID,TUTORIAL_POPUP_MOVE_SCENE_TIME); + + if( allowFade ) + { + //Initialise a timer to fade us out again + XuiSetTimer(m_hObj,TUTORIAL_POPUP_FADE_TIMER_ID,tutorial->GetTutorialDisplayMessageTime()); + } + else + { + XuiKillTimer(m_hObj,TUTORIAL_POPUP_FADE_TIMER_ID); + } + m_bAllowFade = allowFade; + + if(isReminder) + { + wstring text(app.GetString( IDS_TUTORIAL_REMINDER )); + text.append( desc ); + // set the text colour + wchar_t formatting[40]; + swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White),m_textFontSize); + text = formatting + text; + + hr = m_description.SetText( text.c_str() ); + } + else + { + wstring text(desc); + // set the text colour + wchar_t formatting[40]; + swprintf(formatting, 40, L"",app.GetHTMLColour(eHTMLColor_White),m_textFontSize); + text = formatting + text; + + hr = m_description.SetText( text.c_str() ); + } + + D3DXVECTOR3 titlePos; + hr = XuiElementGetPosition( m_title, &titlePos ); + + BOOL titleShowAtStart = m_title.IsShown(); + if( title != NULL && title[0] != 0 ) + { + m_title.SetText( title ); + m_title.SetShow(TRUE); + } + else + { + m_title.SetText( L"" ); + m_title.SetShow(FALSE); + } + BOOL titleShowAtEnd = m_title.IsShown(); + if(titleShowAtStart != titleShowAtEnd) + { + float fHeight, fWidth, fTitleHeight, fDescHeight, fDescWidth; + m_title.GetBounds(&fWidth,&fTitleHeight); + GetBounds(&fWidth,&fHeight); + + + // 4J Stu - For some reason when we resize the scene it resets the size of the HTML control + // We don't want that to happen, so get it's size before and set it back after + m_description.GetBounds(&fDescWidth,&fDescHeight); + if(titleShowAtEnd) + { + titlePos.y += fTitleHeight; + + SetBounds(fWidth, fHeight + fTitleHeight); + } + else + { + SetBounds(fWidth, fHeight - fTitleHeight); + } + XuiElementSetPosition( m_description, &titlePos ); + m_description.SetBounds(fDescWidth, fDescHeight); + } + + // Check if we need to resize the box + XUIContentDims contentDims; + m_description.GetContentDims(&contentDims); + + int heightDiff = contentDims.nContentHeight - contentDims.nPageHeight; + + float fHeight, fWidth; + GetBounds(&fWidth,&fHeight); + SetBounds(fWidth, fHeight + heightDiff); + + m_description.GetBounds(&fWidth,&fHeight); + m_description.SetBounds(fWidth, (float)(contentDims.nPageHeight + heightDiff)); + } + return hr; +} + +HRESULT CScene_TutorialPopup::SetDescription(int iPad, TutorialPopupInfo *info) +{ + HRESULT hr = S_OK; + +#ifdef _XBOX + HXUIOBJ hObj = app.GetCurrentTutorialScene(iPad); + HXUICLASS thisClass = XuiFindClass( L"CScene_TutorialPopup" ); + HXUICLASS objClass = XuiGetObjectClass( hObj ); + + // Also returns TRUE if they are the same (which is what we want) + if( XuiClassDerivesFrom( objClass, thisClass ) ) + { + + CScene_TutorialPopup *pThis; + hr = XuiObjectFromHandle(hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + wstring parsed = pThis->_SetIcon(info->icon, info->iAuxVal, info->isFoil, info->desc); + parsed = pThis->_SetImage( parsed ); + parsed = CScene_TutorialPopup::ParseDescription(iPad, parsed); + if(parsed.empty()) + { + hr = pThis->_SetDescription( info->interactScene, NULL, NULL, info->allowFade, info->isReminder ); + } + else + { + hr = pThis->_SetDescription( info->interactScene, parsed.c_str(), info->title, info->allowFade, info->isReminder ); + } + } +#endif + return hr; +} + +wstring CScene_TutorialPopup::_SetIcon(int icon, int iAuxVal, bool isFoil, LPCWSTR desc) +{ + wstring temp(desc); + + BOOL iconShowAtStart = m_pCraftingPic->IsShown(); + + if( icon != TUTORIAL_NO_ICON ) + { + bool itemIsFoil = false; + itemIsFoil = (shared_ptr(new ItemInstance(icon,1,iAuxVal)))->isFoil(); + if(!itemIsFoil) itemIsFoil = isFoil; + + m_pCraftingPic->SetIcon(m_iPad, icon,iAuxVal,1,10,31,false,itemIsFoil); + } + else + { + wstring openTag(L"{*ICON*}"); + wstring closeTag(L"{*/ICON*}"); + int iconTagStartPos = (int)temp.find(openTag); + int iconStartPos = iconTagStartPos + (int)openTag.length(); + if( iconTagStartPos > 0 && iconStartPos < (int)temp.length() ) + { + int iconEndPos = (int)temp.find( closeTag, iconStartPos ); + + if(iconEndPos > iconStartPos && iconEndPos < (int)temp.length() ) + { + wstring id = temp.substr(iconStartPos, iconEndPos - iconStartPos); + + vector idAndAux = stringSplit(id,L':'); + + int iconId = _fromString(idAndAux[0]); + + if(idAndAux.size() > 1) + { + iAuxVal = _fromString(idAndAux[1]); + } + else + { + iAuxVal = 0; + } + + bool itemIsFoil = false; + itemIsFoil = (shared_ptr(new ItemInstance(iconId,1,iAuxVal)))->isFoil(); + if(!itemIsFoil) itemIsFoil = isFoil; + + m_pCraftingPic->SetIcon(m_iPad, iconId,iAuxVal,1,10,31,false,itemIsFoil); + + temp.replace(iconTagStartPos, iconEndPos - iconTagStartPos + closeTag.length(), L""); + } + } + + // remove any icon text + else if(temp.find(L"{*CraftingTableIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::workBench->id,0,1,10,31,false); + } + else if(temp.find(L"{*SticksIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::stick->id,0,1,10,31,false); + } + else if(temp.find(L"{*PlanksIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::wood->id,0,1,10,31,false); + } + else if(temp.find(L"{*WoodenShovelIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::shovel_wood->id,0,1,10,31,false); + } + else if(temp.find(L"{*WoodenHatchetIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::hatchet_wood->id,0,1,10,31,false); + } + else if(temp.find(L"{*WoodenPickaxeIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::pickAxe_wood->id,0,1,10,31,false); + } + else if(temp.find(L"{*FurnaceIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::furnace_Id,0,1,10,31,false); + } + else if(temp.find(L"{*WoodenDoorIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::door_wood->id,0,1,10,31,false); + } + else if(temp.find(L"{*TorchIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::torch_Id,0,1,10,31,false); + } + else if(temp.find(L"{*BoatIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::boat_Id,0,1,10,31,false); + } + else if(temp.find(L"{*FishingRodIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::fishingRod_Id,0,1,10,31,false); + } + else if(temp.find(L"{*FishIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::fish_raw_Id,0,1,10,31,false); + } + else if(temp.find(L"{*MinecartIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Item::minecart_Id,0,1,10,31,false); + } + else if(temp.find(L"{*RailIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::rail_Id,0,1,10,31,false); + } + else if(temp.find(L"{*PoweredRailIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::goldenRail_Id,0,1,10,31,false); + } + else if(temp.find(L"{*StructuresIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, XZP_ICON_STRUCTURES,0,1,10,31,false); + } + else if(temp.find(L"{*ToolsIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, XZP_ICON_TOOLS,0,1,10,31,false); + } + else if(temp.find(L"{*StoneIcon*}")!=wstring::npos) + { + m_pCraftingPic->SetIcon(m_iPad, Tile::rock_Id,0,1,10,31,false); + } + else + { + // hide the icon slot + m_pCraftingPic->SetIcon(m_iPad, 0,0,0,0,0,false,false,FALSE); + } + } + + BOOL iconShowAtEnd = m_pCraftingPic->IsShown(); + if(iconShowAtStart != iconShowAtEnd) + { + float fHeight, fWidth, fIconHeight, fDescHeight, fDescWidth; + m_pCraftingPic->GetBounds(&fWidth,&fIconHeight); + GetBounds(&fWidth,&fHeight); + + + // 4J Stu - For some reason when we resize the scene it resets the size of the HTML control + // We don't want that to happen, so get it's size before and set it back after + m_description.GetBounds(&fDescWidth,&fDescHeight); + if(iconShowAtEnd) + { + SetBounds(fWidth, fHeight + fIconHeight); + } + else + { + SetBounds(fWidth, fHeight - fIconHeight); + } + m_description.SetBounds(fDescWidth, fDescHeight); + } + + return temp; +} + +wstring CScene_TutorialPopup::_SetImage(wstring &desc) +{ + + BOOL imageShowAtStart = m_image.IsShown(); + + wstring openTag(L"{*IMAGE*}"); + wstring closeTag(L"{*/IMAGE*}"); + int imageTagStartPos = (int)desc.find(openTag); + int imageStartPos = imageTagStartPos + (int)openTag.length(); + if( imageTagStartPos > 0 && imageStartPos < (int)desc.length() ) + { + int imageEndPos = (int)desc.find( closeTag, imageStartPos ); + + if(imageEndPos > imageStartPos && imageEndPos < (int)desc.length() ) + { + wstring id = desc.substr(imageStartPos, imageEndPos - imageStartPos); + m_image.SetImagePath( id.c_str() ); + m_image.SetShow( TRUE ); + + desc.replace(imageTagStartPos, imageEndPos - imageTagStartPos + closeTag.length(), L""); + } + } + else + { + // hide the icon slot + m_image.SetShow( FALSE ); + } + + BOOL imageShowAtEnd = m_image.IsShown(); + if(imageShowAtStart != imageShowAtEnd) + { + float fHeight, fWidth, fIconHeight, fDescHeight, fDescWidth; + m_image.GetBounds(&fWidth,&fIconHeight); + GetBounds(&fWidth,&fHeight); + + + // 4J Stu - For some reason when we resize the scene it resets the size of the HTML control + // We don't want that to happen, so get it's size before and set it back after + m_description.GetBounds(&fDescWidth,&fDescHeight); + if(imageShowAtEnd) + { + SetBounds(fWidth, fHeight + fIconHeight); + } + else + { + SetBounds(fWidth, fHeight - fIconHeight); + } + m_description.SetBounds(fDescWidth, fDescHeight); + } + + return desc; +} + + +wstring CScene_TutorialPopup::ParseDescription(int iPad, wstring &text) +{ + text = replaceAll(text, L"{*CraftingTableIcon*}", L""); + text = replaceAll(text, L"{*SticksIcon*}", L""); + text = replaceAll(text, L"{*PlanksIcon*}", L""); + text = replaceAll(text, L"{*WoodenShovelIcon*}", L""); + text = replaceAll(text, L"{*WoodenHatchetIcon*}", L""); + text = replaceAll(text, L"{*WoodenPickaxeIcon*}", L""); + text = replaceAll(text, L"{*FurnaceIcon*}", L""); + text = replaceAll(text, L"{*WoodenDoorIcon*}", L""); + text = replaceAll(text, L"{*TorchIcon*}", L""); + text = replaceAll(text, L"{*MinecartIcon*}", L""); + text = replaceAll(text, L"{*BoatIcon*}", L""); + text = replaceAll(text, L"{*FishingRodIcon*}", L""); + text = replaceAll(text, L"{*FishIcon*}", L""); + text = replaceAll(text, L"{*RailIcon*}", L""); + text = replaceAll(text, L"{*PoweredRailIcon*}", L""); + text = replaceAll(text, L"{*StructuresIcon*}", L""); + text = replaceAll(text, L"{*ToolsIcon*}", L""); + text = replaceAll(text, L"{*StoneIcon*}", L""); + + if( app.GetLocalPlayerCount() > 1 ) + { + // TODO TU-1 - This should really be a string as well rather than hardcoded here + text = replaceAll(text, L"{*EXIT_PICTURE*}", L"
" ); + + // TODO TU-1 - This should also be separate strings, or move these things out of a localisable file so we can add/change them at will + text = replaceAll(text, L"height=\"30\" width=\"30\"", L"height=\"20\" width=\"20\""); + } + else + { + text = replaceAll(text, L"{*EXIT_PICTURE*}", app.GetString( IDS_TUTORIAL_HTML_EXIT_PICTURE ) ); + } + /* +#define MINECRAFT_ACTION_RENDER_DEBUG ACTION_INGAME_13 +#define MINECRAFT_ACTION_PAUSEMENU ACTION_INGAME_15 +#define MINECRAFT_ACTION_SNEAK_TOGGLE ACTION_INGAME_17 + */ + + return app.FormatHTMLString(iPad,text); +} + +HRESULT CScene_TutorialPopup::_SetVisible(bool show) +{ + HRESULT hr = this->SetShow( show ); + + if( show && m_bAllowFade ) + { + //Initialise a timer to fade us out again + XuiSetTimer(m_hObj,TUTORIAL_POPUP_FADE_TIMER_ID,tutorial->GetTutorialDisplayMessageTime()); + } + + return hr; +} + +bool CScene_TutorialPopup::_IsSceneVisible() +{ + bool isVisible = this->IsShown()==TRUE?true:false; + + return isVisible; +} + +HRESULT CScene_TutorialPopup::SetSceneVisible(int iPad, bool show) +{ + HRESULT hr = S_OK; + + HXUIOBJ hObj = app.GetCurrentTutorialScene(iPad); + HXUICLASS thisClass = XuiFindClass( L"CScene_TutorialPopup" ); + HXUICLASS objClass = XuiGetObjectClass( hObj ); + + // Also returns TRUE if they are the same (which is what we want) + if( XuiClassDerivesFrom( objClass, thisClass ) ) + { + CScene_TutorialPopup *pThis; + hr = XuiObjectFromHandle(hObj, (void **) &pThis); + if (FAILED(hr)) + return hr; + + hr = pThis->_SetVisible( show ); + } + return hr; +} + + +HRESULT CScene_TutorialPopup::OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled) +{ + bHandled=true; + return app.AdjustSplitscreenScene_PlayerChanged(m_hObj,&m_OriginalPosition,m_iPad,bJoining); +} + +bool CScene_TutorialPopup::IsSceneVisible(int iPad) +{ + bool isVisible = false; + HRESULT hr; + + HXUIOBJ hObj = app.GetCurrentTutorialScene(iPad); + HXUICLASS thisClass = XuiFindClass( L"CScene_TutorialPopup" ); + HXUICLASS objClass = XuiGetObjectClass( hObj ); + + // Also returns TRUE if they are the same (which is what we want) + if( XuiClassDerivesFrom( objClass, thisClass ) ) + { + CScene_TutorialPopup *pThis; + hr = XuiObjectFromHandle(hObj, (void **) &pThis); + if (FAILED(hr)) + return false; + + isVisible = pThis->_IsSceneVisible(); + } + return isVisible; +} \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_TutorialPopup.h b/Minecraft.Client/Common/XUI/XUI_TutorialPopup.h new file mode 100644 index 0000000..6571675 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_TutorialPopup.h @@ -0,0 +1,78 @@ +#pragma once +#include "../media/xuiscene_tutorialpopup.h" +#include "XUI_CustomMessages.h" + +class Tutorial; +class CXuiCtrlCraftIngredientSlot; + +#define TUTORIAL_POPUP_FADE_TIMER_ID 0 +#define TUTORIAL_POPUP_MOVE_SCENE_TIMER_ID 1 +#define TUTORIAL_POPUP_MOVE_SCENE_TIME 500 + +class CScene_TutorialPopup : public CXuiSceneImpl +{ +private: + Tutorial *tutorial; + + // A scene that may be displayed behind the popup that the player is using, that will need shifted so we can see it clearly. + CXuiScene *m_interactScene, *m_lastInteractSceneMoved; + bool m_lastSceneMovedLeft; + bool m_bAllowFade; + + int m_iPad; + + CXuiHtmlControl m_description; + CXuiCtrlCraftIngredientSlot *m_pCraftingPic; + CXuiControl m_title; + CXuiImageElement m_image; + + CXuiControl m_fontSizeControl; + + int m_textFontSize; + D3DXVECTOR3 m_OriginalPosition; + +protected: + // Message map. Here we tie messages to message handlers. + XUI_BEGIN_MSG_MAP() + XUI_ON_XM_INIT( OnInit ) + XUI_ON_XM_TIMER(OnTimer) + XUI_ON_XM_DESTROY( OnDestroy ) + XUI_ON_XM_SPLITSCREENPLAYER_MESSAGE(OnCustomMessage_Splitscreenplayer) + XUI_END_MSG_MAP() + + // Control mapping to objects + BEGIN_CONTROL_MAP() + MAP_CONTROL(IDC_Title, m_title) + MAP_CONTROL(IDC_Description, m_description) + MAP_OVERRIDE(IDC_XuiInventoryPic, m_pCraftingPic) + MAP_CONTROL(IDC_XuiImage, m_image) + MAP_CONTROL(IDC_FontSize, m_fontSizeControl) + + END_CONTROL_MAP() + + HRESULT OnInit( XUIMessageInit* pInitData, BOOL& bHandled ); + HRESULT OnTimer(XUIMessageTimer *pData,BOOL& rfHandled); + HRESULT OnDestroy(); + HRESULT OnCustomMessage_Splitscreenplayer(bool bJoining, BOOL& bHandled); +public: + + // Define the class. The class name must match the ClassOverride property + // set for the scene in the UI Authoring tool. + XUI_IMPLEMENT_CLASS( CScene_TutorialPopup, L"CScene_TutorialPopup", XUI_CLASS_SCENE ) + +private: + HRESULT _SetDescription(CXuiScene *interactScene, LPCWSTR desc, LPCWSTR title, bool allowFade, bool isReminder); + wstring _SetIcon(int icon, int iAuxVal, bool isFoil, LPCWSTR desc); + wstring _SetImage(wstring &desc); + HRESULT _SetVisible(bool show); + bool _IsSceneVisible(); + void UpdateInteractScenePosition(bool visible); + +public: + static HRESULT SetDescription(int iPad, TutorialPopupInfo *info); + static wstring ParseDescription(int iPad, wstring &text); + + static HRESULT SetSceneVisible(int iPad, bool show); + static bool IsSceneVisible(int iPad); + +}; \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_XZP_Icons.h b/Minecraft.Client/Common/XUI/XUI_XZP_Icons.h new file mode 100644 index 0000000..46a1c22 --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_XZP_Icons.h @@ -0,0 +1,20 @@ +#pragma once + +#define XZP_ICON_SKELETON 32000 +#define XZP_ICON_CREEPER 32001 +#define XZP_ICON_SPIDERJOCKEY 32002 +#define XZP_ICON_SPIDER 32003 +#define XZP_ICON_ZOMBIE 32004 +#define XZP_ICON_ZOMBIEPIGMAN 32005 +#define XZP_ICON_SWAM 32006 +#define XZP_ICON_WALKED 32007 +#define XZP_ICON_FALLEN 32008 +#define XZP_ICON_PORTAL 32009 +#define XZP_ICON_CLIMBED 32010 +#define XZP_ICON_GHAST 32011 +#define XZP_ICON_SLIME 32012 +#define XZP_ICON_STRUCTURES 32013 +#define XZP_ICON_TOOLS 32014 + +#define XZP_ICON_SHANK_01 0 +#define XZP_ICON_SHANK_03 1 \ No newline at end of file diff --git a/Minecraft.Client/Common/XUI/XUI_debug.cpp b/Minecraft.Client/Common/XUI/XUI_debug.cpp new file mode 100644 index 0000000..61b818a --- /dev/null +++ b/Minecraft.Client/Common/XUI/XUI_debug.cpp @@ -0,0 +1,391 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "XUI_Debug.h" +#include "..\..\..\Minecraft.Client\StatsCounter.h" + +LPCWSTR CScene_Debug::m_DebugCheckboxTextA[eDebugSetting_Max+1]= +{ + L"Load Saves From Local Folder Mode", + L"Write Saves To Local Folder Mode", + L"Freeze Players", //L"Not Used", + L"Display Safe Area", + L"Mobs don't attack", + L"Freeze Time", + L"Disable Weather", + L"Craft Anything", + L"Use DPad for debug", + L"Mobs don't tick", + L"Instant Mine", + L"Show UI Console", + L"Distributable Save", + L"Debug Leaderboards", + L"Height-Water-Biome Maps", + L"Superflat nether", + //L"Light/Dark background", + L"More lightning when thundering", + L"", +}; + +LPCWSTR CScene_Debug::m_DebugButtonTextA[eDebugButton_Max+1]= +{ + L"Award Theme", + L"Award Avatar Item 1", + L"Award Avatar Item 2", + L"Award Avatar Item 3", + L"Award Gamerpic 1", + L"Award Gamerpic 2", + L"Check Tips", + L"Wipe Leaderboards", + L"", +}; + + + +//---------------------------------------------------------------------------------- +// Performs initialization tasks - retrieves controls. +//---------------------------------------------------------------------------------- +HRESULT CScene_Debug::OnInit( XUIMessageInit* pInitData, BOOL& bHandled ) +{ + m_iPad=*(int *)pInitData->pvInitData; + + // set text and enable any debug options required + int iCheckboxIndex=0; + int iButtonIndex=0; + float fWidth=500.0f; + float fX=240.0f; + float fY=200.0f; + float fYInc=5.0f; + float fControlHeight=25.0f; + + if((!RenderManager.IsHiDef() && !RenderManager.IsWidescreen()) || app.GetLocalPlayerCount()>1) + { + fWidth=250.0f; + fX=30.0f; + fY=30.0f; + fYInc=5.0f; + fControlHeight=15.0f; + } + + CXuiCheckbox *pCheckbox; + CXuiControl *pButton; + m_iTotalCheckboxElements=0; + m_iTotalButtonElements=0; + m_bOnCheckboxes=true; + + // count the items + while((*m_DebugCheckboxTextA[m_iTotalCheckboxElements])!=0) + { + m_iTotalCheckboxElements++; + } + + m_DebugCheckboxDataA= new DEBUGDATA [m_iTotalCheckboxElements]; + + while((*m_DebugCheckboxTextA[iCheckboxIndex])!=0) + { + XuiCreateObject( XUI_CLASS_CHECKBOX, &m_DebugCheckboxDataA[iCheckboxIndex].hXuiObj ); + XuiObjectFromHandle( m_DebugCheckboxDataA[iCheckboxIndex].hXuiObj, &m_DebugCheckboxDataA[iCheckboxIndex].pvData ); + + pCheckbox = (CXuiCheckbox *)m_DebugCheckboxDataA[iCheckboxIndex].pvData; + //m_phXuiObjA[iElementIndex] = pCheckbox->m_hObj; + pCheckbox->SetText(m_DebugCheckboxTextA[iCheckboxIndex]); + pCheckbox->SetShow(TRUE); + + D3DXVECTOR3 vPos; + float tx,ty; + pCheckbox->GetBounds(&tx,&ty); + pCheckbox->SetBounds(fWidth,fControlHeight); + vPos.x=fX; + vPos.y=fY; + vPos.z=0.0f; + pCheckbox->SetPosition(&vPos); + fY+=fControlHeight+fYInc; + + XuiElementAddChild(m_hObj,m_DebugCheckboxDataA[iCheckboxIndex].hXuiObj); + + iCheckboxIndex++; + } + + // and the buttons + if((!RenderManager.IsHiDef() && !RenderManager.IsWidescreen())|| app.GetLocalPlayerCount()>1) + { + fWidth=260.0f; + fX=300.0f; + fY=30.0f; + fControlHeight=40.0f; + } + else + { + fWidth=350.0f; + fX=750.0f; + fY=200.0f; + fControlHeight=40.0f; + } + + + + while((*m_DebugButtonTextA[m_iTotalButtonElements])!=0) + { + m_iTotalButtonElements++; + } + + m_DebugButtonDataA= new DEBUGDATA [m_iTotalButtonElements]; + + while((*m_DebugButtonTextA[iButtonIndex])!=0) + { + XuiCreateObject( XUI_CLASS_BUTTON, &m_DebugButtonDataA[iButtonIndex].hXuiObj ); + XuiObjectFromHandle( m_DebugButtonDataA[iButtonIndex].hXuiObj, &m_DebugButtonDataA[iButtonIndex].pvData ); + + pButton = (CXuiControl *)m_DebugButtonDataA[iButtonIndex].pvData; + //m_phXuiObjA[iElementIndex] = pCheckbox->m_hObj; + pButton->SetText(m_DebugButtonTextA[iButtonIndex]); + pButton->SetShow(TRUE); + + D3DXVECTOR3 vPos; + float tx,ty; + pButton->GetBounds(&tx,&ty); + pButton->SetBounds(fWidth,fControlHeight); + vPos.x=fX; + vPos.y=fY; + vPos.z=0.0f; + pButton->SetPosition(&vPos); + fY+=fControlHeight+fYInc; + + XuiElementAddChild(m_hObj,m_DebugButtonDataA[iButtonIndex].hXuiObj); + + // if you're not the primary player, disable all these - you need a storage device for them + + if(ProfileManager.GetPrimaryPad()!=m_iPad) + { + XuiControlSetEnable(m_DebugButtonDataA[iButtonIndex].hXuiObj,FALSE); + } + iButtonIndex++; + } + + + + unsigned int uiDebugBitmask=app.GetGameSettingsDebugMask(m_iPad); + + for(int i=0;iSetCheck( (uiDebugBitmask&(1<UserIndex, pInputData->dwKeyCode); + + HRESULT hr=S_OK; + int iCurrentBitmaskIndex=0; + unsigned int uiDebugBitmask=0L; + + // Explicitly handle X button presses + if (pInputData->dwKeyCode == VK_PAD_B) + { + // check all the settings + for(int i=0;iIsChecked()?(1<UserIndex)) + { + app.SetGameSettingsDebugMask(pInputData->UserIndex,uiDebugBitmask); + if(app.DebugSettingsOn()) + { + app.ActionDebugMask(pInputData->UserIndex); + } + else + { + // force debug mask off + app.ActionDebugMask(pInputData->UserIndex,true); + } + + app.CheckGameSettingsChanged(true,pInputData->UserIndex); + } + app.NavigateBack(pInputData->UserIndex); + rfHandled = TRUE; + } + + + return hr; +} + + +HRESULT CScene_Debug::OnControlNavigate(XUIMessageControlNavigate *pControlNavigateData, BOOL& bHandled) +{ + if(m_bOnCheckboxes) + { + // If it's not from the current control, ignore it + if(m_DebugCheckboxDataA[m_iCurrentCheckboxElement].hXuiObj!=pControlNavigateData->hObjSource) return S_OK; + + switch(pControlNavigateData->nControlNavigate) + { + case XUI_CONTROL_NAVIGATE_UP: + if(m_iCurrentCheckboxElement>0) + { + m_iCurrentCheckboxElement--; + XuiElementSetUserFocus(m_DebugCheckboxDataA[m_iCurrentCheckboxElement].hXuiObj, m_iPad); + } + break; + case XUI_CONTROL_NAVIGATE_DOWN: + if((m_iCurrentCheckboxElement+1)hObjSource) return S_OK; + + switch(pControlNavigateData->nControlNavigate) + { + case XUI_CONTROL_NAVIGATE_UP: + if(m_iCurrentButtonElement>0) + { + m_iCurrentButtonElement--; + XuiElementSetUserFocus(m_DebugButtonDataA[m_iCurrentButtonElement].hXuiObj, m_iPad); + } + break; + case XUI_CONTROL_NAVIGATE_DOWN: + if((m_iCurrentButtonElement+1)UserIndex, VK_PAD_A); + + int iButton=0; + + while((iButtonUserIndex, eAward_socialPost ); + break; + case eDebugButton_Avatar_Item_1: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_eatPorkChop ); + break; + case eDebugButton_Avatar_Item_2: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_play100Days ); + break; + case eDebugButton_Avatar_Item_3: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_arrowKillCreeper ); + break; + case eDebugButton_Gamerpic_1: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_mine100Blocks ); + break; + case eDebugButton_Gamerpic_2: + ProfileManager.Award( pNotifyPressData->UserIndex, eAward_kill10Creepers ); + break; + case eDebugButton_CheckTips: + app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_DebugTips); + break; + + case eDebugButton_WipeLeaderboards: +#ifdef _DEBUG +// commenting out so it doesn't get done by mistake +// Minecraft::GetInstance()->stats[pNotifyPressData->UserIndex]->WipeLeaderboards(); +#endif + break; + + } + + return S_OK; +} + diff --git a/Minecraft.Client/Common/xuiscene_base.h b/Minecraft.Client/Common/xuiscene_base.h new file mode 100644 index 0000000..a4e71ad --- /dev/null +++ b/Minecraft.Client/Common/xuiscene_base.h @@ -0,0 +1,176 @@ +#define IDC_BottomLeftAnchorPoint L"BottomLeftAnchorPoint" +#define IDC_TopLeftAnchorPoint L"TopLeftAnchorPoint" +#define IDC_XuiDarkOverlay L"XuiDarkOverlay" +#define IDC_Background L"Background" +#define IDC_Logo L"Logo" +#define IDC_XuiSceneHudRoot L"XuiSceneHudRoot" +#define IDC_XuiSceneChatRoot L"XuiSceneChatRoot" +#define IDC_XuiSceneContainer L"XuiSceneContainer" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_Tooltips L"Tooltips" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_TooltipsSmall L"TooltipsSmall" +#define IDC_SelectedItem L"SelectedItem" +#define IDC_SelectedItemSmall L"SelectedItemSmall" +#define IDC_TitleText L"TitleText" +#define IDC_ProgressBar1 L"ProgressBar1" +#define IDC_ProgressBar2 L"ProgressBar2" +#define IDC_ProgressBar3 L"ProgressBar3" +#define IDC_ProgressBar1_small L"ProgressBar1_small" +#define IDC_ProgressBar2_small L"ProgressBar2_small" +#define IDC_ProgressBar3_small L"ProgressBar3_small" +#define IDC_BossHealth L"BossHealth" +#define IDC_XuiSceneTutorialContainer L"XuiSceneTutorialContainer" +#define IDC_XuiGamertag L"XuiGamertag" +#define IDC_BasePlayer3 L"BasePlayer3" +#define IDC_BottomLeftAnchorPoint L"BottomLeftAnchorPoint" +#define IDC_TopLeftAnchorPoint L"TopLeftAnchorPoint" +#define IDC_XuiDarkOverlay L"XuiDarkOverlay" +#define IDC_Background L"Background" +#define IDC_Logo L"Logo" +#define IDC_XuiSceneHudRoot L"XuiSceneHudRoot" +#define IDC_XuiSceneChatRoot L"XuiSceneChatRoot" +#define IDC_XuiSceneContainer L"XuiSceneContainer" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_Tooltips L"Tooltips" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_TooltipsSmall L"TooltipsSmall" +#define IDC_SelectedItem L"SelectedItem" +#define IDC_SelectedItemSmall L"SelectedItemSmall" +#define IDC_TitleText L"TitleText" +#define IDC_ProgressBar1 L"ProgressBar1" +#define IDC_ProgressBar2 L"ProgressBar2" +#define IDC_ProgressBar3 L"ProgressBar3" +#define IDC_ProgressBar1_small L"ProgressBar1_small" +#define IDC_ProgressBar2_small L"ProgressBar2_small" +#define IDC_ProgressBar3_small L"ProgressBar3_small" +#define IDC_BossHealth L"BossHealth" +#define IDC_XuiSceneTutorialContainer L"XuiSceneTutorialContainer" +#define IDC_XuiGamertag L"XuiGamertag" +#define IDC_BasePlayer2 L"BasePlayer2" +#define IDC_BottomLeftAnchorPoint L"BottomLeftAnchorPoint" +#define IDC_TopLeftAnchorPoint L"TopLeftAnchorPoint" +#define IDC_XuiDarkOverlay L"XuiDarkOverlay" +#define IDC_Background L"Background" +#define IDC_Logo L"Logo" +#define IDC_XuiSceneHudRoot L"XuiSceneHudRoot" +#define IDC_XuiSceneChatRoot L"XuiSceneChatRoot" +#define IDC_XuiSceneContainer L"XuiSceneContainer" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_Tooltips L"Tooltips" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_TooltipsSmall L"TooltipsSmall" +#define IDC_SelectedItem L"SelectedItem" +#define IDC_SelectedItemSmall L"SelectedItemSmall" +#define IDC_TitleText L"TitleText" +#define IDC_ProgressBar1 L"ProgressBar1" +#define IDC_ProgressBar2 L"ProgressBar2" +#define IDC_ProgressBar3 L"ProgressBar3" +#define IDC_ProgressBar1_small L"ProgressBar1_small" +#define IDC_ProgressBar2_small L"ProgressBar2_small" +#define IDC_ProgressBar3_small L"ProgressBar3_small" +#define IDC_BossHealth L"BossHealth" +#define IDC_XuiSceneTutorialContainer L"XuiSceneTutorialContainer" +#define IDC_XuiGamertag L"XuiGamertag" +#define IDC_BasePlayer1 L"BasePlayer1" +#define IDC_BottomLeftAnchorPoint L"BottomLeftAnchorPoint" +#define IDC_TopLeftAnchorPoint L"TopLeftAnchorPoint" +#define IDC_XuiDarkOverlay L"XuiDarkOverlay" +#define IDC_Background L"Background" +#define IDC_Logo L"Logo" +#define IDC_XuiSceneHudRoot L"XuiSceneHudRoot" +#define IDC_XuiSceneChatRoot L"XuiSceneChatRoot" +#define IDC_XuiSceneContainer L"XuiSceneContainer" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_Tooltips L"Tooltips" +#define IDC_LStick L"LStick" +#define IDC_LBButton L"LBButton" +#define IDC_RBButton L"RBButton" +#define IDC_RTrigger L"RTrigger" +#define IDC_LTrigger L"LTrigger" +#define IDC_YButton L"YButton" +#define IDC_XButton L"XButton" +#define IDC_BButton L"BButton" +#define IDC_AButton L"AButton" +#define IDC_TooltipsSmall L"TooltipsSmall" +#define IDC_SelectedItem L"SelectedItem" +#define IDC_SelectedItemSmall L"SelectedItemSmall" +#define IDC_TitleText L"TitleText" +#define IDC_ProgressBar1 L"ProgressBar1" +#define IDC_ProgressBar2 L"ProgressBar2" +#define IDC_ProgressBar3 L"ProgressBar3" +#define IDC_ProgressBar1_small L"ProgressBar1_small" +#define IDC_ProgressBar2_small L"ProgressBar2_small" +#define IDC_ProgressBar3_small L"ProgressBar3_small" +#define IDC_BossHealth L"BossHealth" +#define IDC_XuiSceneTutorialContainer L"XuiSceneTutorialContainer" +#define IDC_XuiGamertag L"XuiGamertag" +#define IDC_BasePlayer0 L"BasePlayer0" +#define IDC_XuiPressStartMessage L"XuiPressStartMessage" +#define IDC_XuiSceneDebugContainer L"XuiSceneDebugContainer" +#define IDC_XuiSavingIcon L"XuiSavingIcon" +#define IDC_XuiTrialTimer L"XuiTrialTimer" +#define IDC_SafeArea L"SafeArea" +#define IDC_XuiSoundXACTBack L"XuiSoundXACTBack" +#define IDC_XuiSoundXACTCraft L"XuiSoundXACTCraft" +#define IDC_XuiSoundXACTCraftFail L"XuiSoundXACTCraftFail" +#define IDC_XuiSoundXACTFocus L"XuiSoundXACTFocus" +#define IDC_XuiSoundXACTPress L"XuiSoundXACTPress" +#define IDC_XuiSoundXACTScroll L"XuiSoundXACTScroll" +#define IDC_XuiBaseScene L"XuiBaseScene" diff --git a/Minecraft.Client/Common/zlib/adler32.c b/Minecraft.Client/Common/zlib/adler32.c new file mode 100644 index 0000000..33d70c6 --- /dev/null +++ b/Minecraft.Client/Common/zlib/adler32.c @@ -0,0 +1,178 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ +#include "zutil.h" + +#define local static + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); + +#define BASE 65521 /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ +#ifdef NO_DIVIDE +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ + do { \ + CHOP(a); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD(a) \ + do { \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD28(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + + /* the derivation of this formula is left as an exercise for the reader */ + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/Minecraft.Client/Common/zlib/compress.c b/Minecraft.Client/Common/zlib/compress.c new file mode 100644 index 0000000..6f3f259 --- /dev/null +++ b/Minecraft.Client/Common/zlib/compress.c @@ -0,0 +1,81 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/Minecraft.Client/Common/zlib/crc32.c b/Minecraft.Client/Common/zlib/crc32.c new file mode 100644 index 0000000..979a719 --- /dev/null +++ b/Minecraft.Client/Common/zlib/crc32.c @@ -0,0 +1,425 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/Minecraft.Client/Common/zlib/crc32.h b/Minecraft.Client/Common/zlib/crc32.h new file mode 100644 index 0000000..9e0c778 --- /dev/null +++ b/Minecraft.Client/Common/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/Minecraft.Client/Common/zlib/deflate.c b/Minecraft.Client/Common/zlib/deflate.c new file mode 100644 index 0000000..6969577 --- /dev/null +++ b/Minecraft.Client/Common/zlib/deflate.c @@ -0,0 +1,1967 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + s = strm->state; + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateResetKeep (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + deflate_state *s; + int put; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len; + deflate_state *s = strm->state; + + _tr_flush_bits(s); + len = s->pending; + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s->lookahead <= MAX_MATCH) { + fill_window(s); + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} diff --git a/Minecraft.Client/Common/zlib/deflate.h b/Minecraft.Client/Common/zlib/deflate.h new file mode 100644 index 0000000..ce0299e --- /dev/null +++ b/Minecraft.Client/Common/zlib/deflate.h @@ -0,0 +1,346 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2012 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/Minecraft.Client/Common/zlib/gzclose.c b/Minecraft.Client/Common/zlib/gzclose.c new file mode 100644 index 0000000..caeb99a --- /dev/null +++ b/Minecraft.Client/Common/zlib/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/Minecraft.Client/Common/zlib/gzguts.h b/Minecraft.Client/Common/zlib/gzguts.h new file mode 100644 index 0000000..d87659d --- /dev/null +++ b/Minecraft.Client/Common/zlib/gzguts.h @@ -0,0 +1,209 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99, yet still not supported by + Microsoft more than a decade later!), _snprintf does not guarantee null + termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/Minecraft.Client/Common/zlib/gzlib.c b/Minecraft.Client/Common/zlib/gzlib.c new file mode 100644 index 0000000..fae202e --- /dev/null +++ b/Minecraft.Client/Common/zlib/gzlib.c @@ -0,0 +1,634 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_WIN32) && !defined(__BORLANDC__) +# define LSEEK _lseeki64 +#else +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const void *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + state->x.have = 0; /* no output data available */ + if (state->mode == GZ_READ) { /* for reading ... */ + state->eof = 0; /* not at end of file */ + state->past = 0; /* have not read past end yet */ + state->how = LOOK; /* look for gzip header */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->x.pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const void *path; + int fd; + const char *mode; +{ + gz_statep state; + size_t len; + int oflag; +#ifdef O_CLOEXEC + int cloexec = 0; +#endif +#ifdef O_EXCL + int exclusive = 0; +#endif + + /* check input */ + if (path == NULL) + return NULL; + + /* allocate gzFile structure to return */ + state = (gz_statep)malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; +#ifdef O_CLOEXEC + case 'e': + cloexec = 1; + break; +#endif +#ifdef O_EXCL + case 'x': + exclusive = 1; + break; +#endif + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + break; + case 'T': + state->direct = 1; + break; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + + /* save the path name for error messages */ +#ifdef _WIN32 + if (fd == -2) { + len = wcstombs(NULL, path, 0); + if (len == (size_t)-1) + len = 0; + } + else +#endif + len = strlen((const char *)path); + state->path = (char *)malloc(len + 1); + if (state->path == NULL) { + free(state); + return NULL; + } +#ifdef _WIN32 + if (fd == -2) + if (len) + wcstombs(state->path, path, len + 1); + else + *(state->path) = 0; + else +#endif +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->path, len + 1, "%s", (const char *)path); +#else + strcpy(state->path, path); +#endif + + /* compute the flags for open() */ + oflag = +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif +#ifdef O_CLOEXEC + (cloexec ? O_CLOEXEC : 0) | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | +#ifdef O_EXCL + (exclusive ? O_EXCL : 0) | +#endif + (state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))); + + /* open the file with the appropriate flags (or just use fd) */ + state->fd = fd > -1 ? fd : ( +#ifdef _WIN32 + fd == -2 ? _wopen(path, oflag, 0666) : +#endif + open((const char *)path, oflag, 0666)); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ +#else + sprintf(path, "", fd); /* for debugging */ +#endif + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +#ifdef _WIN32 +gzFile ZEXPORT gzopen_w(path, mode) + const wchar_t *path; + const char *mode; +{ + return gz_open(path, -2, mode); +} +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size < 2) + size = 2; /* need two bytes to check magic header */ + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->x.pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->x.pos + offset >= 0) { + ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + if (ret == -1) + return -1; + state->x.have = 0; + state->eof = 0; + state->past = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->x.pos += offset; + return state->x.pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->x.pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? + (unsigned)offset : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->x.pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->x.pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? state->past : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->err == Z_MEM_ERROR ? "out of memory" : + (state->msg == NULL ? "" : state->msg); +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) { + state->eof = 0; + state->past = 0; + } + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + if (err != Z_OK && err != Z_BUF_ERROR) + state->x.have = 0; + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, return literal string when requested */ + if (err == Z_MEM_ERROR) + return; + + /* construct error message with path */ + if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == + NULL) { + state->err = Z_MEM_ERROR; + return; + } +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); +#else + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); +#endif + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/Minecraft.Client/Common/zlib/gzread.c b/Minecraft.Client/Common/zlib/gzread.c new file mode 100644 index 0000000..bf4538e --- /dev/null +++ b/Minecraft.Client/Common/zlib/gzread.c @@ -0,0 +1,594 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_look OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_fetch OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + If strm->avail_in != 0, then the current data is moved to the beginning of + the input buffer, and then the remainder of the buffer is loaded with the + available data from the input file. */ +local int gz_avail(state) + gz_statep state; +{ + unsigned got; + z_streamp strm = &(state->strm); + + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + if (state->eof == 0) { + if (strm->avail_in) { /* copy what's there to the start */ + unsigned char *p = state->in; + unsigned const char *q = strm->next_in; + unsigned n = strm->avail_in; + do { + *p++ = *q++; + } while (--n); + } + if (gz_load(state, state->in + strm->avail_in, + state->size - strm->avail_in, &got) == -1) + return -1; + strm->avail_in += got; + strm->next_in = state->in; + } + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression. If direct copying, then leftover input + data from the input buffer will be copied to the output buffer. In that + case, all further file reads will be directly to either the output buffer or + a user buffer. If decompressing, the inflate state will be initialized. + gz_look() will return 0 on success or -1 on failure. */ +local int gz_look(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = (unsigned char *)malloc(state->want); + state->out = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get at least the magic bytes in the input buffer */ + if (strm->avail_in < 2) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for gzip magic bytes -- if there, do gzip decoding (note: there is + a logical dilemma here when considering the case of a partially written + gzip file, to wit, if a single 31 byte is written, then we cannot tell + whether this is a single-byte file, or just a partially written gzip + file -- for here we assume that if a gzip file is being written, then + the header will be written in a single operation, so that reading a + single byte is sufficient indication that it is not a gzip file) */ + if (strm->avail_in > 1 && + strm->next_in[0] == 31 && strm->next_in[1] == 139) { + inflateReset(strm); + state->how = GZIP; + state->direct = 0; + return 0; + } + + /* no gzip header -- if we were decoding gzip before, then this is trailing + garbage. Ignore the trailing garbage and finish. */ + if (state->direct == 0) { + strm->avail_in = 0; + state->eof = 1; + state->x.have = 0; + return 0; + } + + /* doing raw i/o, copy any leftover input to output -- this assumes that + the output buffer is larger than the input buffer, which also assures + space for gzungetc() */ + state->x.next = state->out; + if (strm->avail_in) { + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + On return, state->x.have and state->x.next point to the just decompressed + data. If the gzip stream completes, state->how is reset to LOOK to look for + the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + on success, -1 on failure. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret = Z_OK; + unsigned had; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_BUF_ERROR, "unexpected end of file"); + break; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output */ + state->x.have = had - strm->avail_out; + state->x.next = strm->next_out - state->x.have; + + /* if the gzip stream completed successfully, look for another */ + if (ret == Z_STREAM_END) + state->how = LOOK; + + /* good decompression */ + return 0; +} + +/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for to determine whether to copy or decompress. Returns -1 on error, + otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + end of the input file has been reached and all data has been processed. */ +local int gz_fetch(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + do { + switch(state->how) { + case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ + if (gz_look(state) == -1) + return -1; + if (state->how == LOOK) + return 0; + break; + case COPY: /* -> COPY */ + if (gz_load(state, state->out, state->size << 1, &(state->x.have)) + == -1) + return -1; + state->x.next = state->out; + return 0; + case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + } while (state->x.have == 0 && (!state->eof || strm->avail_in)); + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->x.have) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? + (unsigned)len : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->x.have) { + n = state->x.have > len ? len : state->x.have; + memcpy(buf, state->x.next, n); + state->x.next += n; + state->x.have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) { + state->past = 1; /* tried to read past end */ + break; + } + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + continue; /* no progress yet -- go back to copy above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = (unsigned char *)buf; + if (gz_decomp(state) == -1) + return -1; + n = state->x.have; + state->x.have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->x.pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +#else +# undef gzgetc +#endif +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->x.have) { + state->x.have--; + state->x.pos++; + return *(state->x.next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +int ZEXPORT gzgetc_(file) +gzFile file; +{ + return gzgetc(file); +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->x.have == 0) { + state->x.have = 1; + state->x.next = state->out + (state->size << 1) - 1; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->x.have == (state->size << 1)) { + gz_error(state, Z_DATA_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->x.next == state->out) { + unsigned char *src = state->out + state->x.have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->x.next = dest; + } + state->x.have++; + state->x.next--; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->x.have == 0 && gz_fetch(state) == -1) + return NULL; /* error */ + if (state->x.have == 0) { /* end of file */ + state->past = 1; /* read past end */ + break; /* return what we have */ + } + + /* look for end-of-line in current output buffer */ + n = state->x.have > left ? left : state->x.have; + eol = (unsigned char *)memchr(state->x.next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->x.next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->x.next, n); + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* return terminated string, or if nothing, end of file */ + if (buf == str) + return NULL; + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + + /* return 1 if transparent, 0 if processing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret, err; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : err; +} diff --git a/Minecraft.Client/Common/zlib/gzwrite.c b/Minecraft.Client/Common/zlib/gzwrite.c new file mode 100644 index 0000000..aa767fb --- /dev/null +++ b/Minecraft.Client/Common/zlib/gzwrite.c @@ -0,0 +1,577 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input buffer */ + state->in = (unsigned char *)malloc(state->want); + if (state->in == NULL) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = (unsigned char *)malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. If gz->direct + is true, then simply write to the output file without compressing, and + ignore flush. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* write directly if requested */ + if (state->direct) { + got = write(state->fd, strm->next_in, strm->avail_in); + if (got < 0 || (unsigned)got != strm->avail_in) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in = 0; + return 0; + } + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->x.next); + if (have && ((got = write(state->fd, state->x.next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->x.next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + unsigned have, copy; + + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + copy = state->size - have; + if (copy > len) + copy = len; + memcpy(state->in + have, buf, copy); + strm->avail_in += copy; + state->x.pos += copy; + buf = (const char *)buf + copy; + len -= copy; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (z_const Bytef *)buf; + state->x.pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned have; + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (have < state->size) { + state->in[have] = c; + strm->avail_in++; + state->x.pos++; + return c & 0xff; + } + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c & 0xff; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf((char *)(state->in), format, va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf((char *)(state->in), format, va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf((char *)(state->in), size, format, va); + len = strlen((char *)(state->in)); +# else + len = vsnprintf((char *)(state->in), size, format, va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) +{ + va_list va; + int ret; + + va_start(va, format); + ret = gzvprintf(file, format, va); + va_end(va); + return ret; +} + +#else /* !STDC && !Z_HAVE_STDARG_H */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that can really pass pointer in ints */ + if (sizeof(int) != sizeof(void *)) + return 0; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen((char *)(state->in)); +# else + len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, + a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = Z_OK; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + ret = state->err; + } + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1) + ret = state->err; + if (state->size) { + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + if (close(state->fd) == -1) + ret = Z_ERRNO; + free(state); + return ret; +} diff --git a/Minecraft.Client/Common/zlib/infback.c b/Minecraft.Client/Common/zlib/infback.c new file mode 100644 index 0000000..f3833c2 --- /dev/null +++ b/Minecraft.Client/Common/zlib/infback.c @@ -0,0 +1,640 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/Minecraft.Client/Common/zlib/inffast.c b/Minecraft.Client/Common/zlib/inffast.c new file mode 100644 index 0000000..bda59ce --- /dev/null +++ b/Minecraft.Client/Common/zlib/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/Minecraft.Client/Common/zlib/inffast.h b/Minecraft.Client/Common/zlib/inffast.h new file mode 100644 index 0000000..e5c1aa4 --- /dev/null +++ b/Minecraft.Client/Common/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/Minecraft.Client/Common/zlib/inffixed.h b/Minecraft.Client/Common/zlib/inffixed.h new file mode 100644 index 0000000..d628327 --- /dev/null +++ b/Minecraft.Client/Common/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/Minecraft.Client/Common/zlib/inflate.c b/Minecraft.Client/Common/zlib/inflate.c new file mode 100644 index 0000000..870f89b --- /dev/null +++ b/Minecraft.Client/Common/zlib/inflate.c @@ -0,0 +1,1512 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateResetKeep(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, end, copy) +z_streamp strm; +const Bytef *end; +unsigned copy; +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +const unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/Minecraft.Client/Common/zlib/inflate.h b/Minecraft.Client/Common/zlib/inflate.h new file mode 100644 index 0000000..95f4986 --- /dev/null +++ b/Minecraft.Client/Common/zlib/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/Minecraft.Client/Common/zlib/inftrees.c b/Minecraft.Client/Common/zlib/inftrees.c new file mode 100644 index 0000000..44d89cf --- /dev/null +++ b/Minecraft.Client/Common/zlib/inftrees.c @@ -0,0 +1,306 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/Minecraft.Client/Common/zlib/inftrees.h b/Minecraft.Client/Common/zlib/inftrees.h new file mode 100644 index 0000000..baa53a0 --- /dev/null +++ b/Minecraft.Client/Common/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/Minecraft.Client/Common/zlib/trees.c b/Minecraft.Client/Common/zlib/trees.c new file mode 100644 index 0000000..1fd7759 --- /dev/null +++ b/Minecraft.Client/Common/zlib/trees.c @@ -0,0 +1,1226 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2012 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/Minecraft.Client/Common/zlib/trees.h b/Minecraft.Client/Common/zlib/trees.h new file mode 100644 index 0000000..d35639d --- /dev/null +++ b/Minecraft.Client/Common/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/Minecraft.Client/Common/zlib/uncompr.c b/Minecraft.Client/Common/zlib/uncompr.c new file mode 100644 index 0000000..242e949 --- /dev/null +++ b/Minecraft.Client/Common/zlib/uncompr.c @@ -0,0 +1,59 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/Minecraft.Client/Common/zlib/zconf.h b/Minecraft.Client/Common/zlib/zconf.h new file mode 100644 index 0000000..9987a77 --- /dev/null +++ b/Minecraft.Client/Common/zlib/zconf.h @@ -0,0 +1,511 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/Minecraft.Client/Common/zlib/zlib.h b/Minecraft.Client/Common/zlib/zlib.h new file mode 100644 index 0000000..3e0c767 --- /dev/null +++ b/Minecraft.Client/Common/zlib/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/Minecraft.Client/Common/zlib/zutil.c b/Minecraft.Client/Common/zlib/zutil.c new file mode 100644 index 0000000..23d2ebe --- /dev/null +++ b/Minecraft.Client/Common/zlib/zutil.c @@ -0,0 +1,324 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +z_const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/Minecraft.Client/Common/zlib/zutil.h b/Minecraft.Client/Common/zlib/zutil.h new file mode 100644 index 0000000..24ab06b --- /dev/null +++ b/Minecraft.Client/Common/zlib/zutil.h @@ -0,0 +1,253 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/Minecraft.Client/CompassTexture.cpp b/Minecraft.Client/CompassTexture.cpp new file mode 100644 index 0000000..93d6a62 --- /dev/null +++ b/Minecraft.Client/CompassTexture.cpp @@ -0,0 +1,142 @@ +#include "stdafx.h" +#include "Minecraft.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "MultiplayerLocalPlayer.h" +#include "..\Minecraft.World\JavaMath.h" +#include "Texture.h" +#include "CompassTexture.h" + +CompassTexture *CompassTexture::instance = NULL; + +CompassTexture::CompassTexture() : StitchedTexture(L"compass") +{ + instance = this; + + m_dataTexture = NULL; + m_iPad = XUSER_INDEX_ANY; + + rot = rota = 0.0; +} + +CompassTexture::CompassTexture(int iPad, CompassTexture *dataTexture) : StitchedTexture(L"compass") +{ + m_dataTexture = dataTexture; + m_iPad = iPad; + + rot = rota = 0.0; +} + +void CompassTexture::cycleFrames() +{ + Minecraft *mc = Minecraft::GetInstance(); + + if (m_iPad >= 0 && m_iPad < XUSER_MAX_COUNT && mc->level != NULL && mc->localplayers[m_iPad] != NULL) + { + updateFromPosition(mc->localplayers[m_iPad]->level, mc->localplayers[m_iPad]->x, mc->localplayers[m_iPad]->z, mc->localplayers[m_iPad]->yRot, false, false); + } + else + { + frame = 1; + updateFromPosition(NULL, 0, 0, 0, false, true); + } +} + +void CompassTexture::updateFromPosition(Level *level, double x, double z, double yRot, bool noNeedle, bool instant) +{ + double rott = 0; + if (level != NULL && !noNeedle) + { + Pos *spawnPos = level->getSharedSpawnPos(); + double xa = spawnPos->x - x; + double za = spawnPos->z - z; + delete spawnPos; + yRot = (int)yRot % 360; + rott = -((yRot - 90) * PI / 180 - atan2(za, xa)); + if (!level->dimension->isNaturalDimension()) + { + rott = Math::random() * PI * 2; + } + } + + if (instant) + { + rot = rott; + } + else + { + double rotd = rott - rot; + while (rotd < -PI) + rotd += PI * 2; + while (rotd >= PI) + rotd -= PI * 2; + if (rotd < -1) rotd = -1; + if (rotd > 1) rotd = 1; + rota += rotd * 0.1; + rota *= 0.8; + rot += rota; + } + + // 4J Stu - We share data with another texture + if(m_dataTexture != NULL) + { + int newFrame = (int) (((rot / (PI * 2)) + 1.0) * m_dataTexture->frames->size()) % m_dataTexture->frames->size(); + while (newFrame < 0) + { + newFrame = (newFrame + m_dataTexture->frames->size()) % m_dataTexture->frames->size(); + } + if (newFrame != frame) + { + frame = newFrame; + m_dataTexture->source->blit(this->x, this->y, m_dataTexture->frames->at(this->frame), rotated); + } + } + else + { + int newFrame = (int) (((rot / (PI * 2)) + 1.0) * frames->size()) % frames->size(); + while (newFrame < 0) + { + newFrame = (newFrame + frames->size()) % frames->size(); + } + if (newFrame != frame) + { + frame = newFrame; + source->blit(this->x, this->y, frames->at(this->frame), rotated); + } + } +} + +int CompassTexture::getSourceWidth() const +{ + return source->getWidth(); +} + +int CompassTexture::getSourceHeight() const +{ + return source->getHeight(); +} + +int CompassTexture::getFrames() +{ + if(m_dataTexture == NULL) + { + return StitchedTexture::getFrames(); + } + else + { + return m_dataTexture->getFrames(); + } +} + +void CompassTexture::freeFrameTextures() +{ + if(m_dataTexture == NULL) + { + StitchedTexture::freeFrameTextures(); + } +} + +bool CompassTexture::hasOwnData() +{ + return m_dataTexture == NULL; +} \ No newline at end of file diff --git a/Minecraft.Client/CompassTexture.h b/Minecraft.Client/CompassTexture.h new file mode 100644 index 0000000..44c99e0 --- /dev/null +++ b/Minecraft.Client/CompassTexture.h @@ -0,0 +1,25 @@ +#pragma once +#include "StitchedTexture.h" + +class CompassTexture : public StitchedTexture +{ +private: + int m_iPad; + CompassTexture* m_dataTexture; + +public: + static CompassTexture *instance; + double rot, rota; + + CompassTexture(); + CompassTexture(int iPad, CompassTexture *dataTexture); + + void cycleFrames(); + void updateFromPosition(Level *level, double x, double z, double yRot, bool noNeedle, bool instant); + + virtual int getSourceWidth() const; + virtual int getSourceHeight() const; + virtual int getFrames(); + virtual void freeFrameTextures(); // 4J added + virtual bool hasOwnData(); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.Client/ConfirmScreen.cpp b/Minecraft.Client/ConfirmScreen.cpp new file mode 100644 index 0000000..b67ea19 --- /dev/null +++ b/Minecraft.Client/ConfirmScreen.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "ConfirmScreen.h" +#include "SmallButton.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + +ConfirmScreen::ConfirmScreen(Screen *parent, const wstring& title1, const wstring& title2, int id) +{ + this->parent = parent; + this->title1 = title1; + this->title2 = title2; + this->id = id; + + Language *language = Language::getInstance(); + yesButton = language->getElement(L"gui.yes"); + noButton = language->getElement(L"gui.no"); +} + +ConfirmScreen::ConfirmScreen(Screen *parent, const wstring& title1, const wstring& title2, const wstring& yesButton, const wstring& noButton, int id) +{ + this->parent = parent; + this->title1 = title1; + this->title2 = title2; + this->yesButton = yesButton; + this->noButton = noButton; + this->id = id; +} + +void ConfirmScreen::init() +{ + buttons.push_back(new SmallButton(0, width / 2 - 155 + 0 % 2 * 160, height / 6 + 24 * 4, yesButton)); + buttons.push_back(new SmallButton(1, width / 2 - 155 + 1 % 2 * 160, height / 6 + 24 * 4, noButton)); +} + +void ConfirmScreen::buttonClicked(Button *button) +{ + parent->confirmResult(button->id == 0, id); +} + +void ConfirmScreen::render(int xm, int ym, float a) +{ + renderBackground(); + + drawCenteredString(font, title1, width / 2, 70, 0xffffff); + drawCenteredString(font, title2, width / 2, 90, 0xffffff); + + Screen::render(xm, ym, a); + + // 4J - debug code - remove + static int count = 0; + if( count++ == 100 ) + { + count = 0; + buttonClicked(buttons[0]); + } +} \ No newline at end of file diff --git a/Minecraft.Client/ConfirmScreen.h b/Minecraft.Client/ConfirmScreen.h new file mode 100644 index 0000000..12b8569 --- /dev/null +++ b/Minecraft.Client/ConfirmScreen.h @@ -0,0 +1,23 @@ +#pragma once +#include "Screen.h" +using namespace std; + +class ConfirmScreen : public Screen +{ +private: + Screen *parent; + wstring title1; + wstring title2; + wstring yesButton; + wstring noButton; + int id; + +public: + ConfirmScreen(Screen *parent, const wstring& title1, const wstring& title2, int id); + ConfirmScreen(Screen *parent, const wstring& title1, const wstring& title2, const wstring& yesButton, const wstring& noButton, int id); + virtual void init(); +protected: + virtual void buttonClicked(Button *button); +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/ConnectScreen.cpp b/Minecraft.Client/ConnectScreen.cpp new file mode 100644 index 0000000..2cf005b --- /dev/null +++ b/Minecraft.Client/ConnectScreen.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" +#include "ConnectScreen.h" +#include "ClientConnection.h" +#include "TitleScreen.h" +#include "Button.h" +#include "Minecraft.h" +#include "User.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + + +ConnectScreen::ConnectScreen(Minecraft *minecraft, const wstring& ip, int port) +{ + aborted = false; +// System.out.println("Connecting to " + ip + ", " + port); + minecraft->setLevel(NULL); +#if 1 + // 4J - removed from separate thread, but need to investigate what we actually need here + connection = new ClientConnection(minecraft, ip, port); + if (aborted) return; + connection->send( shared_ptr( new PreLoginPacket(minecraft->user->name) ) ); +#else + + new Thread() { + public void run() { + + try { + connection = new ClientConnection(minecraft, ip, port); + if (aborted) return; + connection.send(new PreLoginPacket(minecraft.user.name)); + } catch (UnknownHostException e) { + if (aborted) return; + minecraft.setScreen(new DisconnectedScreen("connect.failed", "disconnect.genericReason", "Unknown host '" + ip + "'")); + } catch (ConnectException e) { + if (aborted) return; + minecraft.setScreen(new DisconnectedScreen("connect.failed", "disconnect.genericReason", e.getMessage())); + } catch (Exception e) { + if (aborted) return; + e.printStackTrace(); + minecraft.setScreen(new DisconnectedScreen("connect.failed", "disconnect.genericReason", e.toString())); + } + } + }.start(); +#endif +} + +void ConnectScreen::tick() +{ + if (connection != NULL) + { + connection->tick(); + } +} + +void ConnectScreen::keyPressed(char eventCharacter, int eventKey) +{ +} + +void ConnectScreen::init() +{ + Language *language = Language::getInstance(); + + buttons.clear(); + buttons.push_back(new Button(0, width / 2 - 100, height / 4 + 24 * 5 + 12, language->getElement(L"gui.cancel"))); + +} + +void ConnectScreen::buttonClicked(Button *button) +{ + if (button->id == 0) + { + aborted = true; + if (connection != NULL) connection->close(); + minecraft->setScreen(new TitleScreen()); + } +} + +void ConnectScreen::render(int xm, int ym, float a) +{ + renderBackground(); + + Language *language = Language::getInstance(); + + if (connection == NULL) + { + drawCenteredString(font, language->getElement(L"connect.connecting"), width / 2, height / 2 - 50, 0xffffff); + drawCenteredString(font, L"", width / 2, height / 2 - 10, 0xffffff); + } + else + { + drawCenteredString(font, language->getElement(L"connect.authorizing"), width / 2, height / 2 - 50, 0xffffff); + drawCenteredString(font, connection->message, width / 2, height / 2 - 10, 0xffffff); + } + + Screen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/ConnectScreen.h b/Minecraft.Client/ConnectScreen.h new file mode 100644 index 0000000..07e65cb --- /dev/null +++ b/Minecraft.Client/ConnectScreen.h @@ -0,0 +1,24 @@ +#pragma once +#include "Screen.h" +class ClientConnection; +class Minecraft; + +using namespace std; + +class ConnectScreen : public Screen +{ +private: + ClientConnection *connection; + bool aborted; +public: + ConnectScreen(Minecraft *minecraft, const wstring& ip, int port); + virtual void tick(); +protected: + virtual void keyPressed(char eventCharacter, int eventKey); +public: + virtual void init(); +protected: + virtual void buttonClicked(Button *button); +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/ConsoleInput.cpp b/Minecraft.Client/ConsoleInput.cpp new file mode 100644 index 0000000..7fd6c49 --- /dev/null +++ b/Minecraft.Client/ConsoleInput.cpp @@ -0,0 +1,8 @@ +#include "stdafx.h" +#include "ConsoleInput.h" + +ConsoleInput::ConsoleInput(const wstring& msg, ConsoleInputSource *source) +{ + this->msg = msg; + this->source = source; +} \ No newline at end of file diff --git a/Minecraft.Client/ConsoleInput.h b/Minecraft.Client/ConsoleInput.h new file mode 100644 index 0000000..1206b24 --- /dev/null +++ b/Minecraft.Client/ConsoleInput.h @@ -0,0 +1,12 @@ +#pragma once +#include "ConsoleInputSource.h" +using namespace std; + +class ConsoleInput +{ +public: + wstring msg; + ConsoleInputSource *source; + + ConsoleInput(const wstring& msg, ConsoleInputSource *source); +}; \ No newline at end of file diff --git a/Minecraft.Client/ConsoleInputSource.h b/Minecraft.Client/ConsoleInputSource.h new file mode 100644 index 0000000..ab2a258 --- /dev/null +++ b/Minecraft.Client/ConsoleInputSource.h @@ -0,0 +1,9 @@ +#pragma once + +class ConsoleInputSource +{ +public: + virtual void info(const wstring& string) = 0; + virtual void warn(const wstring& string) = 0; + virtual wstring getConsoleName() = 0; +}; diff --git a/Minecraft.Client/ContainerScreen.cpp b/Minecraft.Client/ContainerScreen.cpp new file mode 100644 index 0000000..17b4540 --- /dev/null +++ b/Minecraft.Client/ContainerScreen.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "ContainerScreen.h" +#include "Textures.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" + +ContainerScreen::ContainerScreen(shared_ptr inventory, shared_ptr container) : AbstractContainerScreen(new ContainerMenu(inventory, container)) +{ + this->inventory = inventory; + this->container = container; + this->passEvents = false; + + int defaultHeight = 222; + int noRowHeight = defaultHeight - 6 * 18; + containerRows = container->getContainerSize() / 9; + + imageHeight = noRowHeight + containerRows * 18; + +} + +void ContainerScreen::renderLabels() +{ +#if 0 + font->draw(container->getName(), 8, 2 + 2 + 2, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); +#endif +} + +void ContainerScreen::renderBg(float a) +{ + // 4J Unused +#if 0 + int tex = minecraft->textures->loadTexture(L"/gui/container.png"); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, containerRows * 18 + 17); + this->blit(xo, yo + containerRows * 18 + 17, 0, 222 - 96, imageWidth, 96); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/ContainerScreen.h b/Minecraft.Client/ContainerScreen.h new file mode 100644 index 0000000..38806c1 --- /dev/null +++ b/Minecraft.Client/ContainerScreen.h @@ -0,0 +1,19 @@ +#pragma once +#include "AbstractContainerScreen.h" +class Container; + +class ContainerScreen : public AbstractContainerScreen +{ +private: + shared_ptr inventory; + shared_ptr container; + + int containerRows; + +public: + ContainerScreen(shared_ptrinventory, shared_ptrcontainer); + +protected: + virtual void renderLabels(); + virtual void renderBg(float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/ControlsScreen.cpp b/Minecraft.Client/ControlsScreen.cpp new file mode 100644 index 0000000..487dbb1 --- /dev/null +++ b/Minecraft.Client/ControlsScreen.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "ControlsScreen.h" +#include "Options.h" +#include "SmallButton.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + +ControlsScreen::ControlsScreen(Screen *lastScreen, Options *options) +{ + // 4J - added initialisers + title == L"Controls"; + selectedKey = -1; + + this->lastScreen = lastScreen; + this->options = options; +} + +int ControlsScreen::getLeftScreenPosition() +{ + return width / 2 - 155; +} + +void ControlsScreen::init() +{ + Language *language = Language::getInstance(); + + int leftPos = getLeftScreenPosition(); + for (int i = 0; i < Options::keyMappings_length; i++) + { + buttons.push_back(new SmallButton(i, leftPos + i % 2 * ROW_WIDTH, height / 6 + 24 * (i >> 1), BUTTON_WIDTH, 20, options->getKeyMessage(i))); + } + + buttons.push_back(new Button(200, width / 2 - 100, height / 6 + 24 * 7, language->getElement(L"gui.done"))); + title = language->getElement(L"controls.title"); + +} + +void ControlsScreen::buttonClicked(Button *button) +{ + for (int i = 0; i < Options::keyMappings_length; i++) + { + buttons[i]->msg = options->getKeyMessage(i); + } + if (button->id == 200) + { + minecraft->setScreen(lastScreen); + } + else + { + selectedKey = button->id; + button->msg = L"> " + options->getKeyMessage(button->id) + L" <"; + } +} + +void ControlsScreen::keyPressed(wchar_t eventCharacter, int eventKey) +{ + if (selectedKey >= 0) + { + options->setKey(selectedKey, eventKey); + buttons[selectedKey]->msg = options->getKeyMessage(selectedKey); + selectedKey = -1; + } + else + { + Screen::keyPressed(eventCharacter, eventKey); + } +} + +void ControlsScreen::render(int xm, int ym, float a) +{ + renderBackground(); + drawCenteredString(font, title, width / 2, 20, 0xffffff); + + int leftPos = getLeftScreenPosition(); + for (int i = 0; i < Options::keyMappings_length; i++) + { + drawString(font, options->getKeyDescription(i), leftPos + i % 2 * ROW_WIDTH + BUTTON_WIDTH + 6, height / 6 + 24 * (i >> 1) + 7, 0xffffffff); + } + + Screen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/ControlsScreen.h b/Minecraft.Client/ControlsScreen.h new file mode 100644 index 0000000..b70bc4e --- /dev/null +++ b/Minecraft.Client/ControlsScreen.h @@ -0,0 +1,31 @@ +#pragma once +#include "Screen.h" +using namespace std; +class Options; + +class ControlsScreen : public Screen +{ +private: + Screen *lastScreen; +protected: + wstring title; +private: + Options *options; + + int selectedKey; + + static const int BUTTON_WIDTH = 70; + static const int ROW_WIDTH = 160; + +public: + ControlsScreen(Screen *lastScreen, Options *options); +private: + int getLeftScreenPosition(); +public: + void init(); +protected: + void buttonClicked(Button *button); + void keyPressed(wchar_t eventCharacter, int eventKey); +public: + void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/CowModel.cpp b/Minecraft.Client/CowModel.cpp new file mode 100644 index 0000000..bf16ade --- /dev/null +++ b/Minecraft.Client/CowModel.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "CowModel.h" +#include "ModelPart.h" + +CowModel::CowModel() : QuadrupedModel(12,0) +{ + head = new ModelPart(this, 0, 0); + head->addBox(-4, -4, -6, 8, 8, 6, 0); // Head + head->setPos(0, 12 - 6 - 2, -8); + head->texOffs(22, 0)->addBox(-5, -5, -4, 1, 3, 1, 0); // Horn1 + head->texOffs(22, 0)->addBox(+4, -5, -4, 1, 3, 1, 0); // Horn1 + + body = new ModelPart(this, 18, 4); + body->addBox(-6, -10, -7, 12, 18, 10, 0); // Body + body->setPos(0, 11 + 6 - 12, 2); + body->texOffs(52, 0)->addBox(-2, 2, -8, 4, 6, 1); + + leg0->x -= 1; + leg1->x += 1; + leg0->z += 0; + leg1->z += 0; + leg2->x -= 1; + leg3->x += 1; + leg2->z -= 1; + leg3->z -= 1; + + this->zHeadOffs += 2; + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + head->compile(1.0f/16.0f); + body->compile(1.0f/16.0f); +} diff --git a/Minecraft.Client/CowModel.h b/Minecraft.Client/CowModel.h new file mode 100644 index 0000000..617bb94 --- /dev/null +++ b/Minecraft.Client/CowModel.h @@ -0,0 +1,10 @@ +#pragma once +#include "QuadrupedModel.h" + +class CowModel : public QuadrupedModel +{ +public: + CowModel(); +// virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +// virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale); +}; diff --git a/Minecraft.Client/CowRenderer.cpp b/Minecraft.Client/CowRenderer.cpp new file mode 100644 index 0000000..c4eaf26 --- /dev/null +++ b/Minecraft.Client/CowRenderer.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "CowRenderer.h" + +CowRenderer::CowRenderer(Model *model, float shadow) : MobRenderer(model, shadow) +{ +} + +void CowRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + MobRenderer::render(_mob, x, y, z, rot, a); +} \ No newline at end of file diff --git a/Minecraft.Client/CowRenderer.h b/Minecraft.Client/CowRenderer.h new file mode 100644 index 0000000..e99e1c1 --- /dev/null +++ b/Minecraft.Client/CowRenderer.h @@ -0,0 +1,9 @@ +#pragma once +#include "MobRenderer.h" + +class CowRenderer : public MobRenderer +{ +public: + CowRenderer(Model *model, float shadow); + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/CraftingScreen.cpp b/Minecraft.Client/CraftingScreen.cpp new file mode 100644 index 0000000..c28df18 --- /dev/null +++ b/Minecraft.Client/CraftingScreen.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "CraftingScreen.h" +#include "Textures.h" +#include "MultiplayerLocalPlayer.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" + +CraftingScreen::CraftingScreen(shared_ptr inventory, Level *level, int x, int y, int z) : AbstractContainerScreen(new CraftingMenu(inventory, level, x, y, z)) +{ +} + +void CraftingScreen::removed() +{ + AbstractContainerScreen::removed(); + menu->removed(dynamic_pointer_cast(minecraft->player)); +} + +void CraftingScreen::renderLabels() +{ + font->draw(L"Crafting", 8 + 16 + 4, 2 + 2 + 2, 0x404040); + font->draw(L"Inventory", 8, imageHeight - 96 + 2, 0x404040); +} + +void CraftingScreen::renderBg(float a) +{ + // 4J Unused +#if 0 + int tex = minecraft->textures->loadTexture(L"/gui/crafting.png"); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, imageHeight); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/CraftingScreen.h b/Minecraft.Client/CraftingScreen.h new file mode 100644 index 0000000..2de1681 --- /dev/null +++ b/Minecraft.Client/CraftingScreen.h @@ -0,0 +1,14 @@ +#pragma once +#include "AbstractContainerScreen.h" +class Inventory; +class Level; + +class CraftingScreen : public AbstractContainerScreen +{ +public: + CraftingScreen(shared_ptr inventory, Level *level, int x, int y, int z); + virtual void removed(); +protected: + virtual void renderLabels(); + virtual void renderBg(float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/CreateWorldScreen.cpp b/Minecraft.Client/CreateWorldScreen.cpp new file mode 100644 index 0000000..b9856c7 --- /dev/null +++ b/Minecraft.Client/CreateWorldScreen.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "CreateWorldScreen.h" +#include "EditBox.h" +#include "Button.h" +#include "SurvivalMode.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\Minecraft.World\SharedConstants.h" +#include "..\Minecraft.World\Random.h" + +CreateWorldScreen::CreateWorldScreen(Screen *lastScreen) +{ + done = false; // 4J added + this->lastScreen = lastScreen; +} + +void CreateWorldScreen::tick() +{ + nameEdit->tick(); + seedEdit->tick(); + + // 4J - debug code - to be removed + static int count = 0; + if(count++ == 100 ) buttonClicked(buttons[0]); +} + +void CreateWorldScreen::init() +{ + Language *language = Language::getInstance(); + + Keyboard::enableRepeatEvents(true); + buttons.clear(); + buttons.push_back(new Button(0, width / 2 - 100, height / 4 + 24 * 4 + 12, language->getElement(L"selectWorld.create"))); + buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + 12, language->getElement(L"gui.cancel"))); + + nameEdit = new EditBox(this, font, width / 2 - 100, 60, 200, 20, language->getElement(L"testWorld")); // 4J - test - should be L"selectWorld.newWorld" + nameEdit->inFocus = true; + nameEdit->setMaxLength(32); + + seedEdit = new EditBox(this, font, width / 2 - 100, 116, 200, 20, L""); + + updateResultFolder(); +} + +void CreateWorldScreen::updateResultFolder() +{ + resultFolder = trimString(nameEdit->getValue()); + + for( int i = 0; i < SharedConstants::ILLEGAL_FILE_CHARACTERS_LENGTH; i++ ) + { + size_t pos; + while( (pos = resultFolder.find(SharedConstants::ILLEGAL_FILE_CHARACTERS[i])) != wstring::npos) + { + resultFolder[pos] = L'_'; + } + } + + if (resultFolder.length()==0) + { + resultFolder = L"World"; + } + resultFolder = CreateWorldScreen::findAvailableFolderName(minecraft->getLevelSource(), resultFolder); + +} + +wstring CreateWorldScreen::findAvailableFolderName(LevelStorageSource *levelSource, const wstring& folder) +{ + wstring folder2 = folder; // 4J - copy input as it is const + +#if 0 + while (levelSource->getDataTagFor(folder2) != NULL) + { + folder2 = folder2 + L"-"; + } +#endif + return folder2; +} + +void CreateWorldScreen::removed() +{ + Keyboard::enableRepeatEvents(false); +} + +void CreateWorldScreen::buttonClicked(Button *button) +{ + if (!button->active) return; + if (button->id == 1) + { + minecraft->setScreen(lastScreen); + } + else if (button->id == 0) + { + // note: code copied from SelectWorldScreen + minecraft->setScreen(NULL); + if (done) return; + done = true; + + __int64 seedValue = (new Random())->nextLong(); + wstring seedString = seedEdit->getValue(); + + if (seedString.length() != 0) + { + // try to convert it to a long first +// try { // 4J - removed try/catch + __int64 value = _fromString<__int64>(seedString); + if (value != 0) + { + seedValue = value; + } + // } catch (NumberFormatException e) { + // // not a number, fetch hash value + // seedValue = seedString.hashCode(); + // } + } + +// 4J Stu - This screen is not used, so removing this to stop the build failing +#if 0 + minecraft->gameMode = new SurvivalMode(minecraft); + minecraft->selectLevel(resultFolder, nameEdit->getValue(), seedValue); + minecraft->setScreen(NULL); +#endif + } + +} + +void CreateWorldScreen::keyPressed(wchar_t ch, int eventKey) +{ + if (nameEdit->inFocus) nameEdit->keyPressed(ch, eventKey); + else seedEdit->keyPressed(ch, eventKey); + + if (ch == 13) + { + buttonClicked(buttons[0]); + } + buttons[0]->active = nameEdit->getValue().length() > 0; + + updateResultFolder(); +} + +void CreateWorldScreen::mouseClicked(int x, int y, int buttonNum) +{ + Screen::mouseClicked(x, y, buttonNum); + + nameEdit->mouseClicked(x, y, buttonNum); + seedEdit->mouseClicked(x, y, buttonNum); +} + +void CreateWorldScreen::render(int xm, int ym, float a) +{ + Language *language = Language::getInstance(); + + // fill(0, 0, width, height, 0x40000000); + renderBackground(); + + drawCenteredString(font, language->getElement(L"selectWorld.create"), width / 2, height / 4 - 60 + 20, 0xffffff); + drawString(font, language->getElement(L"selectWorld.enterName"), width / 2 - 100, 47, 0xa0a0a0); + drawString(font, language->getElement(L"selectWorld.resultFolder") + L" " + resultFolder, width / 2 - 100, 85, 0xa0a0a0); + + drawString(font, language->getElement(L"selectWorld.enterSeed"), width / 2 - 100, 104, 0xa0a0a0); + drawString(font, language->getElement(L"selectWorld.seedInfo"), width / 2 - 100, 140, 0xa0a0a0); + + nameEdit->render(); + seedEdit->render(); + + Screen::render(xm, ym, a); + +} + +void CreateWorldScreen::tabPressed() +{ + if (nameEdit->inFocus) + { + nameEdit->focus(false); + seedEdit->focus(true); + } + else + { + nameEdit->focus(true); + seedEdit->focus(false); + } +} \ No newline at end of file diff --git a/Minecraft.Client/CreateWorldScreen.h b/Minecraft.Client/CreateWorldScreen.h new file mode 100644 index 0000000..7808178 --- /dev/null +++ b/Minecraft.Client/CreateWorldScreen.h @@ -0,0 +1,32 @@ +#pragma once +#include "Screen.h" +class EditBox; +class LevelStorageSource; +using namespace std; + +class CreateWorldScreen : public Screen +{ +private: + Screen *lastScreen; + EditBox *nameEdit; + EditBox *seedEdit; + wstring resultFolder; + bool done; + +public: + CreateWorldScreen(Screen *lastScreen); + virtual void tick(); + virtual void init(); +private: + void updateResultFolder(); +public: + static wstring findAvailableFolderName(LevelStorageSource *levelSource, const wstring& folder); + virtual void removed(); +protected: + virtual void buttonClicked(Button *button); + virtual void keyPressed(wchar_t ch, int eventKey); + virtual void mouseClicked(int x, int y, int buttonNum); +public: + virtual void render(int xm, int ym, float a); + virtual void tabPressed(); +}; \ No newline at end of file diff --git a/Minecraft.Client/CreativeMode.cpp b/Minecraft.Client/CreativeMode.cpp new file mode 100644 index 0000000..48342eb --- /dev/null +++ b/Minecraft.Client/CreativeMode.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "CreativeMode.h" +#include "User.h" +#include "LocalPlayer.h" +#include "..\Minecraft.World\\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" + +CreativeMode::CreativeMode(Minecraft *minecraft) : GameMode(minecraft) +{ + destroyDelay = 0; + instaBuild = true; +} + +void CreativeMode::init() +{ + // initPlayer(); +} + +void CreativeMode::enableCreativeForPlayer(shared_ptr player) +{ + // please check ServerPlayerGameMode.java if you change these + player->abilities.mayfly = true; + player->abilities.instabuild = true; + player->abilities.invulnerable = true; +} + +void CreativeMode::disableCreativeForPlayer(shared_ptr player) +{ + player->abilities.mayfly = false; + player->abilities.flying = false; + player->abilities.instabuild = false; + player->abilities.invulnerable = false; +} + +void CreativeMode::adjustPlayer(shared_ptr player) +{ + enableCreativeForPlayer(player); + + for (int i = 0; i < 9; i++) + { + if (player->inventory->items[i] == NULL) + { + player->inventory->items[i] = shared_ptr( new ItemInstance(User::allowedTiles[i]) ); + } + else + { + // 4J-PB - this line is commented out in 1.0.1 + //player->inventory->items[i]->count = 1; + } + } +} + +void CreativeMode::creativeDestroyBlock(Minecraft *minecraft, GameMode *gameMode, int x, int y, int z, int face) +{ + if(!minecraft->level->extinguishFire(minecraft->player, x, y, z, face)) + { + gameMode->destroyBlock(x, y, z, face); + } +} + +bool CreativeMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, bool bTestUseOnOnly, bool *pbUsedItem) +{ + int t = level->getTile(x, y, z); + if (t > 0) + { + if (Tile::tiles[t]->use(level, x, y, z, player)) return true; + } + if (item == NULL) return false; + int aux = item->getAuxValue(); + int count = item->count; + bool success = item->useOn(player, level, x, y, z, face); + item->setAuxValue(aux); + item->count = count; + return success; +} + +void CreativeMode::startDestroyBlock(int x, int y, int z, int face) +{ + creativeDestroyBlock(minecraft, this, x, y, z, face); + destroyDelay = 5; +} + +void CreativeMode::continueDestroyBlock(int x, int y, int z, int face) +{ + destroyDelay--; + if (destroyDelay <= 0) + { + destroyDelay = 5; + creativeDestroyBlock(minecraft, this, x, y, z, face); + } +} + +void CreativeMode::stopDestroyBlock() +{ +} + +bool CreativeMode::canHurtPlayer() +{ + return false; +} + +void CreativeMode::initLevel(Level *level) +{ + GameMode::initLevel(level); +} + +float CreativeMode::getPickRange() +{ + return 5.0f; +} + +bool CreativeMode::hasMissTime() +{ + return false; +} + +bool CreativeMode::hasInfiniteItems() +{ + return true; +} + +bool CreativeMode::hasFarPickRange() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.Client/CreativeMode.h b/Minecraft.Client/CreativeMode.h new file mode 100644 index 0000000..10b27a5 --- /dev/null +++ b/Minecraft.Client/CreativeMode.h @@ -0,0 +1,26 @@ +#pragma once +#include "GameMode.h" + +class CreativeMode : public GameMode +{ +private: + int destroyDelay; + +public: + CreativeMode(Minecraft *minecraft); + virtual void init(); + static void enableCreativeForPlayer(shared_ptr player); + static void disableCreativeForPlayer(shared_ptr player); + virtual void adjustPlayer(shared_ptr player); + static void creativeDestroyBlock(Minecraft *minecraft, GameMode *gameMode, int x, int y, int z, int face); + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, bool bTestUseOnOnly=false, bool *pbUsedItem = NULL); + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual void continueDestroyBlock(int x, int y, int z, int face); + virtual void stopDestroyBlock(); + virtual bool canHurtPlayer(); + virtual void initLevel(Level *level); + virtual float getPickRange(); + virtual bool hasMissTime(); + virtual bool hasInfiniteItems(); + virtual bool hasFarPickRange(); +}; \ No newline at end of file diff --git a/Minecraft.Client/CreeperModel.cpp b/Minecraft.Client/CreeperModel.cpp new file mode 100644 index 0000000..51237ce --- /dev/null +++ b/Minecraft.Client/CreeperModel.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "CreeperModel.h" +#include "ModelPart.h" + +// 4J - added +void CreeperModel::_init(float g) +{ + int yo = 4; + + head = new ModelPart(this, 0, 0); + head->addBox(-4, - 8, -4, 8, 8, 8, g); // Head + head->setPos(0, (float)(yo), 0); + + hair = new ModelPart(this, 32, 0); + hair->addBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Head + hair->setPos(0, (float)(yo), 0); + + body = new ModelPart(this, 16, 16); + body->addBox(-4, 0, -2, 8, 12, 4, g); // Body + body->setPos(0, (float)(yo), 0); + + leg0 = new ModelPart(this, 0, 16); + leg0->addBox(-2, 0, -2, 4, 6, 4, g); // Leg0 + leg0->setPos(-2, (float)(12 + yo), 4); + + leg1 = new ModelPart(this, 0, 16); + leg1->addBox(-2, 0, -2, 4, 6, 4, g); // Leg1 + leg1->setPos(2, (float)(12 + yo), 4); + + leg2 = new ModelPart(this, 0, 16); + leg2->addBox(-2, 0, -2, 4, 6, 4, g); // Leg2 + leg2->setPos(-2, (float)(12 + yo), -4); + + leg3 = new ModelPart(this, 0, 16); + leg3->addBox(-2, 0, -2, 4, 6, 4, g); // Leg3 + leg3->setPos(2, (float)(12 + yo), -4); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + head->compile(1.0f/16.0f); + hair->compile(1.0f/16.0f); + body->compile(1.0f/16.0f); + leg0->compile(1.0f/16.0f); + leg1->compile(1.0f/16.0f); + leg2->compile(1.0f/16.0f); + leg3->compile(1.0f/16.0f); +} + +CreeperModel::CreeperModel() : Model() +{ + _init(0); +} + +CreeperModel::CreeperModel(float g) : Model() +{ + _init(g); +} + +void CreeperModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + head->render(scale, usecompiled); + body->render(scale, usecompiled); + leg0->render(scale, usecompiled); + leg1->render(scale, usecompiled); + leg2->render(scale, usecompiled); + leg3->render(scale, usecompiled); +} + +void CreeperModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + head->yRot = yRot / (float) (180 / PI); + head->xRot = xRot / (float) (180 / PI); + + leg0->xRot = (Mth::cos(time * 0.6662f) * 1.4f) * r; + leg1->xRot = (Mth::cos(time * 0.6662f + PI) * 1.4f) * r; + leg2->xRot = (Mth::cos(time * 0.6662f + PI) * 1.4f) * r; + leg3->xRot = (Mth::cos(time * 0.6662f) * 1.4f) * r; +} \ No newline at end of file diff --git a/Minecraft.Client/CreeperModel.h b/Minecraft.Client/CreeperModel.h new file mode 100644 index 0000000..06231d0 --- /dev/null +++ b/Minecraft.Client/CreeperModel.h @@ -0,0 +1,14 @@ +#pragma once +#include "Model.h" + +class CreeperModel : public Model +{ +public: + ModelPart *head, *hair, *body, *leg0, *leg1, *leg2, *leg3; + + void _init(float g); // 4J added + CreeperModel(); + CreeperModel(float g); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); +}; \ No newline at end of file diff --git a/Minecraft.Client/CreeperRenderer.cpp b/Minecraft.Client/CreeperRenderer.cpp new file mode 100644 index 0000000..483d14e --- /dev/null +++ b/Minecraft.Client/CreeperRenderer.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "CreeperRenderer.h" +#include "CreeperModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\Mth.h" + +CreeperRenderer::CreeperRenderer() : MobRenderer( new CreeperModel(), 0.5f ) +{ + armorModel = new CreeperModel(2); +} + +void CreeperRenderer::scale(shared_ptr mob, float a) +{ + shared_ptr creeper = dynamic_pointer_cast(mob); + + float g = creeper->getSwelling(a); + + float wobble = 1.0f + Mth::sin(g * 100) * g * 0.01f; + if (g < 0) g = 0; + if (g > 1) g = 1; + g = g * g; + g = g * g; + float s = (1.0f + g * 0.4f) * wobble; + float hs = (1.0f + g * 0.1f) / wobble; + glScalef(s, hs, s); +} + +int CreeperRenderer::getOverlayColor(shared_ptr mob, float br, float a) +{ + shared_ptr creeper = dynamic_pointer_cast(mob); + + float step = creeper->getSwelling(a); + + if ((int) (step * 10) % 2 == 0) return 0; + + int _a = (int) (step * 0.2f * 255) + 25; // 4J - added 25 here as our entities are rendered with alpha test still enabled, and so anything less is invisible + if (_a < 0) _a = 0; + if (_a > 255) _a = 255; + + int r = 255; + int g = 255; + int b = 255; + + return (_a << 24) | (r << 16) | (g << 8) | b; +} + +int CreeperRenderer::prepareArmor(shared_ptr _mob, int layer, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + if (mob->isPowered()) + { + if (mob->isInvisible()) glDepthMask(false); + else glDepthMask(true); + + if (layer == 1) + { + float time = mob->tickCount + a; + bindTexture(TN_POWERED_CREEPER);// was L"/armor/power.png"); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + float uo = time * 0.01f; + float vo = time * 0.01f; + glTranslatef(uo, vo, 0); + setArmor(armorModel); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_BLEND); + float br = 0.5f; + glColor4f(br, br, br, 1); + glDisable(GL_LIGHTING); + glBlendFunc(GL_ONE, GL_ONE); + return 1; + } + if (layer == 2) + { + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); + } + } + return -1; + +} + +int CreeperRenderer::prepareArmorOverlay(shared_ptr mob, int layer, float a) +{ + return -1; +} \ No newline at end of file diff --git a/Minecraft.Client/CreeperRenderer.h b/Minecraft.Client/CreeperRenderer.h new file mode 100644 index 0000000..4d0fac1 --- /dev/null +++ b/Minecraft.Client/CreeperRenderer.h @@ -0,0 +1,16 @@ +#pragma once +#include "MobRenderer.h" + +class CreeperRenderer: public MobRenderer +{ +private: + Model *armorModel; + +public: + CreeperRenderer(); +protected: + virtual void scale(shared_ptr _mob, float a); + virtual int getOverlayColor(shared_ptr mob, float br, float a); + virtual int prepareArmor(shared_ptr mob, int layer, float a); + virtual int prepareArmorOverlay(shared_ptr _mob, int layer, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/CritParticle.cpp b/Minecraft.Client/CritParticle.cpp new file mode 100644 index 0000000..6c71b02 --- /dev/null +++ b/Minecraft.Client/CritParticle.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "CritParticle.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" + +void CritParticle::_init(Level *level, shared_ptr entity, ePARTICLE_TYPE type) +{ + life = 0; + this->entity = entity; + lifeTime = 3; + particleName = type; + // 4J-PB - can't use a shared_from_this in the constructor + //tick(); +} + +CritParticle::CritParticle(Level *level, shared_ptr entity) : Particle(level, entity->x, entity->bb->y0 + entity->bbHeight / 2, entity->z, entity->xd, entity->yd, entity->zd) +{ + _init(level,entity,eParticleType_crit); +} + +CritParticle::CritParticle(Level *level, shared_ptr entity, ePARTICLE_TYPE type) : Particle(level, entity->x, entity->bb->y0 + entity->bbHeight / 2, entity->z, entity->xd, entity->yd, entity->zd) +{ + _init(level, entity, type); +} + +// 4J - Added this so that we can use some shared_ptr functions that were needed in the ctor +void CritParticle::CritParticlePostConstructor(void) +{ + tick(); +} + +void CritParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ +} + +void CritParticle::tick() +{ + for (int i=0; i<16; i++) + { + double xa = random->nextFloat()*2-1; + double ya = random->nextFloat()*2-1; + double za = random->nextFloat()*2-1; + if (xa*xa+ya*ya+za*za>1) continue; + double x = entity->x+xa*entity->bbWidth/4; + double y = entity->bb->y0+entity->bbHeight/2+ya*entity->bbHeight/4; + double z = entity->z+za*entity->bbWidth/4; + level->addParticle(particleName, x, y, z, xa, ya+0.2, za); + } + life++; + if (life >= lifeTime) + { + remove(); + } +} + +int CritParticle::getParticleTexture() +{ + return ParticleEngine::ENTITY_PARTICLE_TEXTURE; +} \ No newline at end of file diff --git a/Minecraft.Client/CritParticle.h b/Minecraft.Client/CritParticle.h new file mode 100644 index 0000000..23f3033 --- /dev/null +++ b/Minecraft.Client/CritParticle.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Particle.h" + +class Entity; + +class CritParticle : public Particle +{ +private: + shared_ptr entity; + int life; + int lifeTime; + ePARTICLE_TYPE particleName; + + void _init(Level *level, shared_ptr entity, ePARTICLE_TYPE type); + +public: + virtual eINSTANCEOF GetType() { return eType_CRITPARTICLE; } + CritParticle(Level *level, shared_ptr entity); + CritParticle(Level *level, shared_ptr entity, ePARTICLE_TYPE type); + void CritParticlePostConstructor(void); + void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + void tick(); + int getParticleTexture(); +}; \ No newline at end of file diff --git a/Minecraft.Client/CritParticle2.cpp b/Minecraft.Client/CritParticle2.cpp new file mode 100644 index 0000000..363e8f0 --- /dev/null +++ b/Minecraft.Client/CritParticle2.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "CritParticle2.h" +#include "..\Minecraft.World\JavaMath.h" + +void CritParticle2::_init(double xa, double ya, double za, float scale) +{ + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd += xa * 0.4; + yd += ya * 0.4; + zd += za * 0.4; + + rCol = gCol = bCol = (float) (Math::random() * 0.3f + 0.6f); + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = (int) (6 / (Math::random() * 0.8 + 0.6)); + lifetime *= scale; + noPhysics = false; + + setMiscTex(16 * 4 + 1); + // 4J-PB - can't use a shared_from_this in the constructor + //tick(); + m_bAgeUniformly=false; // 4J added +} + +CritParticle2::CritParticle2(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, 0, 0, 0) +{ + _init(xa,ya,za,1); +} + +CritParticle2::CritParticle2(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) : Particle(level, x, y, z, 0, 0, 0) +{ + _init(xa,ya,za,scale); +} + +void CritParticle2::CritParticle2PostConstructor(void) +{ + tick(); +} + +void CritParticle2::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void CritParticle2::SetAgeUniformly() +{ + m_bAgeUniformly=true; +} + +void CritParticle2::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + gCol *= 0.96; + bCol *= 0.9; + + if(m_bAgeUniformly) + { + rCol *= 0.99; + gCol *= 0.99; + bCol *= 0.99; + } + else + { + gCol *= 0.96; + bCol *= 0.9; + } + + xd *= 0.70f; + yd *= 0.70f; + zd *= 0.70f; + yd-=0.02f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } +} diff --git a/Minecraft.Client/CritParticle2.h b/Minecraft.Client/CritParticle2.h new file mode 100644 index 0000000..3febb0f --- /dev/null +++ b/Minecraft.Client/CritParticle2.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Particle.h" + +class CritParticle2 : public Particle +{ +public: + float oSize; + bool m_bAgeUniformly; // 4J added for Halo texture pack + + virtual eINSTANCEOF GetType() { return eType_CRITPARTICLE2; } + void _init(double xa, double ya, double za, float scale); + CritParticle2(Level *level, double x, double y, double z, double xa, double ya, double za); + CritParticle2(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); + void CritParticle2PostConstructor(void); + void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + void tick(); + void SetAgeUniformly(); +}; diff --git a/Minecraft.Client/Cube.cpp b/Minecraft.Client/Cube.cpp new file mode 100644 index 0000000..813a695 --- /dev/null +++ b/Minecraft.Client/Cube.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "Model.h" +#include "ModelPart.h" +#include "Cube.h" + + + +// 4J - added - helper function to set up vertex arrays +VertexArray Cube::VertexArray4(Vertex *v0, Vertex *v1, Vertex *v2, Vertex *v3) +{ + VertexArray ret = VertexArray(4); + ret[0] = v0; + ret[1] = v1; + ret[2] = v2; + ret[3] = v3; + + return ret; +} + +//void Cube::addBox(float x0, float y0, float z0, int w, int h, int d, float g) +Cube::Cube(ModelPart *modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g, int faceMask /* = 63 */, bool bFlipPoly3UVs) : // 4J - added faceMask, added bFlipPoly3UVs to reverse the uvs back so player skins display right + x0(x0), + y0(y0), + z0(z0), + x1(x0 + w), + y1(y0 + h), + z1(z0 + d) +{ +// this->x0 = x0; +// this->y0 = y0; +// this->z0 = z0; +// this->x1 = x0 + w; +// this->y1 = y0 + h; +// this->z1 = z0 + d; + + vertices = VertexArray(8); + polygons = PolygonArray(6); + + float x1 = x0 + w; + float y1 = y0 + h; + float z1 = z0 + d; + + x0 -= g; + y0 -= g; + z0 -= g; + x1 += g; + y1 += g; + z1 += g; + + if (modelPart->bMirror) + { + float tmp = x1; + x1 = x0; + x0 = tmp; + } + + Vertex *u0 = new Vertex(x0, y0, z0, 0, 0); + Vertex *u1 = new Vertex(x1, y0, z0, 0, 8); + Vertex *u2 = new Vertex(x1, y1, z0, 8, 8); + Vertex *u3 = new Vertex(x0, y1, z0, 8, 0); + + Vertex *l0 = new Vertex(x0, y0, z1, 0, 0); + Vertex *l1 = new Vertex(x1, y0, z1, 0, 8); + Vertex *l2 = new Vertex(x1, y1, z1, 8, 8); + Vertex *l3 = new Vertex(x0, y1, z1, 8, 0); + + vertices[0] = u0; + vertices[1] = u1; + vertices[2] = u2; + vertices[3] = u3; + vertices[4] = l0; + vertices[5] = l1; + vertices[6] = l2; + vertices[7] = l3; + + // 4J - added ability to mask individual faces + int faceCount = 0; + if( faceMask & 1 ) polygons[faceCount++] = new _Polygon(VertexArray4(l1, u1, u2, l2), xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Right + if( faceMask & 2 ) polygons[faceCount++] = new _Polygon(VertexArray4(u0, l0, l3, u3), xTexOffs + 0, yTexOffs + d, xTexOffs + d, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Left + if( faceMask & 4 ) polygons[faceCount++] = new _Polygon(VertexArray4(l1, l0, u0, u1), xTexOffs + d, yTexOffs + 0, xTexOffs + d + w, yTexOffs + d, modelPart->xTexSize, modelPart->yTexSize); // Up + if(bFlipPoly3UVs) + { + if( faceMask & 8 ) polygons[faceCount++] = new _Polygon(VertexArray4(u2, u3, l3, l2), xTexOffs + d + w, yTexOffs + 0, xTexOffs + d + w + w, yTexOffs + d, modelPart->xTexSize, modelPart->yTexSize); // Down + } + else + { + if( faceMask & 8 ) polygons[faceCount++] = new _Polygon(VertexArray4(u2, u3, l3, l2), xTexOffs + d + w, yTexOffs + d, xTexOffs + d + w + w, yTexOffs + 0, modelPart->xTexSize, modelPart->yTexSize); // Down + } + if( faceMask & 16 ) polygons[faceCount++] = new _Polygon(VertexArray4(u1, u0, u3, u2), xTexOffs + d, yTexOffs + d, xTexOffs + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Front + if( faceMask & 32 ) polygons[faceCount++] = new _Polygon(VertexArray4(l0, l1, l2, l3), xTexOffs + d + w + d, yTexOffs + d, xTexOffs + d + w + d + w, yTexOffs + d + h, modelPart->xTexSize, modelPart->yTexSize); // Back + polygons.length = faceCount; + + if (modelPart->bMirror) + { + for (unsigned int i = 0; i < polygons.length; i++) + polygons[i]->mirror(); + } +} + + +void Cube::render(Tesselator *t,float scale) +{ + for (int i = 0; i < polygons.length; i++) + { + polygons[i]->render(t, scale); + } +} + +Cube *Cube::setId(const wstring &id) +{ + this->id = id; + return this; +} diff --git a/Minecraft.Client/Cube.h b/Minecraft.Client/Cube.h new file mode 100644 index 0000000..3aee2b6 --- /dev/null +++ b/Minecraft.Client/Cube.h @@ -0,0 +1,29 @@ +#pragma once +#include "..\Minecraft.World\ArrayWithLength.h" +#include "Vertex.h" +#include "Polygon.h" + +class Model; + +class Cube +{ + +private: + VertexArray vertices; + PolygonArray polygons; + +public: + + const float x0, y0, z0, x1, y1, z1; + wstring id; + +public: + Cube(ModelPart *modelPart, int xTexOffs, int yTexOffs, float x0, float y0, float z0, int w, int h, int d, float g, int faceMask = 63, bool bFlipPoly3UVs = false); // 4J - added faceMask + +private: + VertexArray VertexArray4(Vertex *v0, Vertex *v1, Vertex *v2, Vertex *v3); // 4J added + +public: + void render(Tesselator *t,float scale); + Cube *setId(const wstring &id); +}; diff --git a/Minecraft.Client/Culler.h b/Minecraft.Client/Culler.h new file mode 100644 index 0000000..bd428ac --- /dev/null +++ b/Minecraft.Client/Culler.h @@ -0,0 +1,11 @@ +#pragma once +#include "..\Minecraft.World\AABB.h" + +class Culler +{ +public: + virtual bool isVisible(AABB *bb) = 0; + virtual bool cubeInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) = 0; + virtual bool cubeFullyInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) = 0; + virtual void prepare(double xOff, double yOff, double zOff) = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/DLCTexturePack.cpp b/Minecraft.Client/DLCTexturePack.cpp new file mode 100644 index 0000000..3d1e05a --- /dev/null +++ b/Minecraft.Client/DLCTexturePack.cpp @@ -0,0 +1,623 @@ +#include "stdafx.h" +#include "Common\DLC\DLCGameRulesFile.h" +#include "Common\DLC\DLCGameRulesHeader.h" +#include "Common\DLC\DLCGameRules.h" +#include "DLCTexturePack.h" +#include "Common\DLC\DLCColourTableFile.h" +#include "Common\DLC\DLCUIDataFile.h" +#include "Common\DLC\DLCTextureFile.h" +#include "Common\DLC\DLCLocalisationFile.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "StringTable.h" +#include "Common\DLC\DLCAudioFile.h" + +#if defined _XBOX || defined _WINDOWS64 +#include "Xbox\XML\ATGXmlParser.h" +#include "Xbox\XML\xmlFilesCallback.h" +#endif + +DLCTexturePack::DLCTexturePack(DWORD id, DLCPack *pack, TexturePack *fallback) : AbstractTexturePack(id, NULL, pack->getName(), fallback) +{ + m_dlcInfoPack = pack; + m_dlcDataPack = NULL; + bUILoaded = false; + m_bLoadingData = false; + m_bHasLoadedData = false; + m_archiveFile = NULL; + if (app.getLevelGenerationOptions()) app.getLevelGenerationOptions()->setLoadedData(); + m_bUsingDefaultColourTable = true; + + m_stringTable = NULL; + +#ifdef _XBOX + m_pStreamedWaveBank=NULL; + m_pSoundBank=NULL; +#endif + + if(m_dlcInfoPack->doesPackContainFile(DLCManager::e_DLCType_LocalisationData, L"languages.loc")) + { + DLCLocalisationFile *localisationFile = (DLCLocalisationFile *)m_dlcInfoPack->getFile(DLCManager::e_DLCType_LocalisationData, L"languages.loc"); + m_stringTable = localisationFile->getStringTable(); + } + + // 4J Stu - These calls need to be in the most derived version of the class + loadIcon(); + loadName(); + loadDescription(); + //loadDefaultHTMLColourTable(); +} + +void DLCTexturePack::loadIcon() +{ + if(m_dlcInfoPack->doesPackContainFile(DLCManager::e_DLCType_Texture, L"icon.png")) + { + DLCTextureFile *textureFile = (DLCTextureFile *)m_dlcInfoPack->getFile(DLCManager::e_DLCType_Texture, L"icon.png"); + m_iconData = textureFile->getData(m_iconSize); + } + else + { + AbstractTexturePack::loadIcon(); + } +} + +void DLCTexturePack::loadComparison() +{ + if(m_dlcInfoPack->doesPackContainFile(DLCManager::e_DLCType_Texture, L"comparison.png")) + { + DLCTextureFile *textureFile = (DLCTextureFile *)m_dlcInfoPack->getFile(DLCManager::e_DLCType_Texture, L"comparison.png"); + m_comparisonData = textureFile->getData(m_comparisonSize); + } +} + +void DLCTexturePack::loadName() +{ + texname = L""; + + if(m_dlcInfoPack->GetPackID()&1024) + { + if(m_stringTable != NULL) + { + texname = m_stringTable->getString(L"IDS_DISPLAY_NAME"); + m_wsWorldName=m_stringTable->getString(L"IDS_WORLD_NAME"); + } + } + else + { + if(m_stringTable != NULL) + { + texname = m_stringTable->getString(L"IDS_DISPLAY_NAME"); + } + } + +} + +void DLCTexturePack::loadDescription() +{ + desc1 = L""; + + if(m_stringTable != NULL) + { + desc1 = m_stringTable->getString(L"IDS_TP_DESCRIPTION"); + } +} + +wstring DLCTexturePack::getResource(const wstring& name) +{ + // 4J Stu - We should never call this function +#ifndef __CONTENT_PACKAGE + __debugbreak(); +#endif + return L""; +} + +InputStream *DLCTexturePack::getResourceImplementation(const wstring &name) //throws IOException +{ + // 4J Stu - We should never call this function +#ifndef _CONTENT_PACKAGE + __debugbreak(); + if(hasFile(name)) return NULL; +#endif + return NULL; //resource; +} + +bool DLCTexturePack::hasFile(const wstring &name) +{ + bool hasFile = false; + if(m_dlcDataPack != NULL) hasFile = m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, name); + return hasFile; +} + +bool DLCTexturePack::isTerrainUpdateCompatible() +{ + return true; +} + +wstring DLCTexturePack::getPath(bool bTitleUpdateTexture /*= false*/) +{ + return L""; +} + +wstring DLCTexturePack::getAnimationString(const wstring &textureName, const wstring &path) +{ + wstring result = L""; + + wstring fullpath = L"res/" + path + textureName + L".png"; + if(hasFile(fullpath)) + { + result = m_dlcDataPack->getFile(DLCManager::e_DLCType_Texture, fullpath)->getParameterAsString(DLCManager::e_DLCParamType_Anim); + } + + return result; +} + +BufferedImage *DLCTexturePack::getImageResource(const wstring& File, bool filenameHasExtension /*= false*/, bool bTitleUpdateTexture /*=false*/, const wstring &drive /*=L""*/) +{ + if(m_dlcDataPack) return new BufferedImage(m_dlcDataPack, L"/" + File, filenameHasExtension); + else return fallback->getImageResource(File, filenameHasExtension, bTitleUpdateTexture, drive); +} + +DLCPack * DLCTexturePack::getDLCPack() +{ + return m_dlcDataPack; +} + +void DLCTexturePack::loadColourTable() +{ + // Load the game colours + if(m_dlcDataPack != NULL && m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_ColourTable, L"colours.col")) + { + DLCColourTableFile *colourFile = (DLCColourTableFile *)m_dlcDataPack->getFile(DLCManager::e_DLCType_ColourTable, L"colours.col"); + m_colourTable = colourFile->getColourTable(); + m_bUsingDefaultColourTable = false; + } + else + { + // 4J Stu - We can delete the default colour table, but not the one from the DLCColourTableFile + if(!m_bUsingDefaultColourTable) m_colourTable = NULL; + loadDefaultColourTable(); + m_bUsingDefaultColourTable = true; + } + + // Load the text colours +#ifdef _XBOX + if(m_dlcDataPack != NULL && m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp")) + { + DLCUIDataFile *dataFile = (DLCUIDataFile *)m_dlcDataPack->getFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp"); + + DWORD dwSize = 0; + PBYTE pbData = dataFile->getData(dwSize); + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + // Try and load the HTMLColours.col based off the common XML first, before the deprecated xuiscene_colourtable + swprintf(szResourceLocator, LOCATOR_SIZE,L"memory://%08X,%04X#HTMLColours.col",pbData, dwSize); + BYTE *data; + UINT dataLength; + if(XuiResourceLoadAll(szResourceLocator, &data, &dataLength) == S_OK) + { + m_colourTable->loadColoursFromData(data,dataLength); + + XuiFree(data); + } + else + { + + swprintf(szResourceLocator, LOCATOR_SIZE,L"memory://%08X,%04X#xuiscene_colourtable.xur",pbData, dwSize); + HXUIOBJ hScene; + HRESULT hr = XuiSceneCreate(szResourceLocator,szResourceLocator, NULL, &hScene); + + if(HRESULT_SUCCEEDED(hr)) + { + loadHTMLColourTableFromXuiScene(hScene); + } + else + { + loadDefaultHTMLColourTable(); + } + } + } + else + { + loadDefaultHTMLColourTable(); + } +#else + if(app.hasArchiveFile(L"HTMLColours.col")) + { + byteArray textColours = app.getArchiveFile(L"HTMLColours.col"); + m_colourTable->loadColoursFromData(textColours.data,textColours.length); + + delete [] textColours.data; + } +#endif +} + +void DLCTexturePack::loadData() +{ + int mountIndex = m_dlcInfoPack->GetDLCMountIndex(); + + if(mountIndex > -1) + { +#ifdef _DURANGO + if(StorageManager.MountInstalledDLC(ProfileManager.GetPrimaryPad(),mountIndex,&DLCTexturePack::packMounted,this,L"TPACK")!=ERROR_IO_PENDING) +#else + if(StorageManager.MountInstalledDLC(ProfileManager.GetPrimaryPad(),mountIndex,&DLCTexturePack::packMounted,this,"TPACK")!=ERROR_IO_PENDING) +#endif + { + // corrupt DLC + m_bHasLoadedData = true; + if (app.getLevelGenerationOptions()) app.getLevelGenerationOptions()->setLoadedData(); + app.DebugPrintf("Failed to mount texture pack DLC %d for pad %d\n",mountIndex,ProfileManager.GetPrimaryPad()); + } + else + { + m_bLoadingData = true; + app.DebugPrintf("Attempted to mount DLC data for texture pack %d\n", mountIndex); + } + } + else + { + m_bHasLoadedData = true; + if (app.getLevelGenerationOptions()) app.getLevelGenerationOptions()->setLoadedData(); + app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ReloadTexturePack); + } +} + + + + + +wstring DLCTexturePack::getFilePath(DWORD packId, wstring filename, bool bAddDataFolder) +{ + return app.getFilePath(packId,filename,bAddDataFolder); +} + +int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicenceMask) +{ + DLCTexturePack *texturePack = (DLCTexturePack *)pParam; + texturePack->m_bLoadingData = false; + if(dwErr!=ERROR_SUCCESS) + { + // corrupt DLC + app.DebugPrintf("Failed to mount DLC for pad %d: %d\n",iPad,dwErr); + } + else + { + app.DebugPrintf("Mounted DLC for texture pack, attempting to load data\n"); + texturePack->m_dlcDataPack = new DLCPack(texturePack->m_dlcInfoPack->getName(), dwLicenceMask); + texturePack->setHasAudio(false); + DWORD dwFilesProcessed = 0; + // Load the DLC textures + wstring dataFilePath = texturePack->m_dlcInfoPack->getFullDataPath(); + if(!dataFilePath.empty()) + { + if(!app.m_dlcManager.readDLCDataFile(dwFilesProcessed, getFilePath(texturePack->m_dlcInfoPack->GetPackID(), dataFilePath),texturePack->m_dlcDataPack)) + { + delete texturePack->m_dlcDataPack; + texturePack->m_dlcDataPack = NULL; + } + + // Load the UI data + if(texturePack->m_dlcDataPack != NULL) + { +#ifdef _XBOX + File xzpPath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"TexturePack.xzp") ) ); + + if(xzpPath.exists()) + { + const char *pchFilename=wstringtofilename(xzpPath.getPath()); + HANDLE fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); + + if( fileHandle != INVALID_HANDLE_VALUE ) + { + DWORD dwFileSize = xzpPath.length(); + DWORD bytesRead; + PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; + BOOL success = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); + CloseHandle(fileHandle); + if(success) + { + DLCUIDataFile *uiDLCFile = (DLCUIDataFile *)texturePack->m_dlcDataPack->addFile(DLCManager::e_DLCType_UIData,L"TexturePack.xzp"); + uiDLCFile->addData(pbData,bytesRead,true); + + } + } + } +#else + File archivePath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"media.arc") ) ); + if(archivePath.exists()) texturePack->m_archiveFile = new ArchiveFile(archivePath); +#endif + + /** + 4J-JEV: + For all the GameRuleHeader files we find + */ + DLCPack *pack = texturePack->m_dlcInfoPack->GetParentPack(); + LevelGenerationOptions *levelGen = app.getLevelGenerationOptions(); + if (levelGen != NULL && !levelGen->hasLoadedData()) + { + int gameRulesCount = pack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader); + for(int i = 0; i < gameRulesCount; ++i) + { + DLCGameRulesHeader *dlcFile = (DLCGameRulesHeader *) pack->getFile(DLCManager::e_DLCType_GameRulesHeader, i); + + if (!dlcFile->getGrfPath().empty()) + { + File grf( getFilePath(texturePack->m_dlcInfoPack->GetPackID(), dlcFile->getGrfPath() ) ); + if (grf.exists()) + { +#ifdef _UNICODE + wstring path = grf.getPath(); + const WCHAR *pchFilename=path.c_str(); + HANDLE fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#else + const char *pchFilename=wstringtofilename(grf.getPath()); + HANDLE fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#endif + + if( fileHandle != INVALID_HANDLE_VALUE ) + { + DWORD dwFileSize = grf.length(); + DWORD bytesRead; + PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; + BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + CloseHandle(fileHandle); + + // 4J-PB - is it possible that we can get here after a read fail and it's not an error? + dlcFile->setGrfData(pbData, dwFileSize, texturePack->m_stringTable); + + delete [] pbData; + + app.m_gameRules.setLevelGenerationOptions( dlcFile->lgo ); + } + } + } + } + if(levelGen->requiresBaseSave() && !levelGen->getBaseSavePath().empty() ) + { + File grf(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), levelGen->getBaseSavePath() )); + if (grf.exists()) + { +#ifdef _UNICODE + wstring path = grf.getPath(); + const WCHAR *pchFilename=path.c_str(); + HANDLE fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#else + const char *pchFilename=wstringtofilename(grf.getPath()); + HANDLE fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#endif + + if( fileHandle != INVALID_HANDLE_VALUE ) + { + DWORD bytesRead,dwFileSize = GetFileSize(fileHandle,NULL); + PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; + BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + CloseHandle(fileHandle); + + // 4J-PB - is it possible that we can get here after a read fail and it's not an error? + levelGen->setBaseSaveData(pbData, dwFileSize); + } + } + } + } + + + // any audio data? +#ifdef _XBOX + File audioXSBPath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"MashUp.xsb") ) ); + File audioXWBPath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"MashUp.xwb") ) ); + + if(audioXSBPath.exists() && audioXWBPath.exists()) + { + + texturePack->setHasAudio(true); + const char *pchXWBFilename=wstringtofilename(audioXWBPath.getPath()); + Minecraft::GetInstance()->soundEngine->CreateStreamingWavebank(pchXWBFilename,&texturePack->m_pStreamedWaveBank); + const char *pchXSBFilename=wstringtofilename(audioXSBPath.getPath()); + Minecraft::GetInstance()->soundEngine->CreateSoundbank(pchXSBFilename,&texturePack->m_pSoundBank); + + } +#else + //DLCPack *pack = texturePack->m_dlcInfoPack->GetParentPack(); + if(pack->getDLCItemsCount(DLCManager::e_DLCType_Audio)>0) + { + DLCAudioFile *dlcFile = (DLCAudioFile *) pack->getFile(DLCManager::e_DLCType_Audio, 0); + texturePack->setHasAudio(true); + // init the streaming sound ids for this texture pack + int iOverworldStart, iNetherStart, iEndStart; + int iOverworldC, iNetherC, iEndC; + + iOverworldStart=0; + iOverworldC=dlcFile->GetCountofType(DLCAudioFile::e_AudioType_Overworld); + iNetherStart=iOverworldC; + iNetherC=dlcFile->GetCountofType(DLCAudioFile::e_AudioType_Nether); + iEndStart=iOverworldC+iNetherC; + iEndC=dlcFile->GetCountofType(DLCAudioFile::e_AudioType_End); + + Minecraft::GetInstance()->soundEngine->SetStreamingSounds(iOverworldStart,iOverworldStart+iOverworldC, + iNetherStart,iNetherStart+iNetherC,iEndStart,iEndStart+iEndC,iEndStart+iEndC); // push the CD start to after + } +#endif +} + texturePack->loadColourTable(); + } + + // 4J-PB - we need to leave the texture pack mounted if it contained streaming audio + if(texturePack->hasAudio()==false) + { +#ifdef _XBOX + StorageManager.UnmountInstalledDLC("TPACK"); +#endif + } + } + + texturePack->m_bHasLoadedData = true; + if (app.getLevelGenerationOptions()) app.getLevelGenerationOptions()->setLoadedData(); + app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ReloadTexturePack); + + return 0; +} + +void DLCTexturePack::loadUI() +{ +#ifdef _XBOX +//Syntax: "memory://" + Address + "," + Size + "#" + File +//L"memory://0123ABCD,21A3#skin_default.xur" + + // Load new skin + if(m_dlcDataPack != NULL && m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp")) + { + DLCUIDataFile *dataFile = (DLCUIDataFile *)m_dlcDataPack->getFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp"); + + DWORD dwSize = 0; + PBYTE pbData = dataFile->getData(dwSize); + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + swprintf(szResourceLocator, LOCATOR_SIZE,L"memory://%08X,%04X#skin_Minecraft.xur",pbData, dwSize); + + XuiFreeVisuals(L""); + + + HRESULT hr = app.LoadSkin(szResourceLocator,NULL);//L"TexturePack"); + if(HRESULT_SUCCEEDED(hr)) + { + bUILoaded = true; + //CXuiSceneBase::GetInstance()->SetVisualPrefix(L"TexturePack"); + //CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); + } + } +#else + if(m_archiveFile && m_archiveFile->hasFile(L"skin.swf")) + { + ui.ReloadSkin(); + bUILoaded = true; + } +#endif + else + { + loadDefaultUI(); + bUILoaded = true; + } + + AbstractTexturePack::loadUI(); +#ifndef _XBOX + if(hasAudio()==false && !ui.IsReloadingSkin()) + { +#ifdef _DURANGO + StorageManager.UnmountInstalledDLC(L"TPACK"); +#else + StorageManager.UnmountInstalledDLC("TPACK"); +#endif + } +#endif +} + +void DLCTexturePack::unloadUI() +{ + // Unload skin + if(bUILoaded) + { +#ifdef _XBOX + XuiFreeVisuals(L"TexturePack"); + XuiFreeVisuals(L""); + CXuiSceneBase::GetInstance()->SetVisualPrefix(L""); + CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); +#endif + setHasAudio(false); + } + AbstractTexturePack::unloadUI(); + + app.m_dlcManager.removePack(m_dlcDataPack); + m_dlcDataPack = NULL; + delete m_archiveFile; + m_bHasLoadedData = false; + + bUILoaded = false; +} + +wstring DLCTexturePack::getXuiRootPath() +{ + wstring path = L""; + if(m_dlcDataPack != NULL && m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp")) + { + DLCUIDataFile *dataFile = (DLCUIDataFile *)m_dlcDataPack->getFile(DLCManager::e_DLCType_UIData, L"TexturePack.xzp"); + + DWORD dwSize = 0; + PBYTE pbData = dataFile->getData(dwSize); + + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + swprintf(szResourceLocator, LOCATOR_SIZE,L"memory://%08X,%04X#",pbData, dwSize); + path = szResourceLocator; + } + return path; +} + +unsigned int DLCTexturePack::getDLCParentPackId() +{ + return m_dlcInfoPack->GetParentPackId(); +} + +unsigned char DLCTexturePack::getDLCSubPackId() +{ + return (m_dlcInfoPack->GetPackId()>>24)&0xFF; +} + +DLCPack * DLCTexturePack::getDLCInfoParentPack() +{ + return m_dlcInfoPack->GetParentPack(); +} + +XCONTENTDEVICEID DLCTexturePack::GetDLCDeviceID() +{ + return m_dlcInfoPack->GetDLCDeviceID(); +} diff --git a/Minecraft.Client/DLCTexturePack.h b/Minecraft.Client/DLCTexturePack.h new file mode 100644 index 0000000..14e6ea2 --- /dev/null +++ b/Minecraft.Client/DLCTexturePack.h @@ -0,0 +1,72 @@ +#pragma once + +#include "AbstractTexturePack.h" + +class DLCPack; +class StringTable; + +class DLCTexturePack : public AbstractTexturePack +{ +private: + DLCPack *m_dlcInfoPack; // Description, icon etc + DLCPack *m_dlcDataPack; // Actual textures + StringTable *m_stringTable; + bool bUILoaded; + bool m_bLoadingData, m_bHasLoadedData; + bool m_bUsingDefaultColourTable; + //bool m_bHasAudio; + ArchiveFile *m_archiveFile; + + + +public: + using AbstractTexturePack::getResource; + + DLCTexturePack(DWORD id, DLCPack *pack, TexturePack *fallback); + ~DLCTexturePack(); + + virtual wstring getResource(const wstring& name); + virtual DLCPack * getDLCPack(); + // Added for sound banks with MashUp packs +#ifdef _XBOX + IXACT3WaveBank *m_pStreamedWaveBank; + IXACT3SoundBank *m_pSoundBank; +#endif +protected: + //@Override + void loadIcon(); + void loadComparison(); + void loadName(); + void loadDescription(); + InputStream *getResourceImplementation(const wstring &name); //throws IOException + +public: + //@Override + bool hasFile(const wstring &name); + bool isTerrainUpdateCompatible(); + + // 4J Added + virtual wstring getPath(bool bTitleUpdateTexture = false); + virtual wstring getAnimationString(const wstring &textureName, const wstring &path); + virtual BufferedImage *getImageResource(const wstring& File, bool filenameHasExtension = false, bool bTitleUpdateTexture=false, const wstring &drive =L""); + virtual void loadColourTable(); + virtual bool hasData() { return m_bHasLoadedData; } + virtual bool isLoadingData() { return m_bLoadingData; } + +private: + static wstring getRootPath(DWORD packId, bool allowOverride, bool bAddDataFolder); + static wstring getFilePath(DWORD packId, wstring filename, bool bAddDataFolder=true); + +public: + static int packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicenceMask); + virtual void loadData(); + virtual void loadUI(); + virtual void unloadUI(); + virtual wstring getXuiRootPath(); + virtual ArchiveFile *getArchiveFile() { return m_archiveFile; } + + virtual unsigned int getDLCParentPackId(); + virtual DLCPack *getDLCInfoParentPack(); + virtual unsigned char getDLCSubPackId(); + XCONTENTDEVICEID GetDLCDeviceID(); +}; diff --git a/Minecraft.Client/DeathScreen.cpp b/Minecraft.Client/DeathScreen.cpp new file mode 100644 index 0000000..a06606e --- /dev/null +++ b/Minecraft.Client/DeathScreen.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "DeathScreen.h" +#include "Button.h" +#include "MultiplayerLocalPlayer.h" +#include "TitleScreen.h" + +void DeathScreen::init() +{ + buttons.clear(); + buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 3, L"Respawn")); + buttons.push_back(new Button(2, width / 2 - 100, height / 4 + 24 * 4, L"Title menu")); + + if (minecraft->user == NULL) + { + buttons[1]->active = false; + } +} + +void DeathScreen::keyPressed(char eventCharacter, int eventKey) +{ +} + +void DeathScreen::buttonClicked(Button *button) +{ + if (button->id == 0) + { + // minecraft.setScreen(new OptionsScreen(this, minecraft.options)); + } + if (button->id == 1) + { + minecraft->player->respawn(); + minecraft->setScreen(NULL); + // minecraft.setScreen(new NewLevelScreen(this)); + } + if (button->id == 2) + { + minecraft->setLevel(NULL); + minecraft->setScreen(new TitleScreen()); + } +} + +void DeathScreen::render(int xm, int ym, float a) +{ + fillGradient(0, 0, width, height, 0x60500000, 0xa0803030); + + glPushMatrix(); + glScalef(2, 2, 2); + drawCenteredString(font, L"Game over!", width / 2 / 2, 60 / 2, 0xffffff); + glPopMatrix(); + drawCenteredString(font, L"Score: &e" + _toString( minecraft->player->getScore() ), width / 2, 100, 0xffffff); + + Screen::render(xm, ym, a); + + // 4J - debug code - remove + static int count = 0; + if( count++ == 100 ) + { + count = 0; + buttonClicked(buttons[0]); + } +} + +bool DeathScreen::isPauseScreen() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/DeathScreen.h b/Minecraft.Client/DeathScreen.h new file mode 100644 index 0000000..6fbf234 --- /dev/null +++ b/Minecraft.Client/DeathScreen.h @@ -0,0 +1,14 @@ +#pragma once +#include "Screen.h" + +class DeathScreen : public Screen +{ +public: + virtual void init(); +protected: + virtual void keyPressed(char eventCharacter, int eventKey); + virtual void buttonClicked(Button *button); +public: + virtual void render(int xm, int ym, float a); + virtual bool isPauseScreen(); +}; \ No newline at end of file diff --git a/Minecraft.Client/DefaultRenderer.cpp b/Minecraft.Client/DefaultRenderer.cpp new file mode 100644 index 0000000..d4c1573 --- /dev/null +++ b/Minecraft.Client/DefaultRenderer.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "DefaultRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" + +void DefaultRenderer::render(shared_ptr entity, double x, double y, double z, float rot, float a) +{ + glPushMatrix(); +// 4J - removed following line as doesn't really make any sense +// render(entity->bb, (x-entity->xOld), (y-entity->yOld), (z-entity->zOld)); + glPopMatrix(); +} diff --git a/Minecraft.Client/DefaultRenderer.h b/Minecraft.Client/DefaultRenderer.h new file mode 100644 index 0000000..dcb5a9c --- /dev/null +++ b/Minecraft.Client/DefaultRenderer.h @@ -0,0 +1,8 @@ +#pragma once +#include "EntityRenderer.h" + +class DefaultRenderer : public EntityRenderer +{ +public: + virtual void render(shared_ptr entity, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/DefaultTexturePack.cpp b/Minecraft.Client/DefaultTexturePack.cpp new file mode 100644 index 0000000..d2974b6 --- /dev/null +++ b/Minecraft.Client/DefaultTexturePack.cpp @@ -0,0 +1,131 @@ +#include "stdafx.h" +#include "DefaultTexturePack.h" +#include "Textures.h" +#include "..\Minecraft.World\StringHelpers.h" + + +DefaultTexturePack::DefaultTexturePack() : AbstractTexturePack(0, NULL, L"Minecraft", NULL) +{ + // 4J Stu - These calls need to be in the most derived version of the class + loadIcon(); + loadName(); // 4J-PB - added so the PS3 can have localised texture names' + loadDescription(); + loadColourTable(); +} + +void DefaultTexturePack::loadIcon() +{ +#ifdef _XBOX + // 4J Stu - Temporary only + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL); + swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/TexturePackIcon.png"); + + UINT size = 0; + HRESULT hr = XuiResourceLoadAllNoLoc(szResourceLocator, &m_iconData, &size); + m_iconSize = size; +#else + if(app.hasArchiveFile(L"Graphics\\TexturePackIcon.png")) + { + byteArray ba = app.getArchiveFile(L"Graphics\\TexturePackIcon.png"); + m_iconData = ba.data; + m_iconSize = ba.length; + } +#endif +} + +void DefaultTexturePack::loadDescription() +{ + desc1 = L"LOCALISE ME: The default look of Minecraft"; +} +void DefaultTexturePack::loadName() +{ + texname = L"Minecraft"; +} + +bool DefaultTexturePack::hasFile(const wstring &name) +{ +// return DefaultTexturePack::class->getResourceAsStream(name) != null; + return true; +} + +bool DefaultTexturePack::isTerrainUpdateCompatible() +{ + return true; +} + +InputStream *DefaultTexturePack::getResourceImplementation(const wstring &name)// throws FileNotFoundException +{ + wstring wDrive = L""; + // Make the content package point to to the UPDATE: drive is needed +#ifdef _XBOX + #ifdef _TU_BUILD + wDrive=L"UPDATE:\\res"; + #else + + wDrive=L"GAME:\\res\\TitleUpdate\\res"; + #endif +#elif __PS3__ + + char *pchUsrDir; + if(app.GetBootedFromDiscPatch()) + { + const char *pchTextureName=wstringtofilename(name); + pchUsrDir = app.GetBDUsrDirPath(pchTextureName); + app.DebugPrintf("DefaultTexturePack::getResourceImplementation - texture %s - Drive - %s\n",pchTextureName,pchUsrDir); + } + else + { + const char *pchTextureName=wstringtofilename(name); + pchUsrDir=getUsrDirPath(); + app.DebugPrintf("DefaultTexturePack::getResourceImplementation - texture %s - Drive - %s\n",pchTextureName,pchUsrDir); + } + + + wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); + + wDrive = wstr + L"\\Common\\res\\TitleUpdate\\res"; +#elif __PSVITA__ + + /* + char *pchUsrDir=getUsrDirPath(); + wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); + + wDrive = wstr + L"Common\\res\\TitleUpdate\\res"; + */ + wDrive = L"Common\\res\\TitleUpdate\\res"; +#else + wDrive = L"Common\\res\\TitleUpdate\\res"; + +#endif + InputStream *resource = InputStream::getResourceAsStream(wDrive + name); + //InputStream *stream = DefaultTexturePack::class->getResourceAsStream(name); + //if (stream == NULL) + //{ + // throw new FileNotFoundException(name); + //} + + //return stream; + return resource; +} + +void DefaultTexturePack::loadUI() +{ + loadDefaultUI(); + + AbstractTexturePack::loadUI(); +} + +void DefaultTexturePack::unloadUI() +{ +#ifdef _XBOX + // Unload skin + XuiFreeVisuals(L"TexturePack"); + XuiFreeVisuals(L""); + CXuiSceneBase::GetInstance()->SetVisualPrefix(L""); + CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); +#endif + AbstractTexturePack::unloadUI(); +} diff --git a/Minecraft.Client/DefaultTexturePack.h b/Minecraft.Client/DefaultTexturePack.h new file mode 100644 index 0000000..9aa87a0 --- /dev/null +++ b/Minecraft.Client/DefaultTexturePack.h @@ -0,0 +1,33 @@ +#pragma once +#include "AbstractTexturePack.h" + +class DefaultTexturePack : public AbstractTexturePack +{ +public: + DefaultTexturePack(); + DLCPack * getDLCPack() {return NULL;} + +protected: + //@Override + void loadIcon(); + void loadName(); + void loadDescription(); + +public: + //@Override + bool hasFile(const wstring &name); + bool isTerrainUpdateCompatible(); + + wstring getDesc1() {return app.GetString(IDS_DEFAULT_TEXTUREPACK);} + +protected: + //@Override + InputStream *getResourceImplementation(const wstring &name); // throws FileNotFoundException + +public: + virtual bool hasData() { return true; } + virtual bool hasAudio() { return false; } + virtual bool isLoadingData() { return false; } + virtual void loadUI(); + virtual void unloadUI(); +}; \ No newline at end of file diff --git a/Minecraft.Client/DemoLevel.cpp b/Minecraft.Client/DemoLevel.cpp new file mode 100644 index 0000000..52edc0b --- /dev/null +++ b/Minecraft.Client/DemoLevel.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "DemoLevel.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" + +DemoLevel::DemoLevel(shared_ptr levelStorage, const wstring& levelName) : Level(levelStorage, levelName, DEMO_LEVEL_SEED) +{ +} + +DemoLevel::DemoLevel(Level *level, Dimension *dimension): Level(level, dimension) +{ +} + +void DemoLevel::setInitialSpawn() +{ + levelData->setSpawn(DEMO_SPAWN_X, DEMO_SPAWN_Y, DEMO_SPAWN_Z); +} \ No newline at end of file diff --git a/Minecraft.Client/DemoLevel.h b/Minecraft.Client/DemoLevel.h new file mode 100644 index 0000000..8364d5b --- /dev/null +++ b/Minecraft.Client/DemoLevel.h @@ -0,0 +1,16 @@ +#pragma once +#include "..\Minecraft.World\net.minecraft.world.level.h" + +class DemoLevel : public Level +{ +private: + static const __int64 DEMO_LEVEL_SEED = 0; // 4J - TODO - was "Don't Look Back".hashCode(); + static const int DEMO_SPAWN_X = 796; + static const int DEMO_SPAWN_Y = 72; + static const int DEMO_SPAWN_Z = -731; +public: + DemoLevel(shared_ptr levelStorage, const wstring& levelName); + DemoLevel(Level *level, Dimension *dimension); +protected: + virtual void setInitialSpawn(); +}; diff --git a/Minecraft.Client/DemoMode.cpp b/Minecraft.Client/DemoMode.cpp new file mode 100644 index 0000000..3b24af7 --- /dev/null +++ b/Minecraft.Client/DemoMode.cpp @@ -0,0 +1,125 @@ +#include "stdafx.h" +#include "DemoMode.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" + +DemoMode::DemoMode(Minecraft *minecraft) : SurvivalMode(minecraft) +{ + demoHasEnded = false; + demoEndedReminder = 0; +} + +void DemoMode::tick() +{ + SurvivalMode::tick(); + +/* 4J - TODO - seems unlikely we need this demo mode anyway + __int64 time = minecraft->level->getTime(); + __int64 day = (time / Level::TICKS_PER_DAY) + 1; + + demoHasEnded = (time > (500 + Level::TICKS_PER_DAY * DEMO_DAYS)); + if (demoHasEnded) + { + demoEndedReminder++; + } + + if ((time % Level::TICKS_PER_DAY) == 500) + { + if (day <= (DEMO_DAYS + 1)) + { + minecraft->gui->displayClientMessage(L"demo.day." + _toString<__int64>(day)); + } + } + else if (day == 1) + { + Options *options = minecraft->options; + wstring message; + + if (time == 100) { + minecraft.gui.addMessage("Seed: " + minecraft.level.getSeed()); + message = language.getElement("demo.help.movement"); + message = String.format(message, Keyboard.getKeyName(options.keyUp.key), Keyboard.getKeyName(options.keyLeft.key), Keyboard.getKeyName(options.keyDown.key), + Keyboard.getKeyName(options.keyRight.key)); + } else if (time == 175) { + message = language.getElement("demo.help.jump"); + message = String.format(message, Keyboard.getKeyName(options.keyJump.key)); + } else if (time == 250) { + message = language.getElement("demo.help.inventory"); + message = String.format(message, Keyboard.getKeyName(options.keyBuild.key)); + } + if (message != null) { + minecraft.gui.addMessage(message); + } + } else if (day == DEMO_DAYS) { + if ((time % Level.TICKS_PER_DAY) == 22000) { + minecraft.gui.displayClientMessage("demo.day.warning"); + } + } +*/ +} + +void DemoMode::outputDemoReminder() +{ +/* 4J - TODO + if (demoEndedReminder > 100) { + minecraft.gui.displayClientMessage("demo.reminder"); + demoEndedReminder = 0; + } + */ +} + +void DemoMode::startDestroyBlock(int x, int y, int z, int face) +{ + if (demoHasEnded) + { + outputDemoReminder(); + return; + } + SurvivalMode::startDestroyBlock(x, y, z, face); +} + +void DemoMode::continueDestroyBlock(int x, int y, int z, int face) +{ + if (demoHasEnded) + { + return; + } + SurvivalMode::continueDestroyBlock(x, y, z, face); +} + +bool DemoMode::destroyBlock(int x, int y, int z, int face) +{ + if (demoHasEnded) + { + return false; + } + return SurvivalMode::destroyBlock(x, y, z, face); +} + +bool DemoMode::useItem(shared_ptr player, Level *level, shared_ptr item) +{ + if (demoHasEnded) + { + outputDemoReminder(); + return false; + } + return SurvivalMode::useItem(player, level, item); +} + +bool DemoMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face) +{ + if (demoHasEnded) { + outputDemoReminder(); + return false; + } + return SurvivalMode::useItemOn(player, level, item, x, y, z, face); +} + +void DemoMode::attack(shared_ptr player, shared_ptr entity) +{ + if (demoHasEnded) + { + outputDemoReminder(); + return; + } + SurvivalMode::attack(player, entity); +} diff --git a/Minecraft.Client/DemoMode.h b/Minecraft.Client/DemoMode.h new file mode 100644 index 0000000..429c9ec --- /dev/null +++ b/Minecraft.Client/DemoMode.h @@ -0,0 +1,27 @@ +#pragma once +#include "SurvivalMode.h" + +class DemoMode : public SurvivalMode +{ +private: + static const int DEMO_DAYS = 5; + + bool demoHasEnded; + int demoEndedReminder; + +public: + DemoMode(Minecraft *minecraft); + virtual void tick(); +private: + void outputDemoReminder(); +public: + using GameMode::useItem; + using SurvivalMode::useItemOn; + + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual void continueDestroyBlock(int x, int y, int z, int face); + virtual bool destroyBlock(int x, int y, int z, int face); + virtual bool useItem(shared_ptr player, Level *level, shared_ptr item); + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face); + virtual void attack(shared_ptr player, shared_ptr entity); +}; diff --git a/Minecraft.Client/DemoUser.cpp b/Minecraft.Client/DemoUser.cpp new file mode 100644 index 0000000..b19b368 --- /dev/null +++ b/Minecraft.Client/DemoUser.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "DemoUser.h" + +DemoUser::DemoUser() : User(L"DemoUser", L"n/a") +{ +} \ No newline at end of file diff --git a/Minecraft.Client/DemoUser.h b/Minecraft.Client/DemoUser.h new file mode 100644 index 0000000..d10085d --- /dev/null +++ b/Minecraft.Client/DemoUser.h @@ -0,0 +1,8 @@ +#pragma once +#include "User.h" + +class DemoUser : public User +{ +public: + DemoUser(); +}; \ No newline at end of file diff --git a/Minecraft.Client/DerivedServerLevel.cpp b/Minecraft.Client/DerivedServerLevel.cpp new file mode 100644 index 0000000..a67408d --- /dev/null +++ b/Minecraft.Client/DerivedServerLevel.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "DerivedServerLevel.h" +#include "..\Minecraft.World\SavedDataStorage.h" +#include "..\Minecraft.World\DerivedLevelData.h" + +DerivedServerLevel::DerivedServerLevel(MinecraftServer *server, shared_ptr levelStorage, const wstring& levelName, int dimension, LevelSettings *levelSettings, ServerLevel *wrapped) + : ServerLevel(server, levelStorage, levelName, dimension, levelSettings) +{ + // 4J-PB - we're going to override the savedDataStorage, so we need to delete the current one + if(this->savedDataStorage) + { + delete this->savedDataStorage; + this->savedDataStorage=NULL; + } + this->savedDataStorage = wrapped->savedDataStorage; + levelData = new DerivedLevelData(wrapped->getLevelData()); +} + +DerivedServerLevel::~DerivedServerLevel() +{ + // we didn't allocate savedDataStorage here, so we don't want the level destructor to delete it + this->savedDataStorage=NULL; +} + +void DerivedServerLevel::saveLevelData() +{ + // Do nothing? + // Do nothing! +} \ No newline at end of file diff --git a/Minecraft.Client/DerivedServerLevel.h b/Minecraft.Client/DerivedServerLevel.h new file mode 100644 index 0000000..2d49e4f --- /dev/null +++ b/Minecraft.Client/DerivedServerLevel.h @@ -0,0 +1,12 @@ +#pragma once +#include "ServerLevel.h" + +class DerivedServerLevel : public ServerLevel +{ +public: + DerivedServerLevel(MinecraftServer *server, shared_ptrlevelStorage, const wstring& levelName, int dimension, LevelSettings *levelSettings, ServerLevel *wrapped); + ~DerivedServerLevel(); + +protected: + void saveLevelData(); +}; \ No newline at end of file diff --git a/Minecraft.Client/DirtyChunkSorter.cpp b/Minecraft.Client/DirtyChunkSorter.cpp new file mode 100644 index 0000000..1a7c10c --- /dev/null +++ b/Minecraft.Client/DirtyChunkSorter.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "DirtyChunkSorter.h" +#include "../Minecraft.World/net.minecraft.world.entity.player.h" +#include "Chunk.h" + +DirtyChunkSorter::DirtyChunkSorter(shared_ptr cameraEntity, int playerIndex) // 4J - added player index +{ + this->cameraEntity = cameraEntity; + this->playerIndex = playerIndex; +} + +bool DirtyChunkSorter::operator()(const Chunk *c0, const Chunk *c1) const +{ + bool i0 = c0->clipChunk->visible; + bool i1 = c1->clipChunk->visible; + if (i0 && !i1) return false; + if (i1 && !i0) return true; + + double d0 = c0->distanceToSqr(cameraEntity); + double d1 = c1->distanceToSqr(cameraEntity); + + if (d0 < d1) return false; + if (d0 > d1) return true; + + return c0->id >= c1->id; // 4J - was c0.id < c1.id ? 1 : -1 +} \ No newline at end of file diff --git a/Minecraft.Client/DirtyChunkSorter.h b/Minecraft.Client/DirtyChunkSorter.h new file mode 100644 index 0000000..4caa2fa --- /dev/null +++ b/Minecraft.Client/DirtyChunkSorter.h @@ -0,0 +1,14 @@ +#pragma once +class Chunk; +class Mob; + +class DirtyChunkSorter : public std::binary_function +{ +private: + shared_ptr cameraEntity; + int playerIndex; // 4J added + +public: + DirtyChunkSorter(shared_ptr cameraEntity, int playerIndex); // 4J - added player index + bool operator()(const Chunk *a, const Chunk *b) const; +}; \ No newline at end of file diff --git a/Minecraft.Client/DisconnectedScreen.cpp b/Minecraft.Client/DisconnectedScreen.cpp new file mode 100644 index 0000000..b445e4d --- /dev/null +++ b/Minecraft.Client/DisconnectedScreen.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "DisconnectedScreen.h" +#include "TitleScreen.h" +#include "Button.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + +DisconnectedScreen::DisconnectedScreen(const wstring& title, const wstring reason, void *reasonObjects, ...) +{ + Language *language = Language::getInstance(); + + this->title = language->getElement(title); + if (reasonObjects != NULL) + { + this->reason = language->getElement(reason, reasonObjects); + } + else + { + this->reason = language->getElement(reason); + } +} + +void DisconnectedScreen::tick() +{ +} + +void DisconnectedScreen::keyPressed(char eventCharacter, int eventKey) +{ +} + +void DisconnectedScreen::init() +{ + Language *language = Language::getInstance(); + + buttons.clear(); + buttons.push_back(new Button(0, width / 2 - 100, height / 4 + 24 * 5 + 12, language->getElement(L"gui.toMenu"))); + +} + +void DisconnectedScreen::buttonClicked(Button *button) +{ + if (button->id == 0) + { + minecraft->setScreen(new TitleScreen()); + } +} + +void DisconnectedScreen::render(int xm, int ym, float a) +{ + renderBackground(); + + drawCenteredString(font, title, width / 2, height / 2 - 50, 0xffffff); + drawCenteredString(font, reason, width / 2, height / 2 - 10, 0xffffff); + + Screen::render(xm, ym, a); +} diff --git a/Minecraft.Client/DisconnectedScreen.h b/Minecraft.Client/DisconnectedScreen.h new file mode 100644 index 0000000..78e61af --- /dev/null +++ b/Minecraft.Client/DisconnectedScreen.h @@ -0,0 +1,23 @@ +#pragma once +#include "Screen.h" +using namespace std; + +class DisconnectedScreen : public Screen +{ +private: + wstring title, reason; + +public: + DisconnectedScreen(const wstring& title, const wstring reason, void *reasonObjects, ...); + virtual void tick(); +protected: + using Screen::keyPressed; + + virtual void keyPressed(char eventCharacter, int eventKey); +public: + virtual void init(); +protected: + virtual void buttonClicked(Button *button); +public: + virtual void render(int xm, int ym, float a); +}; diff --git a/Minecraft.Client/DistanceChunkSorter.cpp b/Minecraft.Client/DistanceChunkSorter.cpp new file mode 100644 index 0000000..80ac0cd --- /dev/null +++ b/Minecraft.Client/DistanceChunkSorter.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "DistanceChunkSorter.h" +#include "../Minecraft.World/net.minecraft.world.entity.player.h" +#include "Chunk.h" + +DistanceChunkSorter::DistanceChunkSorter(shared_ptr player) +{ + ix = -player->x; + iy = -player->y; + iz = -player->z; +} + +bool DistanceChunkSorter::operator()(const Chunk *c0, const Chunk *c1) const +{ + double xd0 = c0->xm + ix; + double yd0 = c0->ym + iy; + double zd0 = c0->zm + iz; + + double xd1 = c1->xm + ix; + double yd1 = c1->ym + iy; + double zd1 = c1->zm + iz; + + return (((xd0 * xd0 + yd0 * yd0 + zd0 * zd0) - (xd1 * xd1 + yd1 * yd1 + zd1 * zd1)) * 1024) < 0.0; +} \ No newline at end of file diff --git a/Minecraft.Client/DistanceChunkSorter.h b/Minecraft.Client/DistanceChunkSorter.h new file mode 100644 index 0000000..4789070 --- /dev/null +++ b/Minecraft.Client/DistanceChunkSorter.h @@ -0,0 +1,13 @@ +#pragma once +class Entity; +class Chunk; + +class DistanceChunkSorter : public std::binary_function +{ +private: + double ix, iy, iz; + +public: + DistanceChunkSorter(shared_ptr player); + bool operator()(const Chunk *a, const Chunk *b) const; +}; \ No newline at end of file diff --git a/Minecraft.Client/DragonBreathParticle.cpp b/Minecraft.Client/DragonBreathParticle.cpp new file mode 100644 index 0000000..0d0f75f --- /dev/null +++ b/Minecraft.Client/DragonBreathParticle.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "..\Minecraft.World\JavaMath.h" +#include "DragonBreathParticle.h" + +void DragonBreathParticle::init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) +{ + xd *= 0.1f; + yd *= 0.1f; + zd *= 0.1f; + xd = xa; //+= xa; + yd = ya; //+= ya; + zd = za; //+= za; + + unsigned int cMin = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DragonBreathMin ); //0xb700d2 + unsigned int cMax = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DragonBreathMax ); //0xdf00f9 + double rMin = ( (cMin>>16)&0xFF )/255.0f, gMin = ( (cMin>>8)&0xFF )/255.0, bMin = ( cMin&0xFF )/255.0; + double rMax = ( (cMax>>16)&0xFF )/255.0f, gMax = ( (cMax>>8)&0xFF )/255.0, bMax = ( cMax&0xFF )/255.0; + + rCol = (rMax - rMin) * Math::random() + rMin; // 184/255 -- 224/255 + gCol = (gMax - gMin) * Math::random() + gMin; // 0,0 + bCol = (bMax - bMin) * Math::random() + bMin; // 210/255 -- 250/255 + + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = (int) (20 / (Math::random() * 0.8 + 0.2)); + lifetime = (int) (lifetime * scale); + noPhysics = false; + + m_bHasHitGround = false; +} + +DragonBreathParticle::DragonBreathParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level, x, y, z, xa, ya, za, 1); +} + +DragonBreathParticle::DragonBreathParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level, x, y, z, xa, ya, za, scale); +} + +void DragonBreathParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void DragonBreathParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + setMiscTex( ( 3 * age / lifetime) + 5 ); + + if(onGround) + { + yd = 0; + m_bHasHitGround = true; + } + + if(m_bHasHitGround) yd += 0.002; //0.004; + + move(xd, yd, zd); + if (y == yo) + { + xd *= 1.1; + zd *= 1.1; + } + xd *= 0.96f; + zd *= 0.96f; + + if(m_bHasHitGround) yd *= 0.96f; + + // if (onGround) + //{ + // xd *= 0.7f; + // zd *= 0.7f; + // } +} + +int DragonBreathParticle::getParticleTexture() +{ + return ParticleEngine::DRAGON_BREATH_TEXTURE; +} + +float DragonBreathParticle::getBrightness(float a) +{ + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + float brightness = (0.5f / l) + 0.5f; + + return brightness; +} \ No newline at end of file diff --git a/Minecraft.Client/DragonBreathParticle.h b/Minecraft.Client/DragonBreathParticle.h new file mode 100644 index 0000000..e9199d7 --- /dev/null +++ b/Minecraft.Client/DragonBreathParticle.h @@ -0,0 +1,20 @@ +#pragma once +#include "Particle.h" + +class DragonBreathParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eTYPE_DRAGONBREATHPARTICLE; } +private: + bool m_bHasHitGround; + void init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); // 4J - added +public: + DragonBreathParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + float oSize; + + DragonBreathParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); + virtual int getParticleTexture(); + virtual float getBrightness(float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/DragonModel.cpp b/Minecraft.Client/DragonModel.cpp new file mode 100644 index 0000000..3986182 --- /dev/null +++ b/Minecraft.Client/DragonModel.cpp @@ -0,0 +1,248 @@ +#include "stdafx.h" +#include "DragonModel.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\Enderdragon.h" + +DragonModel::DragonModel(float g) : Model() +{ + // 4J-PB + texWidth = 256; + texHeight = 256; + + setMapTex(L"body.body", 0, 0); + setMapTex(L"wing.skin", -56, 88); + setMapTex(L"wingtip.skin", -56, 144); + setMapTex(L"rearleg.main", 0, 0); + setMapTex(L"rearfoot.main", 112, 0); + setMapTex(L"rearlegtip.main", 196, 0); + setMapTex(L"head.upperhead", 112, 30); + setMapTex(L"wing.bone", 112, 88); + setMapTex(L"head.upperlip", 176, 44); + setMapTex(L"jaw.jaw", 176, 65); + setMapTex(L"frontleg.main", 112, 104); + setMapTex(L"wingtip.bone", 112, 136); + setMapTex(L"frontfoot.main", 144, 104); + setMapTex(L"neck.box", 192, 104); + setMapTex(L"frontlegtip.main", 226, 138); + setMapTex(L"body.scale", 220, 53); + setMapTex(L"head.scale", 0, 0); + setMapTex(L"neck.scale", 48, 0); + setMapTex(L"head.nostril", 112, 0); + + float zo = -16; + head = new ModelPart(this, L"head"); + head->addBox(L"upperlip", -6, -1, -8 + zo, 12, 5, 16); + head->addBox(L"upperhead", -8, -8, 6 + zo, 16, 16, 16); + head->bMirror = true; + head->addBox(L"scale", -1 - 4, -12, 12 + zo, 2, 4, 6); + head->addBox(L"nostril", -1 - 4, -3, -6 + zo, 2, 2, 4); + head->bMirror = false; + head->addBox(L"scale", -1 + 4, -12, 12 + zo, 2, 4, 6); + head->addBox(L"nostril", -1 + 4, -3, -6 + zo, 2, 2, 4); + + jaw = new ModelPart(this, L"jaw"); + jaw->setPos(0, 4, 8 + zo); + jaw->addBox(L"jaw", -6, 0, -16, 12, 4, 16); + head->addChild(jaw); + + neck = new ModelPart(this, L"neck"); + neck->addBox(L"box", -5, -5, -5, 10, 10, 10); + neck->addBox(L"scale", -1, -9, -5 + 2, 2, 4, 6); + + body = new ModelPart(this, L"body"); + body->setPos(0, 4, 8); + body->addBox(L"body", -12, 0, -16, 24, 24, 64); + body->addBox(L"scale", -1, -6, -10 + 20 * 0, 2, 6, 12); + body->addBox(L"scale", -1, -6, -10 + 20 * 1, 2, 6, 12); + body->addBox(L"scale", -1, -6, -10 + 20 * 2, 2, 6, 12); + + wing = new ModelPart(this, L"wing"); + wing->setPos(-12, 5, 2); + wing->addBox(L"bone", -56, -4, -4, 56, 8, 8); + wing->addBox(L"skin", -56, 0, +2, 56, 0, 56); + wingTip = new ModelPart(this, L"wingtip"); + wingTip->setPos(-56, 0, 0); + wingTip->addBox(L"bone", -56, -2, -2, 56, 4, 4); + wingTip->addBox(L"skin", -56, 0, +2, 56, 0, 56); + wing->addChild(wingTip); + + frontLeg = new ModelPart(this, L"frontleg"); + frontLeg->setPos(-12, 20, 2); + frontLeg->addBox(L"main", -4, -4, -4, 8, 24, 8); + frontLegTip = new ModelPart(this, L"frontlegtip"); + frontLegTip->setPos(0, 20, -1); + frontLegTip->addBox(L"main", -3, -1, -3, 6, 24, 6); + frontLeg->addChild(frontLegTip); + frontFoot = new ModelPart(this, L"frontfoot"); + frontFoot->setPos(0, 23, 0); + frontFoot->addBox(L"main", -4, 0, -12, 8, 4, 16); + frontLegTip->addChild(frontFoot); + + rearLeg = new ModelPart(this, L"rearleg"); + rearLeg->setPos(-12 - 4, 16, 2 + 40); + rearLeg->addBox(L"main", -8, -4, -8, 16, 32, 16); + rearLegTip = new ModelPart(this, L"rearlegtip"); + rearLegTip->setPos(0, 32, -4); + rearLegTip->addBox(L"main", -6, -2, 0, 12, 32, 12); + rearLeg->addChild(rearLegTip); + rearFoot = new ModelPart(this, L"rearfoot"); + rearFoot->setPos(0, 31, 4); + rearFoot->addBox(L"main", -9, 0, -20, 18, 6, 24); + rearLegTip->addChild(rearFoot); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + // 4J Stu - Not just performance, but alpha+depth tests don't work right unless we compile here + head->compile(1.0f/16.0f); + jaw->compile(1.0f/16.0f); + neck->compile(1.0f/16.0f); + body->compile(1.0f/16.0f); + wing->compile(1.0f/16.0f); + wingTip->compile(1.0f/16.0f); + frontLeg->compile(1.0f/16.0f); + frontLegTip->compile(1.0f/16.0f); + frontFoot->compile(1.0f/16.0f); + rearLeg->compile(1.0f/16.0f); + rearLegTip->compile(1.0f/16.0f); + rearFoot->compile(1.0f/16.0f); +} + +void DragonModel::prepareMobModel(shared_ptr mob, float time, float r, float a) +{ + this->a = a; +} + +void DragonModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + glPushMatrix(); + shared_ptr dragon = dynamic_pointer_cast(entity); + + float ttt = dragon->oFlapTime + (dragon->flapTime - dragon->oFlapTime) * a; + jaw->xRot = (float) (Mth::sin(ttt * PI * 2) + 1) * 0.2f; + + float yo = (float) (Mth::sin(ttt * PI * 2 - 1) + 1); + yo = (yo * yo * 1 + yo * 2) * 0.05f; + + glTranslatef(0, yo - 2.0f, -3); + glRotatef(yo * 2, 1, 0, 0); + + float yy = -30.0f; + float zz = 22.0f; + float xx = 0.0f; + + float rotScale = 1.5f; + + + double startComponents[3]; + doubleArray start = doubleArray(startComponents,3); + dragon->getLatencyPos(start, 6, a); + + double latencyPosAComponents[3], latencyPosBComponents[3]; + doubleArray latencyPosA = doubleArray( latencyPosAComponents, 3 ); + doubleArray latencyPosB = doubleArray( latencyPosBComponents, 3 ); + dragon->getLatencyPos(latencyPosA, 5, a); + dragon->getLatencyPos(latencyPosB, 10, a); + float rot2 = rotWrap(latencyPosA[0] - latencyPosB[0]); + float rot = rotWrap(latencyPosA[0] + rot2 / 2); + + yy += 2.0f; + + float rr = 0; + float roff = ttt * PI * 2.0f; + yy = 20.0f; + zz = -12.0f; + double pComponents[3]; + doubleArray p = doubleArray(pComponents,3); + + for (int i = 0; i < 5; i++) + { + dragon->getLatencyPos(p, 5 - i, a); + + rr = (float) Mth::cos(i * 0.45f + roff) * 0.15f; + neck->yRot = rotWrap(dragon->getHeadPartYRotDiff(i, start, p)) * PI / 180.0f * rotScale; // 4J replaced "p[0] - start[0] with call to getHeadPartYRotDiff + neck->xRot = rr + (float) (dragon->getHeadPartYOffset(i, start, p)) * PI / 180.0f * rotScale * 5.0f; // 4J replaced "p[1] - start[1]" with call to getHeadPartYOffset + neck->zRot = -rotWrap(p[0] - rot) * PI / 180.0f * rotScale; + + neck->y = yy; + neck->z = zz; + neck->x = xx; + yy += Mth::sin(neck->xRot) * 10.0f; + zz -= Mth::cos(neck->yRot) * Mth::cos(neck->xRot) * 10.0f; + xx -= Mth::sin(neck->yRot) * Mth::cos(neck->xRot) * 10.0f; + neck->render(scale,usecompiled); + } + + head->y = yy; + head->z = zz; + head->x = xx; + dragon->getLatencyPos(p, 0, a); + head->yRot = rotWrap(dragon->getHeadPartYRotDiff(6, start, p)) * PI / 180.0f * 1; // 4J replaced "p[0] - start[0] with call to getHeadPartYRotDiff + head->xRot = (float) (dragon->getHeadPartYOffset(6, start, p)) * PI / 180.0f * rotScale * 5.0f; // 4J Added + head->zRot = -rotWrap(p[0] - rot) * PI / 180 * 1; + head->render(scale,usecompiled); + glPushMatrix(); + glTranslatef(0, 1, 0); + glRotatef(-(float) (rot2) * rotScale * 1, 0, 0, 1); + glTranslatef(0, -1, 0); + body->zRot = 0; + body->render(scale,usecompiled); + + glEnable(GL_CULL_FACE); + for (int i = 0; i < 2; i++) + { + float flapTime = ttt * PI * 2; + wing->xRot = 0.125f - (float) (Mth::cos(flapTime)) * 0.2f; + wing->yRot = 0.25f; + wing->zRot = (float) (Mth::sin(flapTime) + 0.125f) * 0.8f; + wingTip->zRot = -(float) (Mth::sin(flapTime + 2.0f) + 0.5f) * 0.75f; + + rearLeg->xRot = 1.0f + yo * 0.1f; + rearLegTip->xRot = 0.5f + yo * 0.1f; + rearFoot->xRot = 0.75f + yo * 0.1f; + + frontLeg->xRot = 1.3f + yo * 0.1f; + frontLegTip->xRot = -0.5f - yo * 0.1f; + frontFoot->xRot = 0.75f + yo * 0.1f; + wing->render(scale,usecompiled); + frontLeg->render(scale,usecompiled); + rearLeg->render(scale,usecompiled); + glScalef(-1, 1, 1); + if (i == 0) + { + glCullFace(GL_FRONT); + } + } + glPopMatrix(); + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + + rr = -(float) Mth::sin(ttt * PI * 2) * 0.0f; + roff = ttt * PI * 2; + yy = 10; + zz = 60; + xx = 0; + dragon->getLatencyPos(start, 11, a); + for (int i = 0; i < 12; i++) + { + dragon->getLatencyPos(p, 12 + i, a); + rr += Mth::sin(i * 0.45f + roff) * 0.05f; + neck->yRot = (rotWrap(p[0] - start[0]) * rotScale + 180) * PI / 180; + neck->xRot = rr + (float) (p[1] - start[1]) * PI / 180 * rotScale * 5; + neck->zRot = rotWrap(p[0] - rot) * PI / 180 * rotScale; + neck->y = yy; + neck->z = zz; + neck->x = xx; + yy += Mth::sin(neck->xRot) * 10; + zz -= Mth::cos(neck->yRot) * Mth::cos(neck->xRot) * 10; + xx -= Mth::sin(neck->yRot) * Mth::cos(neck->xRot) * 10; + neck->render(scale,usecompiled); + } + glPopMatrix(); +} +float DragonModel::rotWrap(double d) +{ + while (d >= 180) + d -= 360; + while (d < -180) + d += 360; + return (float) d; +} \ No newline at end of file diff --git a/Minecraft.Client/DragonModel.h b/Minecraft.Client/DragonModel.h new file mode 100644 index 0000000..47c20cb --- /dev/null +++ b/Minecraft.Client/DragonModel.h @@ -0,0 +1,35 @@ +#pragma once +#include "Model.h" +#include "ModelPart.h" + +class DragonModel : public Model +{ +public: + static const int MODEL_ID = 4; + +private: + ModelPart *head; + ModelPart *neck; + ModelPart *jaw; + ModelPart *body; + ModelPart *rearLeg; + ModelPart *frontLeg; + ModelPart *rearLegTip; + ModelPart *frontLegTip; + ModelPart *rearFoot; + ModelPart *frontFoot; + ModelPart *wing; + ModelPart *wingTip; + float a; + +public: + + ModelPart *cubes[5]; + DragonModel(float g); + void prepareMobModel(shared_ptr mob, float time, float r, float a); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + +private: + float rotWrap(double d); + +}; \ No newline at end of file diff --git a/Minecraft.Client/DripParticle.cpp b/Minecraft.Client/DripParticle.cpp new file mode 100644 index 0000000..9463976 --- /dev/null +++ b/Minecraft.Client/DripParticle.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.material.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Mth.h" +#include "DripParticle.h" + +DripParticle::DripParticle(Level *level, double x, double y, double z, Material *material) : Particle(level, x, y, z, 0, 0, 0) +{ + xd = yd = zd = 0; + + unsigned int clr; + if (material == Material::water) + { + clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DripWater); + } + else + { + clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DripLavaStart ); + } + + rCol = ( (clr>>16)&0xFF )/255.0f; + gCol = ( (clr>>8)&0xFF )/255.0; + bCol = ( clr&0xFF )/255.0; + + setMiscTex(16 * 7 + 1); + this->setSize(0.01f, 0.01f); + gravity = 0.06f; + this->material = material; + stuckTime = 40; + + lifetime = (int) (64 / (Math::random() * 0.8 + 0.2)); + xd = yd = zd = 0; +} + +int DripParticle::getLightColor(float a) +{ + if (material == Material::water) return Particle::getLightColor(a); + + // 4J-JEV: Looks like this value was never used on the java version, + // but it is on ours, so I've changed this to be bright manualy. + int s = 0x0f; + int b = 0x0f; + return s << 20 | b << 4; // MGH changed this to a proper value as PS3 wasn't clamping the values. +} + +float DripParticle::getBrightness(float a) +{ + if (material == Material::water) return Particle::getBrightness(a); + else return 1.0f; +} + +void DripParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (material == Material::water) + { + //rCol = 0.2f; + //gCol = 0.3f; + //bCol = 1.0f; + + unsigned int clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DripWater); + rCol = ( (clr>>16)&0xFF )/255.0f; + gCol = ( (clr>>8)&0xFF )/255.0; + bCol = ( clr&0xFF )/255.0; + } + else + { + //rCol = 1.0f; + //gCol = 16.0f / (40 - stuckTime + 16); + //bCol = 4.0f / (40 - stuckTime + 8); + + unsigned int cStart = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DripLavaStart ); + unsigned int cEnd = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_DripLavaEnd ); + double rStart = ( (cStart>>16)&0xFF )/255.0f, gStart = ( (cStart>>8)&0xFF )/255.0, bStart = ( cStart&0xFF )/255.0; + double rEnd = ( (cEnd>>16)&0xFF )/255.0f, gEnd = ( (cEnd>>8)&0xFF )/255.0, bEnd = ( cEnd&0xFF )/255.0; + + float variance = (40 - stuckTime); + rCol = rStart - ((rStart - rEnd)/40) * variance; + gCol = gStart - ((gStart - gEnd)/40) * variance; + bCol = bStart - ((bStart - bEnd)/40) * variance; + } + + yd -= gravity; + if (stuckTime-- > 0) + { + xd *= 0.02; + yd *= 0.02; + zd *= 0.02; + setMiscTex(16 * 7 + 1); + } + else + { + setMiscTex(16 * 7 + 0); + } + move(xd, yd, zd); + xd *= 0.98f; + yd *= 0.98f; + zd *= 0.98f; + + if (lifetime-- <= 0) remove(); + + if (onGround) + { + if (material == Material::water) + { + remove(); + level->addParticle(eParticleType_splash, x, y, z, 0, 0, 0); + } + else + { + setMiscTex(16 * 7 + 2); + + } + xd *= 0.7f; + zd *= 0.7f; + } + + Material *m = level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)); + if (m->isLiquid() || m->isSolid()) + { + double y0 = Mth::floor(y) + 1 - LiquidTile::getHeight(level->getData(Mth::floor(x), Mth::floor(y), Mth::floor(z))); + if (y < y0) + { + remove(); + } + } +} diff --git a/Minecraft.Client/DripParticle.h b/Minecraft.Client/DripParticle.h new file mode 100644 index 0000000..1549c33 --- /dev/null +++ b/Minecraft.Client/DripParticle.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Particle.h" + +class Level; +class Material; + +class DripParticle : public Particle +{ +private: + Material *material; + int stuckTime; + +public: + virtual eINSTANCEOF GetType() { return eTYPE_DRIPPARTICLE; } + + DripParticle(Level *level, double x, double y, double z, Material *material); + + virtual int getLightColor(float a); + virtual float getBrightness(float a); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Achievements/AchievementManager.cpp b/Minecraft.Client/Durango/Achievements/AchievementManager.cpp new file mode 100644 index 0000000..b645734 --- /dev/null +++ b/Minecraft.Client/Durango/Achievements/AchievementManager.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "AchievementManager.h" + +using namespace Platform; +using namespace Windows::Data; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::Xbox::System; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Details; +using namespace Microsoft::Xbox::Services; +using namespace Microsoft::Xbox::Services::Achievements; + + +AchievementManager *AchievementManager::m_instance = new AchievementManager(); //Singleton instance of the Achievement Manager \ No newline at end of file diff --git a/Minecraft.Client/Durango/Achievements/AchievementManager.h b/Minecraft.Client/Durango/Achievements/AchievementManager.h new file mode 100644 index 0000000..d891f91 --- /dev/null +++ b/Minecraft.Client/Durango/Achievements/AchievementManager.h @@ -0,0 +1,8 @@ +#pragma once + +class AchievementManager +{ + +private: + static AchievementManager *m_instance; +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/ApplicationView.cpp b/Minecraft.Client/Durango/ApplicationView.cpp new file mode 100644 index 0000000..2af8649 --- /dev/null +++ b/Minecraft.Client/Durango/ApplicationView.cpp @@ -0,0 +1,346 @@ +// +// ApplicationView.cpp - +// + +#include "stdafx.h" +#include "ApplicationView.h" +#include "MinecraftServer.h" + +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::ApplicationModel::Activation; +using namespace Windows::UI::Core; + +ApplicationView::ApplicationView() +{ + m_activationComplete = false; + m_windowClosed = false; + m_inviteProcessed = false; +} + +XALLOC_ATTRIBUTES ExpandAllocAttributes( _In_ LONGLONG dwAttributes ) +{ + XALLOC_ATTRIBUTES attr; + attr = *((XALLOC_ATTRIBUTES *)&dwAttributes); + return attr; +} + +SIZE_T totalTracked = 0; +unordered_map tracker; +static volatile bool memDump = false; +static volatile bool memReset = false; +static bool memDumpInit = false; +static CRITICAL_SECTION memTrackCS; +static volatile SIZE_T memSizeComp = 0; +static long long lastTrackTime = 0; + +PVOID WINAPI XMemAllocLog( _In_ SIZE_T dwSize, _In_ ULONGLONG dwAttributes ) +{ + if( !memDumpInit ) + { + InitializeCriticalSection(&memTrackCS); + memDumpInit = true; + } + + PVOID result = XMemAllocDefault(dwSize,dwAttributes); + XALLOC_ATTRIBUTES expanded_attributes = ExpandAllocAttributes(dwAttributes); + EnterCriticalSection(&memTrackCS); + tracker[(uintptr_t)result] = dwSize; + totalTracked += dwSize; + LeaveCriticalSection(&memTrackCS); +// app.DebugPrintf("XMemAllocLog %p, Size: %d, AllocatorId: 0x%02x, ObjectType: 0x%04x, PageSize: %d, Alignment: %d, MemoryType: %d\n", result, dwSize, expanded_attributes.s.dwAllocatorId, expanded_attributes.s.dwObjectType, expanded_attributes.s.dwPageSize, expanded_attributes.s.dwAlignment, expanded_attributes.s.dwMemoryType ); + return result; +} + +VOID WINAPI XMemFreeLog( _In_ PVOID lpAddress, _In_ ULONGLONG dwAttributes ) +{ + EnterCriticalSection(&memTrackCS); + auto it = tracker.find((uintptr_t)lpAddress); + if( it != tracker.end() ) + { + totalTracked -= it->second; + tracker.erase(it); + } + LeaveCriticalSection(&memTrackCS); + XALLOC_ATTRIBUTES expanded_attributes = ExpandAllocAttributes(dwAttributes); +// app.DebugPrintf("XMemFreeLog %p, AllocatorId: 0x%02x, ObjectType: 0x%04x, PageSize: %d, Alignment: %d, MemoryType: %d\n", lpAddress, expanded_attributes.s.dwAllocatorId, expanded_attributes.s.dwObjectType, expanded_attributes.s.dwPageSize, expanded_attributes.s.dwAlignment, expanded_attributes.s.dwMemoryType ); + XMemFreeDefault(lpAddress,dwAttributes); + + long long currentTime = System::currentTimeMillis(); + if( ( currentTime - lastTrackTime ) > 1000 ) + { + app.DebugPrintf("Total memory tracked: %d\n", totalTracked); + lastTrackTime = currentTime; + } + + if( memReset ) + { + tracker.clear(); + memReset = false; + totalTracked = 0; + } + + if( memDump ) + { + for( auto it = tracker.begin(); it != tracker.end(); it++ ) + { + app.DebugPrintf("0x%x : %d\n",it->first,it->second); + } + memDump = false; + } +} + +// Called by the system. Perform application initialization here, +// hooking application wide events, etc. +void ApplicationView::Initialize(CoreApplicationView^ applicationView) +{ +// XMemSetAllocationHooks( &XMemAllocLog, &XMemFreeLog ); + applicationView->Activated += ref new TypedEventHandler(this, &ApplicationView::OnActivated); + + CoreApplication::Suspending += ref new EventHandler(this, &ApplicationView::OnSuspending); + + CoreApplication::Resuming += ref new EventHandler(this, &ApplicationView::OnResuming); + + CoreApplication::ResourceAvailabilityChanged += ref new EventHandler< Platform::Object^ >( this, &ApplicationView::OnResourceAvailabilityChanged ); +} + +// Called when we are provided a window. +void ApplicationView::SetWindow(CoreWindow^ window) +{ + window->Closed += ref new TypedEventHandler(this, &ApplicationView::OnWindowClosed); +} + +// The purpose of this method is to get the application entry point. +void ApplicationView::Load(Platform::String^ entryPoint) +{ +} + +void InitializeDurango(Windows::UI::Core::CoreWindow^ window); +void oldWinMainInit(); +void oldWinMainTick(); + +// Called by the system after initialization is complete. This +// implements the traditional game loop +void ApplicationView::Run() +{ +// m_game = ref new Game(); + InitializeDurango(CoreWindow::GetForCurrentThread()); + + // Ensure we finish activation and let the system know were responsive + while (!m_activationComplete) + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + } + +#ifndef _CONTENT_PACKAGE + char buf[256]; + sprintf(buf,"0x%llx this\n",this); + OutputDebugStringA(buf); +#endif + oldWinMainInit(); + + + while (!app.getShutdownFlag() && !m_windowClosed) + { + CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + + oldWinMainTick(); + } + + Windows::ApplicationModel::Core::CoreApplication::Exit(); +} + +void ApplicationView::Uninitialize() +{ +} + +// Called when the application is activated. For now, there is just one activation +// kind - Launch. +void ApplicationView::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) +{ + CoreWindow::GetForCurrentThread()->Activate(); + + Windows::Xbox::Multiplayer::PartyConfig::SuppressGameSessionReadyToast = true; + Windows::Xbox::Multiplayer::PartyConfig::SuppressGameSessionReadyToastMode = Windows::Xbox::Multiplayer::SuppressGameSessionReadyToastMode::Always; + + // On starting up the game for the first time, we'll get one kind of onActivated message through. This will also + // be called at other times though, so don't rely on it only happening once for anything + if( !m_activationComplete ) + { + if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Launch) + { + // We get ActivationKind::Launch of we've just been started up from the user selecting us in the system UI, or if we are run normally from the debugger + ILaunchActivatedEventArgs^ newArgs = static_cast< ILaunchActivatedEventArgs^>(args); + + + } + else if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol) + { + // We get Windows::ApplicationModel::Activation::ActivationKind::Protocol if we have been started from accepting a party invite (including joining an open party ourselves), or from a game session being ready + IProtocolActivatedEventArgs^ proArgs = static_cast< IProtocolActivatedEventArgs^>(args); + + Platform::String^ uriType = proArgs->Uri->Host; + if( uriType == L"partyInviteAccept") + { + if( !m_inviteProcessed ) + { + DQRNetworkManager::SetPartyProcessJoinParty(); + DQRNetworkManager::SetInviteReceivedFlag(); + m_inviteProcessed = true; + } + } + else if( uriType == L"gameSessionReady" ) + { + Platform::String^ uriCommand = ref new Platform::String(proArgs->Uri->RawUri->Data()); + auto uri = ref new Windows::Foundation::Uri(uriCommand); + WwwFormUrlDecoder^ query = uri->QueryParsed; + + DQRNetworkManager::SetPartyProcessJoinSession(0,query->GetFirstValueByName(L"sessionName"),query->GetFirstValueByName(L"serviceConfigurationId"),query->GetFirstValueByName(L"sessionTemplateName")); + } + } + + m_activationComplete = true; + } + else + { + // This type of activation protocol can come in at any time during the game. Not sure of the best way to handle both this and gamesessionready events, so for now going to only process these + // (1) once + // (2) until the main menu is reached + // To try and minimise any conflict of them coming in in the middle of the game when we are already handling this gamesessionready. + if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol) + { + if( !app.HasReachedMainMenu() ) + { + app.DebugPrintf("Accepting party invite after initial activation\n"); + // We get Windows::ApplicationModel::Activation::ActivationKind::Protocol if we have been started from accepting a party invite (including joining an open party ourselves), or from a game session being ready + IProtocolActivatedEventArgs^ proArgs = static_cast< IProtocolActivatedEventArgs^>(args); + + Platform::String^ uriType = proArgs->Uri->Host; + if( uriType == L"partyInviteAccept") + { + if( !m_inviteProcessed ) + { + DQRNetworkManager::SetPartyProcessJoinParty(); + DQRNetworkManager::SetInviteReceivedFlag(); + m_inviteProcessed = true; + } + } + } + else + { + DQRNetworkManager::SetInviteReceivedFlag(); // This just informs the network manager that an invite has been received, not to start processing it + } + } + } +} + +// Called when the application is suspending. +void ApplicationView::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) +{ + // TODO: Save game progress using the ConnectedStroage API. + app.DebugPrintf("###################################\n\n\n"); + app.DebugPrintf("Suspending!"); + app.DebugPrintf("\n\n\n###################################"); + + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime; + float fElapsedTime = 0.0f; + QueryPerformanceFrequency( &qwTicksPerSec ); + float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + // Save the start time + QueryPerformanceCounter( &qwTime ); + + // Request deferral + SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral(); + + MinecraftServer *server = MinecraftServer::getInstance(); + if(server) + { + server->Suspend(); + } + + // Start suspend process for libraries + StorageManager.Suspend(); + RenderManager.Suspend(); + + // Wait for libraries to finish suspending + while(!StorageManager.Suspended() || !RenderManager.Suspended()) + { + Sleep(10); + } + + deferral->Complete(); + + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("Entire suspend process: Elapsed time %f\n", fElapsedTime); +} + +// Called when the application is resuming from suspended. +void ApplicationView::OnResuming(Platform::Object^ sender, Platform::Object^ args) +{ + app.DebugPrintf("###################################\n\n\n"); + app.DebugPrintf("Resuming!"); + app.DebugPrintf("\n\n\n###################################"); + + // Start resume process for libraries + RenderManager.Resume(); + InputManager.Resume(); + + // Switch to offline if we were in an online game + if (!g_NetworkManager.IsLocalGame() && g_NetworkManager.IsInGameplay()) + { + // Bit specific but we want to avoid switching to offline if the primary player has signed out + // (and AFAIK there's no other way to find out until a few ticks time) + if (app.GetXuiAction(0) != eAppAction_PrimaryPlayerSignedOut) + { + // We've lost the party so switch to offline, previously we exited the world here but this seems friendlier + app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_EthernetDisconnected); + } + } + + // 4J-PB - clear all DLC info, and recheck, since someone may have suspended after starting a DLCinstall, and we won't have received any install/ license change event + app.ClearDLCInstalled(); + ui.HandleDLCInstalled(ProfileManager.GetPrimaryPad()); + +} + +void ApplicationView::OnResourceAvailabilityChanged( Platform::Object^ /*sender*/, Platform::Object^ /*args*/ ) +{ + app.DebugPrintf("###################################\n\n\n"); + if( Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full ) + { + app.DebugPrintf( "Entered the 'Full Running' state." ); + } + else + { + app.DebugPrintf( "Entered the 'Constrained' PLM state." ); + } + app.DebugPrintf("\n\n\n###################################"); +} + +void ApplicationView::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args) +{ + m_windowClosed = true; +} + +// Implements a IFrameworkView factory. +IFrameworkView^ ApplicationViewSource::CreateView() +{ + return ref new ApplicationView(); +} + +// Application entry point +[Platform::MTAThread] +int main(Platform::Array^) +{ + auto applicationViewSource = ref new ApplicationViewSource(); + + CoreApplication::Run(applicationViewSource); + + return 0; +} diff --git a/Minecraft.Client/Durango/ApplicationView.h b/Minecraft.Client/Durango/ApplicationView.h new file mode 100644 index 0000000..a8b87a4 --- /dev/null +++ b/Minecraft.Client/Durango/ApplicationView.h @@ -0,0 +1,44 @@ +// +// ApplicationView.h +// + +#pragma once + +// Application - implements the required functionality for a application +ref class ApplicationView sealed : public Windows::ApplicationModel::Core::IFrameworkView +{ +public: + + ApplicationView(); + + // IFrameworkView Methods + virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); + virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); + virtual void Load(Platform::String^ entryPoint); + virtual void Run(); + virtual void Uninitialize(); + +protected: + + // Event Handlers + void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); + void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); + void OnResuming(Platform::Object^ sender, Platform::Object^ args); + void OnResourceAvailabilityChanged( Platform::Object^ sender, Platform::Object^ args ); + void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); + +private: + +// Game^ m_game; + bool m_activationComplete; + bool m_windowClosed; + bool m_inviteProcessed; +}; + +// ApplicationSource - responsible for creating the Application instance +// and passing it back to the system +ref class ApplicationViewSource : Windows::ApplicationModel::Core::IFrameworkViewSource +{ +public: + virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(); +}; diff --git a/Minecraft.Client/Durango/Autogenerated.appxmanifest b/Minecraft.Client/Durango/Autogenerated.appxmanifest new file mode 100644 index 0000000..7b45889 --- /dev/null +++ b/Minecraft.Client/Durango/Autogenerated.appxmanifest @@ -0,0 +1,194 @@ + + + + + + + Minecraft: Xbox One Edition + Microsoft Studios + StoreLogo.png + Minecraft + + + + 6.2 + 6.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ESRB:E10 + PEGI:7 + PEGI-PT:6 + FPB:PG + USK:6 + COB-AU:PG + OFLC-NZ:PG + DJCTQ:L + CERO:A + GRB:All + CSRR:P + PCBP:6 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.cpp b/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.cpp new file mode 100644 index 0000000..81b9d7c --- /dev/null +++ b/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" + +#define DURANGO_STUBBED +//printf("missing function on Xbox One : %s\n", __FUNCTION__); + +//static char sc_loadPath[] = {"/"}; + +//const char* getConsoleHomePath() { return sc_loadPath; } + +DWORD GetFileSize(HANDLE fh,DWORD *pdwHigh) +{ + LARGE_INTEGER FileSize; + + BOOL bRes=GetFileSizeEx(fh,&FileSize); + + if(bRes==FALSE) + { + if(pdwHigh) *pdwHigh=0; + return 0; + } + else + { + if(pdwHigh) *pdwHigh=FileSize.HighPart; + return FileSize.LowPart; + } +} + +DWORD XGetLanguage() +{ + //StringTable::eLocale eCurrentLocale=StringTable::eLocale_American; + bool bLocaleFound; + + WCHAR wchLocaleName[LOCALE_NAME_MAX_LENGTH]; + GetUserDefaultLocaleName(wchLocaleName,LOCALE_NAME_MAX_LENGTH); + eMCLang eLang=(eMCLang)app.get_eMCLang(wchLocaleName); + +#ifdef _DEBUG + app.DebugPrintf("XGetLanguage() ==> '%ls'\n", wchLocaleName); +#endif + + // need to map locale to language + switch(eLang) + { + case eMCLang_jaJP: + return XC_LANGUAGE_JAPANESE; + case eMCLang_deDE: + case eMCLang_deAT: + return XC_LANGUAGE_GERMAN; + case eMCLang_frFR: + case eMCLang_frCA: + return XC_LANGUAGE_FRENCH; + case eMCLang_esES: + case eMCLang_esMX: + return XC_LANGUAGE_SPANISH; + case eMCLang_itIT: + return XC_LANGUAGE_ITALIAN; + case eMCLang_koKR: + return XC_LANGUAGE_KOREAN; + case eMCLang_zhHK: + case eMCLang_zhSG: + case eMCLang_zhTW: + case eMCLang_zhCHT: + return XC_LANGUAGE_TCHINESE; + case eMCLang_zhCHS: + case eMCLang_zhCN: + return XC_LANGUAGE_SCHINESE; + case eMCLang_ptPT: + case eMCLang_ptBR: + return XC_LANGUAGE_PORTUGUESE; + case eMCLang_ruRU: + return XC_LANGUAGE_RUSSIAN; + case eMCLang_nlNL: + case eMCLang_nlBE: + return XC_LANGUAGE_DUTCH; + case eMCLang_fiFI: + return XC_LANGUAGE_FINISH; + case eMCLang_svSV: + case eMCLang_svSE: + return XC_LANGUAGE_SWEDISH; + case eMCLang_daDA: + case eMCLang_daDK: + return XC_LANGUAGE_DANISH; + case eMCLang_noNO: + case eMCLang_nnNO: + case eMCLang_nbNO: + return XC_LANGUAGE_BNORWEGIAN; + case eMCLang_plPL: + return XC_LANGUAGE_POLISH; + case eMCLang_trTR: + return XC_LANGUAGE_TURKISH; + case eMCLang_laLAS: + return XC_LANGUAGE_LATINAMERICANSPANISH; + case eMCLang_elEL: + case eMCLang_elGR: + case eMCLang_enGR: // Hack redirect + return XC_LANGUAGE_GREEK; + case eMCLang_csCZ: + case eMCLang_enCZ: // Hack redirect + return XC_LANGUAGE_CZECH; + case eMCLang_skSK: + case eMCLang_enSK: // Hack redirect + return XC_LANGUAGE_SLOVAK; + case eMCLang_enUS: + case eMCLang_enGB: + case eMCLang_enIE: + case eMCLang_enAU: + case eMCLang_enNZ: + case eMCLang_enCA: + default: + return XC_LANGUAGE_ENGLISH; + } +} + +DWORD XGetLocale() +{ + //return XC_LOCALE_SWEDEN; + + WCHAR wchLocaleName[LOCALE_NAME_MAX_LENGTH]; + GetUserDefaultLocaleName(wchLocaleName,LOCALE_NAME_MAX_LENGTH); + + return app.get_xcLang(wchLocaleName); +} + +DWORD XEnableGuestSignin(BOOL fEnable) +{ + return 0; +} + diff --git a/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.h b/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.h new file mode 100644 index 0000000..2c15b70 --- /dev/null +++ b/Minecraft.Client/Durango/DurangoExtras/DurangoStubs.h @@ -0,0 +1,9 @@ +#pragma once + +//const char* getConsoleHomePath(); + +DWORD GetFileSize(HANDLE fh,DWORD *pdwHigh); + +DWORD XGetLanguage(); +DWORD XGetLocale(); +DWORD XEnableGuestSignin(BOOL fEnable); \ No newline at end of file diff --git a/Minecraft.Client/Durango/DurangoExtras/xcompress.h b/Minecraft.Client/Durango/DurangoExtras/xcompress.h new file mode 100644 index 0000000..c577ad0 --- /dev/null +++ b/Minecraft.Client/Durango/DurangoExtras/xcompress.h @@ -0,0 +1,253 @@ +/************************************************************************ +* * +* xcompress.h -- This module defines the Xbox Compression APIs * +* * +* Copyright (c) Microsoft Corp. All rights reserved. * +* * +************************************************************************/ +#pragma once + +#ifndef _XCOMPRESS_H_ +#define _XCOMPRESS_H_ + +#include + +#ifndef XBOXAPI +#define XBOXAPI +#endif + +#ifdef __cplusplus +#define XCTDEXTERN extern "C" +#else +#define XCTDEXTERN extern +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#pragma warning(push) +#pragma warning(disable: 4200) // zero-sized array + +typedef enum _XMEMCODEC_TYPE +{ + XMEMCODEC_DEFAULT = 0, + XMEMCODEC_LZX = 1 +} XMEMCODEC_TYPE; + +/* + * Data compression flags + */ + +#define XMEMCOMPRESS_STREAM 0x00000001 + +/* + * Data compression functions + */ + +typedef VOID* XMEMCOMPRESSION_CONTEXT; + +XBOXAPI +HRESULT +WINAPI +XMemCreateCompressionContext( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags, + __deref_out XMEMCOMPRESSION_CONTEXT* pContext + ); + +XBOXAPI +XMEMCOMPRESSION_CONTEXT +WINAPI +XMemInitializeCompressionContext( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags, + __out_bcount(ContextSize) VOID* pContextData, + __in SIZE_T ContextSize + ); + +XBOXAPI +VOID +WINAPI +XMemDestroyCompressionContext( + __in XMEMCOMPRESSION_CONTEXT Context + ); + +XBOXAPI +SIZE_T +WINAPI +XMemGetCompressionContextSize( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags + ); + +XBOXAPI +HRESULT +WINAPI +XMemResetCompressionContext( + __in XMEMCOMPRESSION_CONTEXT Context); + +XBOXAPI +HRESULT +WINAPI +XMemCompress( + __in XMEMCOMPRESSION_CONTEXT Context, + __out_bcount_opt( *pDestSize ) VOID* pDestination, + __inout SIZE_T* pDestSize, + __in_bcount( SrcSize ) CONST VOID* pSource, + __in SIZE_T SrcSize + ); + +XBOXAPI +HRESULT +WINAPI +XMemCompressStream( + __in XMEMCOMPRESSION_CONTEXT Context, + __out_bcount_opt( *pDestSize ) VOID* pDestination, + __inout SIZE_T* pDestSize, + __in_bcount( *pSrcSize ) CONST VOID* pSource, + __inout SIZE_T* pSrcSize + ); + +/* + * Data decompression functions + */ + +typedef VOID* XMEMDECOMPRESSION_CONTEXT; + +XBOXAPI +HRESULT +WINAPI +XMemCreateDecompressionContext( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags, + __deref_out XMEMDECOMPRESSION_CONTEXT* pContext + ); + +XBOXAPI +XMEMDECOMPRESSION_CONTEXT +WINAPI +XMemInitializeDecompressionContext( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags, + __out_bcount( ContextSize ) VOID* pContextData, + __in SIZE_T ContextSize + ); + +XBOXAPI +VOID +WINAPI +XMemDestroyDecompressionContext( + __in XMEMDECOMPRESSION_CONTEXT Context + ); + +XBOXAPI +SIZE_T +WINAPI +XMemGetDecompressionContextSize( + __in XMEMCODEC_TYPE CodecType, + __in_opt CONST VOID* pCodecParams, + __in DWORD Flags + ); + +XBOXAPI +HRESULT +WINAPI +XMemResetDecompressionContext( + __in XMEMDECOMPRESSION_CONTEXT Context); + +XBOXAPI +HRESULT +WINAPI +XMemDecompress( + __in XMEMDECOMPRESSION_CONTEXT Context, + __out_bcount( *pDestSize ) VOID* pDestination, + __inout SIZE_T* pDestSize, + __in_bcount( SrcSize) CONST VOID* pSource, + __in SIZE_T SrcSize + ); + +XBOXAPI +HRESULT +WINAPI +XMemDecompressStream( + __in XMEMDECOMPRESSION_CONTEXT Context, + __out_bcount( *pDestSize ) VOID* pDestination, + __inout SIZE_T* pDestSize, + __in_bcount( *pSrcSize ) CONST VOID* pSource, + __inout SIZE_T* pSrcSize + ); + +/* + * LZX codec for lossless compression + */ + +typedef struct _XMEMCODEC_PARAMETERS_LZX +{ + DWORD Flags; + DWORD WindowSize; + DWORD CompressionPartitionSize; +} XMEMCODEC_PARAMETERS_LZX; + +#define XCOMPRESS_LZX_BLOCK_SIZE (32 * 1024) +#define XCOMPRESS_LZX_BLOCK_GROWTH_SIZE_MAX 6155 + +/* + * Error codes + */ + +#define XMCDERR_MOREDATA _HRESULT_TYPEDEF_(0x81DE2001) + +/* + * Compression file headers + */ + +#define XCOMPRESS_FILE_IDENTIFIER_LZXTDECODE 0x0FF512ED +#define XCOMPRESS_FILE_IDENTIFIER_LZXNATIVE 0x0FF512EE + +#define XCOMPRESS_SET_FILE_VERSION(Major, Minor) ((((Major) & 0xFF) << 8) | ((Minor) & 0xFF)) +#define XCOMPRESS_GET_FILE_VERSION_MAJOR(Version) (((Version) >> 8) & 0xFF) +#define XCOMPRESS_GET_FILE_VERSION_MINOR(Version) ((Version) & 0xFF) + +#define XCOMPRESS_LZXNATIVE_VERSION_MAJOR 1 +#define XCOMPRESS_LZXNATIVE_VERSION_MINOR 3 + +typedef struct _XCOMPRESS_FILE_HEADER +{ + DWORD Identifier; + WORD Version; + WORD Reserved; +} XCOMPRESS_FILE_HEADER; + +typedef struct _XCOMPRESS_FILE_HEADER_LZXNATIVE +{ + XCOMPRESS_FILE_HEADER Common; + DWORD ContextFlags; + XMEMCODEC_PARAMETERS_LZX CodecParams; + DWORD UncompressedSizeHigh; + DWORD UncompressedSizeLow; + DWORD CompressedSizeHigh; + DWORD CompressedSizeLow; + DWORD UncompressedBlockSize; + DWORD CompressedBlockSizeMax; +} XCOMPRESS_FILE_HEADER_LZXNATIVE; + +typedef struct _XCOMPRESS_BLOCK_HEADER_LZXNATIVE +{ + DWORD CompressedBlockSize; + BYTE pCompressedData[0]; +} XCOMPRESS_BLOCK_HEADER_LZXNATIVE; + +#pragma warning(pop) + +#ifdef __cplusplus +} +#endif + +#endif /* _XCOMPRESS_H_ */ diff --git a/Minecraft.Client/Durango/Durango_App.cpp b/Minecraft.Client/Durango/Durango_App.cpp new file mode 100644 index 0000000..2ec81a2 --- /dev/null +++ b/Minecraft.Client/Durango/Durango_App.cpp @@ -0,0 +1,799 @@ +#include "stdafx.h" +#include "..\Common\Consoles_App.h" +#include "..\User.h" +#include "..\..\Minecraft.Client\Minecraft.h" +#include "..\..\Minecraft.Client\MinecraftServer.h" +#include "..\..\Minecraft.Client\PlayerList.h" +#include "..\..\Minecraft.Client\ServerPlayer.h" +#include "..\..\Minecraft.World\Level.h" +#include "..\..\Minecraft.World\LevelSettings.h" +#include "..\..\Minecraft.World\BiomeSource.h" +#include "..\..\Minecraft.World\LevelType.h" +#include "ServiceConfig\Events-XBLA.8-149E11AEEvents.h" +#include "..\..\Minecraft.World\DurangoStats.h" +#include "..\..\Minecraft.Client\Durango\XML\xmlFilesCallback.h" + +CConsoleMinecraftApp app; + +CConsoleMinecraftApp::CConsoleMinecraftApp() : CMinecraftApp() +{ + memset(&m_ThumbnailBuffer,0,sizeof(ImageFileBuffer)); + m_bShutdown=false; + + m_bRead_TMS_DLCINFO_XML=false; + m_bTMSPP_GlobalFileListRead=false; + m_bTMSPP_UserFileListRead=false; + + for (int i=0; ibTrialLicense==false)) + { + DLCPack *pack = app.m_dlcManager.getPackFromProductID(xOffer.wszProductID); + if(pack) + { + // Clear the DLC installed flag so the scenes will pick up the new dlc (could be a full pack install) + app.ClearDLCInstalled(); + app.DebugPrintf(">>> HandleDLCLicenseChange - Updating license for DLC [%ls]\n",xOffer.wszOfferName); + pack->updateLicenseMask(1); + } + else + { + app.DebugPrintf(">>> HandleDLCLicenseChange - Couldn't find licensed DLC [%ls] in app.m_dlcManager\n",xOffer.wszOfferName); + } + } + } + + ui.HandleDLCLicenseChange(); +} + + +void CConsoleMinecraftApp::SetRichPresenceContext(int iPad, int contextId) +{ + if(iPad < XUSER_MAX_COUNT && Minecraft::GetInstance()->localplayers[iPad]) + { + PlayerUID uid; + ProfileManager.GetXUID(iPad, &uid, true); + + if (uid != INVALID_XUID) + { + // 4J-JEV: Player has changed, update cached player and ensure this next presence is sent. + if (uid != m_xuidLastPresencePlayer[iPad]) + { + m_xuidLastPresencePlayer[iPad] = uid; + m_iLastPresenceContext[iPad] = -1; + } + + if (m_iLastPresenceContext[iPad] != contextId) + { + app.DebugPrintf(">>> EventWriteRichPresenceState(%ls,_,%d)\n", uid.toString().c_str(), contextId); + EventWriteRichPresenceState(uid.toString().c_str(), DurangoStats::getPlayerSession(), contextId); + + m_iLastPresenceContext[iPad] = contextId; + } + } + } +} + +void CConsoleMinecraftApp::StoreLaunchData() +{ +} +void CConsoleMinecraftApp::ExitGame() +{ + //Windows::ApplicationModel::Core::CoreApplication::Exit(); + m_bShutdown=true; +} +void CConsoleMinecraftApp::FatalLoadError() +{ + // 4J-PB - + //for(int i=0;i<10;i++) + { +#ifndef _CONTENT_PACKAGE + OutputDebugStringA("FatalLoadError\n"); +#endif + //Sleep(5000); + } +} + +void CConsoleMinecraftApp::CaptureSaveThumbnail() +{ + RenderManager.CaptureThumbnail(&m_ThumbnailBuffer); +} +void CConsoleMinecraftApp::GetSaveThumbnail(PBYTE *pbData,DWORD *pdwSize) +{ + // on a save caused by a create world, the thumbnail capture won't have happened + if(m_ThumbnailBuffer.Allocated()) + { + if( pbData ) + { + *pbData= new BYTE [m_ThumbnailBuffer.GetBufferSize()]; + *pdwSize=m_ThumbnailBuffer.GetBufferSize(); + memcpy(*pbData,m_ThumbnailBuffer.GetBufferPointer(),*pdwSize); + } + m_ThumbnailBuffer.Release(); + } + else + { + if( pbData ) + { + // use the default image + StorageManager.GetDefaultSaveThumbnail(pbData,pdwSize); + } + } +} +void CConsoleMinecraftApp::ReleaseSaveThumbnail() +{ + +} + +void CConsoleMinecraftApp::GetScreenshot(int iPad,PBYTE *pbData,DWORD *pdwSize) +{ + +} + +int CConsoleMinecraftApp::GetLocalTMSFileIndex(WCHAR *wchTMSFile,bool bFilenameIncludesExtension,eFileExtensionType eEXT) +{ + return -1; +} + + +int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR *wchTMSFile) +{ + return -1; +} + +int CConsoleMinecraftApp::LoadLocalTMSFile(WCHAR *wchTMSFile, eFileExtensionType eExt) +{ + return -1; +} + +void CConsoleMinecraftApp::FreeLocalTMSFiles(eTMSFileType eType) +{ + +} + +int CConsoleMinecraftApp::LoadLocalDLCImages() +{ + unordered_map *pDLCInfoA=app.GetDLCInfo(); + // 4J-PB - Any local graphic files for the Minecraft Store? + for( AUTO_VAR(it, pDLCInfoA->begin()); it != pDLCInfoA->end(); it++ ) + { + DLC_INFO * pDLCInfo=(*it).second; + + LoadLocalDLCImage(pDLCInfo->wchBanner,&pDLCInfo->pbImageData,&pDLCInfo->dwImageBytes); + } + return 0; +} + +void CConsoleMinecraftApp::FreeLocalDLCImages() +{ + // 4J-PB - Any local graphic files for the Minecraft Store? + unordered_map *pDLCInfoA=app.GetDLCInfo(); + + for( AUTO_VAR(it, pDLCInfoA->begin()); it != pDLCInfoA->end(); it++ ) + { + DLC_INFO * pDLCInfo=(*it).second; + + if(pDLCInfo->dwImageBytes!=0) + { + free(pDLCInfo->pbImageData); + pDLCInfo->dwImageBytes=0; + pDLCInfo->pbImageData=NULL; + } + } +} + + +int CConsoleMinecraftApp::LoadLocalDLCImage(WCHAR *wchName,PBYTE *ppbImageData,DWORD *pdwBytes) +{ + // load the local file + WCHAR wchFilename[64]; + + + // 4J-PB - Read the file containing the product codes. This will be different for the SCEE/SCEA/SCEJ builds + swprintf(wchFilename,L"DLCImages/%s",wchName); + HANDLE hFile = CreateFile(wchFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if( hFile == INVALID_HANDLE_VALUE ) + { + app.DebugPrintf("Failed to open %ls\n", wchFilename); + return FALSE; + } + + DWORD dwHigh=0; + *pdwBytes = GetFileSize(hFile,&dwHigh); + + if(*pdwBytes!=0) + { + DWORD dwBytesRead; + PBYTE pbImageData=(PBYTE)malloc(*pdwBytes); + + if(ReadFile(hFile,pbImageData,*pdwBytes,&dwBytesRead,NULL)==FALSE) + { + // failed + free(pbImageData); + *pdwBytes=0; + } + else + { + *ppbImageData=pbImageData; + } + } + + CloseHandle(hFile); + + return 0; +} + +void CConsoleMinecraftApp::TemporaryCreateGameStart() +{ + ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_Main::OnInit + + app.setLevelGenerationOptions(NULL); + + // From CScene_Main::RunPlayGame + Minecraft *pMinecraft=Minecraft::GetInstance(); + app.ReleaseSaveThumbnail(); + ProfileManager.SetLockedProfile(0); + pMinecraft->user->name = L"Durango"; + app.ApplyGameSettingsChanged(0); + + ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameJoinLoad::OnInit + MinecraftServer::resetFlags(); + + // From CScene_MultiGameJoinLoad::OnNotifyPressEx + app.SetTutorialMode( false ); + app.SetCorruptSaveDeleted(false); + + ////////////////////////////////////////////////////////////////////////////////////////////// From CScene_MultiGameCreate::CreateGame + + app.ClearTerrainFeaturePosition(); + wstring wWorldName = L"TestWorld"; + + StorageManager.ResetSaveData(); + StorageManager.SetSaveTitle(wWorldName.c_str()); + + bool isFlat = false; + __int64 seedValue = BiomeSource::findSeed(isFlat?LevelType::lvl_flat:LevelType::lvl_normal); // 4J - was (new Random())->nextLong() - now trying to actually find a seed to suit our requirements + + NetworkGameInitData *param = new NetworkGameInitData(); + param->seed = seedValue; + param->saveData = NULL; + + app.SetGameHostOption(eGameHostOption_Difficulty,0); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends,0); + app.SetGameHostOption(eGameHostOption_Gamertags,1); + app.SetGameHostOption(eGameHostOption_BedrockFog,1); + + app.SetGameHostOption(eGameHostOption_GameType,GameType::SURVIVAL->getId() ); + app.SetGameHostOption(eGameHostOption_LevelType, 0 ); + app.SetGameHostOption(eGameHostOption_Structures, 1 ); + app.SetGameHostOption(eGameHostOption_BonusChest, 0 ); + + app.SetGameHostOption(eGameHostOption_PvP, 1); + app.SetGameHostOption(eGameHostOption_TrustPlayers, 1 ); + app.SetGameHostOption(eGameHostOption_FireSpreads, 1 ); + app.SetGameHostOption(eGameHostOption_TNT, 1 ); + app.SetGameHostOption(eGameHostOption_HostCanFly, 1); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1 ); + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + g_NetworkManager.FakeLocalPlayerJoined(); + + LoadingInputParams *loadingParams = new LoadingInputParams(); + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + loadingParams->lpParam = (LPVOID)param; + + // Reset the autosave time + app.SetAutosaveTimerTime(); + + C4JThread* thread = new C4JThread(loadingParams->func, loadingParams->lpParam, "RunNetworkGame"); + thread->Run(); +} + +typedef struct +{ + eDLCContentType e_DLC_Type; + //WCHAR *wchDisplayName; + WCHAR *wchProductId; + WCHAR *wchBannerName; + WCHAR *wchFirstSkin; + int iConfig; + unsigned int uiSortIndex; +} +DLC_DETAILS; + +#define MAX_DLC_DETAILS 18 +/*DLC_DETAILS DLCDetailsA[MAX_DLC_DETAILS] = +{ + // skin packs + + // Skin Pack Festive + { e_DLC_SkinPack,L"Festive Skin Pack",L"6dffc4d6-a2d2-4c8c-9284-c607f77e431a",L"SPF.png",L"dlcskin00000600.png",0,1}, + // Skin Pack 1 + { e_DLC_SkinPack,L"Skin Pack 1",L"8ecf0f25-a119-4987-a32b-ee0a5925ad8d",L"SP1.png",L"dlcskin00000000.png",0,2}, + // Skin Pack 2 + { e_DLC_SkinPack,L"Skin Pack 2",L"cc59b688-7cfb-4fa0-a76e-84aa55b92cae",L"SP2.png",L"dlcskin00000900.png",0,3}, + // Skin Pack 6 + //{ e_DLC_SkinPack,L"0037a29f-876e-4709-8bb8-a388738e6f51","SP6.png","dlcskin00000900.png",0,3}, + // Skin Pack Battle And Beasts + { e_DLC_SkinPack,L"Battle And Beasts",L"eeeb6489-02a3-4c6e-a8c9-2ace2aa1094d",L"SPC.png",L"dlcskin00000800.png",0,4}, + // Skin Pack Battle And Beasts 2 + { e_DLC_SkinPack,L"Battle And Beasts 2",L"b858200a-59a8-4e1f-a049-f3e73db2d786",L"SPZ.png",L"dlcskin00001000.png",0,5}, + // Skin Pack Marvel Avengers + { e_DLC_SkinPack,L"Marvel Avengers",L"d59757dd-7757-4c5e-8499-dbe4743baa10",L"SPM.png",L"dlcskin00001700.png",0,6}, + // Skin Pack Marvel Spider-Man + { e_DLC_SkinPack,L"Marvel Spider-Man",L"cdca1bff-13d1-435a-8aee-e7a35002473f",L"SPI.png",L"dlcskin00001800.png",0,7}, + // Skin Pack Birthday 1 + { e_DLC_SkinPack,L"Birthday Skin Pack",L"951e8062-3d4e-470c-8177-5eca91bc08b3",L"SPB.png",L"dlcskin00000700.png",0,8}, + // Skin Pack Birthday 2 + { e_DLC_SkinPack,L"2nd Birthday Skin Pack",L"c7157788-468d-4ca8-9ecf-5d77a09850fc",L"SPB2.png",L"dlcskin00002200.png",0,9}, + + // Texture Packs + + // Plastic Texture Pack + { e_DLC_TexturePacks,L"Plastic Texture Pack",L"52ecdcf1-d362-47a1-973b-1eeca0db0ea8",L"TP01.png",L"",2049,1}, + // Natural Texture Pack + { e_DLC_TexturePacks,L"Natural Texture Pack",L"1c56db0c-ff49-4bb1-b372-2122b0e813c1",L"TP02.png",L"",2053,2}, + // Halloween Texture Pack + { e_DLC_TexturePacks,L"Halloween Texture Pack",L"8cb331d1-8fa1-4367-a41a-d4830a80ce67",L"TP03.png",L"",2052,3}, + // Fantasy Texture Pack + { e_DLC_TexturePacks,L"Fantasy Texture Pack",L"375a1df4-5550-415b-b278-20f65b31a7a3",L"TP04.png",L"",2051,4}, + // City Texture Pack + { e_DLC_TexturePacks,L"City Texture Pack",L"ea5c7b40-e04d-4469-9382-8806467ca2c4",L"TP05.png",L"",2054,5}, + // Candy Texture Pack + { e_DLC_TexturePacks,L"Candy Texture Pack",L"94c75e45-0757-4886-916c-ab026ae27ca9",L"TP06.png",L"",2050,6}, + // Comic Texture Pack + //{ e_DLC_TexturePacks,L"Comic Texture Pack",L"3e14cf0f-26eb-40df-897d-7af905456e58",L"TP07.png",L"",2055,7}, + + // Mash-up Packs + + // Mass Effect + { e_DLC_MashupPacks,L"Mass Effect",L"ead4f3bb-b388-42da-8fa9-f1f91570b5c7",L"MPMA.png",L"dlcskin00001100.png",1024,1}, + // Skyrim + { e_DLC_MashupPacks,L"Skyrim",L"81cc4261-7b63-4e48-af1c-60b9ae099644",L"MPSR.png",L"dlcskin00001400.png",1025,2}, + // Halo + { e_DLC_MashupPacks,L"Halo",L"1e06dafc-ea27-475e-945c-fcee0c455f87",L"MPHA.png",L"dlcskin00001600.png",1026,3}, +};*/ + +void CConsoleMinecraftApp::InitialiseDLCDetails() +{ + for(int i=0;i<18;i++) + { + //RegisterDLCData(DLCDetailsA[i].e_DLC_Type, DLCDetailsA[i].wchBannerName, DLCDetailsA[i].wchDisplayName, DLCDetailsA[i].wchProductId, DLCDetailsA[i].wchFirstSkin, DLCDetailsA[i].iConfig, DLCDetailsA[i].uiSortIndex); + } +} + +bool CConsoleMinecraftApp::UpdateProductId(XCONTENT_DATA &Data) +{ + // Do we have a product id for this? + DLC_INFO *pDLCInfo=app.GetDLCInfoForProductName(Data.wszDisplayName); + + if(pDLCInfo!=NULL) + { + app.DebugPrintf("Updating product id for %ls\n",Data.wszDisplayName); + swprintf_s(Data.wszProductID, 64,L"%ls",pDLCInfo->wsProductId.c_str()); + return true; + } + else + { + app.DebugPrintf("Couldn't find %ls\n",Data.wszDisplayName); + } + + return false; +} + +void CConsoleMinecraftApp::Shutdown() +{ + m_bShutdown=true; +} + +bool CConsoleMinecraftApp::getShutdownFlag() +{ + return m_bShutdown; +} + + +// TMS +bool CConsoleMinecraftApp::TMSPP_ReadBannedList(int iPad,eTMSAction NextAction) +{ + app.DebugPrintf("CConsoleMinecraftApp::TMSPP_ReadBannedList\n"); + eTitleStorageState eResult; + bool bSendBanFileRetrievedMsg=false; + + if(GetBanListRead(iPad)==false) + { + // Attempt to read the ban list + // do we have one in our user filelist? + //if(StorageManager.TMSPP_InFileList(C4JStorage::eGlobalStorage_TitleUser,iPad,L"BannedList")) + { + SetBanListRead(iPad,true); + ClearBanList(iPad); + + eResult=StorageManager.TMSPP_ReadFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"BannedList",&CConsoleMinecraftApp::Callback_TMSPPReadBannedList,this,NextAction); + if(eResult!=eTitleStorage_pending) + { + // something went wrong + app.SetTMSAction(iPad,(eTMSAction)NextAction); + bSendBanFileRetrievedMsg=true; + } + } + } + else + { + bSendBanFileRetrievedMsg=true; + } + + if(bSendBanFileRetrievedMsg) + { + ui.HandleTMSBanFileRetrieved(iPad); + } + + app.SetTMSAction(iPad,(eTMSAction)NextAction); + + return true; +} + +int CConsoleMinecraftApp::Callback_TMSPPReadBannedList(void *pParam,int iPad, int iUserData, LPVOID lpvData,WCHAR *wchFilename) +{ + app.DebugPrintf("CConsoleMinecraftApp::Callback_TMSPPReadBannedList\n"); + C4JStorage::PTMSPP_FILEDATA pFileData=(C4JStorage::PTMSPP_FILEDATA)lpvData; + + CConsoleMinecraftApp* pClass = (CConsoleMinecraftApp*)pParam; + + if(pFileData) + { + // put the entries into the vector + int iEntries=pFileData->dwSize/sizeof(BANNEDLISTDATA); + PBANNEDLISTDATA pData=(PBANNEDLISTDATA)pFileData->pbData; + + for(int i=0;iAddLevelToBannedLevelList(iPad,&pData[i], false); + } + // mark the level as not checked against banned levels - it'll be checked once the level starts + app.SetBanListCheck(iPad,false); + + // Xbox One will clear things within the DownloadBlob +#ifndef _XBOX_ONE + delete [] pFileData->pbData; + delete [] pFileData; +#endif + + ui.HandleTMSBanFileRetrieved(iPad); + } + else + { + // read problem - set state to idel again + StorageManager.TMSPP_ClearTitleStorageState(iPad); + } + + // change the state to the next action + pClass->SetTMSAction(iPad,(eTMSAction)iUserData); + + return 0; +} + +void CConsoleMinecraftApp::TMSPP_ReadDLCFile(int iPad,eTMSAction NextAction) +{ + app.DebugPrintf("CConsoleMinecraftApp::TMSPP_ReadDLCFile\n"); + bool bRetrievedDLCFile=false; + // try reading the DLC.xml file (from TMS global) - only allowed to do this once an hour at the most, but we'll just read once each time the game launches + eTitleStorageState eResult; + if(m_bRead_TMS_DLCINFO_XML==false) + { +// 4J-PB - we're reading this info from a local file now + + eResult=StorageManager.TMSPP_ReadFile(iPad,C4JStorage::eGlobalStorage_Title,C4JStorage::TMS_FILETYPE_BINARY,L"DLCXbox1.cmp",&CConsoleMinecraftApp::Callback_TMSPPReadDLCFile,this,NextAction); + if(eResult!=eTitleStorage_pending) + { + // something went wrong + app.SetTMSAction(iPad,(eTMSAction)NextAction); + bRetrievedDLCFile=true; + m_bRead_TMS_DLCINFO_XML=true; + } + } + else + { + bRetrievedDLCFile=true; + } + + if(bRetrievedDLCFile) + { + ui.HandleTMSDLCFileRetrieved(iPad); + + app.SetTMSAction(iPad,(eTMSAction)NextAction); + } +} + +void CConsoleMinecraftApp::TMSPP_RetrieveFileList(int iPad,C4JStorage::eGlobalStorage eStorageFacility,eTMSAction NextAction) +{ + app.DebugPrintf("CConsoleMinecraftApp::TMSPP_RetrieveFileList\n"); + + if(eStorageFacility==C4JStorage::eGlobalStorage_Title) + { + if(m_bTMSPP_GlobalFileListRead==false) + { + m_bTMSPP_GlobalFileListRead=true; + StorageManager.TMSPP_ReadFileList(iPad,eStorageFacility,&CConsoleMinecraftApp::Callback_TMSPPRetrieveFileList,this,NextAction); + } + else + { + SetTMSAction(iPad,NextAction); + } + } + else + { + if(m_bTMSPP_UserFileListRead==false) + { + m_bTMSPP_UserFileListRead=true; + StorageManager.TMSPP_ReadFileList(iPad,eStorageFacility,&CConsoleMinecraftApp::Callback_TMSPPRetrieveFileList,this,NextAction); + } + else + { + SetTMSAction(iPad,NextAction); + } + } +} + +int CConsoleMinecraftApp::Callback_TMSPPRetrieveFileList(void *pParam,int iPad, int iUserData, LPVOID lpvData, WCHAR *wchFilename) +{ + CConsoleMinecraftApp* pClass = (CConsoleMinecraftApp*)pParam; + app.DebugPrintf("CConsoleMinecraftApp::Callback_TMSPPRetrieveFileList\n"); + if(lpvData!=NULL) + { + vector *pvTmsFileDetails=(vector *)lpvData; + + if(pvTmsFileDetails->size()>0) + { + #ifdef _DEBUG + // dump out the file list + app.DebugPrintf("TMSPP filecount - %d\nFiles - \n",pvTmsFileDetails->size()); + int iCount=0; + AUTO_VAR(itEnd, pvTmsFileDetails->end()); + for( AUTO_VAR(it, pvTmsFileDetails->begin()); it != itEnd; it++ ) + { + C4JStorage::PTMSPP_FILE_DETAILS fd = *it; + app.DebugPrintf("%2d. %ls (size - %d)\n",iCount++,fd->wchFilename,fd->ulFileSize); + } + + #endif + } + } + // change the state to the next action + pClass->SetTMSAction(iPad,(eTMSAction)iUserData); + return 0; +} + +//#define WRITE_DLCINFO 1 +int CConsoleMinecraftApp::Callback_TMSPPReadDLCFile(void *pParam,int iPad, int iUserData, LPVOID lpvData ,WCHAR *pwchFilename) +{ + app.DebugPrintf("CConsoleMinecraftApp::Callback_TMSPPReadDLCFile\n"); + C4JStorage::PTMSPP_FILEDATA pFileData= (C4JStorage::PTMSPP_FILEDATA)lpvData; + CConsoleMinecraftApp* pClass = (CConsoleMinecraftApp*)pParam; + +#ifdef WRITE_DLCINFO + if(0) +#else + if(pFileData && pFileData->dwSize>0) +#endif + { + // the DLC.xml file is now compressed + + unsigned int uiDecompSize=*(unsigned int *)pFileData->pbData; + unsigned int uiCompSize=((unsigned int *)pFileData->pbData)[1]; + + BYTE *pDecompressedData = new BYTE [uiDecompSize]; + + Compression::ECompressionTypes eOriginalCompressionType=Compression::getCompression()->GetDecompressionType(); + Compression::getCompression()->SetDecompressionType(Compression::eCompressionType_LZXRLE); + Compression::getCompression()->Decompress(pDecompressedData,&uiDecompSize,&((unsigned int *)pFileData->pbData)[2],uiCompSize); + Compression::getCompression()->SetDecompressionType(eOriginalCompressionType); + + ATG::XMLParser xmlParser; + xmlDLCInfoCallback xmlCallback; + + xmlParser.RegisterSAXCallbackInterface( &xmlCallback ); + xmlParser.ParseXMLBuffer((CHAR *)pDecompressedData,uiDecompSize); + + pClass->m_bRead_TMS_DLCINFO_XML=true; + + delete pDecompressedData; + + // apply the dlc info to the locally installed DLC + StorageManager.UpdateDLCProductIDs(); + + ui.HandleTMSDLCFileRetrieved(iPad); + } + else + { + + // if there was a read error, reset to idle + StorageManager.TMSPP_ClearTitleStorageState(iPad); + +#ifdef WRITE_DLCINFO + HANDLE file; + DWORD dwHigh=0; + DWORD dwFileSize; + + // hack for now to upload the file + // open the local file + file = CreateFile(L"DLCXbox1.cmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if( file == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + app.DebugPrintf("Failed to open DLCXbox1.cmp with error code %d (%x)\n", error, error); + __debugbreak(); + return 0; + } + + dwHigh=0; + dwFileSize = GetFileSize(file,&dwHigh); + + if(dwFileSize!=0) + { + DWORD bytesRead; + + PBYTE pbData= new BYTE [dwFileSize]; + + ReadFile(file,pbData,dwFileSize,&bytesRead,NULL); + + if(bytesRead==dwFileSize) + { + //StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"DLCXbox1.cmp",(PBYTE) pbData, dwFileSize,NULL,NULL, 0); + StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"TP06.cmp",(PBYTE) pbData, dwFileSize,NULL,NULL, 0); + } + Sleep(5000); + } + + CloseHandle(file); + + /* + // now the icon + file = CreateFile(L"TP06.png", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if( file == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + app.DebugPrintf("Failed to open DLCXbox1.cmp with error code %d (%x)\n", error, error); + return 0; + } + + dwHigh=0; + dwFileSize = GetFileSize(file,&dwHigh); + + if(dwFileSize!=0) + { + DWORD bytesRead; + + PBYTE pbData= new BYTE [dwFileSize]; + + ReadFile(file,pbData,dwFileSize,&bytesRead,NULL); + + if(bytesRead==dwFileSize) + { + StorageManager.TMSPP_WriteFile(iPad,C4JStorage::eGlobalStorage_TitleUser,C4JStorage::TMS_FILETYPE_BINARY,L"TP06.png",(PBYTE) pbData, dwFileSize,NULL,NULL, 0); + } + Sleep(5000); + } + + CloseHandle(file);*/ +#endif + } + + // change the state to the next action + pClass->SetTMSAction(iPad,(eTMSAction)iUserData); + + return 0; +} + +void CConsoleMinecraftApp::Callback_SaveGameIncomplete(void *pParam, C4JStorage::ESaveIncompleteType saveIncompleteType) +{ + CConsoleMinecraftApp* pClass = (CConsoleMinecraftApp*)pParam; + + if ( saveIncompleteType == C4JStorage::ESaveIncomplete_OutOfQuota + || saveIncompleteType == C4JStorage::ESaveIncomplete_OutOfLocalStorage ) + { + StorageManager.SetSaveDisabled(true); + pClass->EnterSaveNotificationSection(); + + int message; + if (saveIncompleteType == C4JStorage::ESaveIncomplete_OutOfQuota) message = IDS_SAVE_INCOMPLETE_EXPLANATION_QUOTA; + else message = IDS_SAVE_INCOMPLETE_EXPLANATION_LOCAL_STORAGE; + + UINT uiIDA[3] = + { + IDS_SAVE_INCOMPLETE_RETRY_SAVING, + IDS_SAVE_INCOMPLETE_DISABLE_SAVING, + IDS_SAVE_INCOMPLETE_DELETE_SAVES + }; + + if ( ui.RequestMessageBox( IDS_SAVE_INCOMPLETE_TITLE, message, uiIDA,3,0,Callback_SaveGameIncompleteMessageBoxReturned,pClass, app.GetStringTable()) == C4JStorage::EMessage_Busy) + { + // If this failed to display, continue as if we cancelled. This isn't ideal, but the user should already have had some system notification of being out of memory, + // and if we instantly retry then they may not be able to navigate whatever other error is blocking this from appearing + Callback_SaveGameIncompleteMessageBoxReturned(pParam, 0, C4JStorage::EMessage_Cancelled); + } + } + else + { + // 4J-JEV: Unknown error, just cancel the operation. + Callback_SaveGameIncompleteMessageBoxReturned(pParam, 0, C4JStorage::EMessage_Cancelled); + } +} + +int CConsoleMinecraftApp::Callback_SaveGameIncompleteMessageBoxReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) +{ + CConsoleMinecraftApp* pClass = (CConsoleMinecraftApp*)pParam; + + switch(result) + { + case C4JStorage::EMessage_ResultAccept: + pClass->LeaveSaveNotificationSection(); + StorageManager.SetSaveDisabled(false); + StorageManager.ContinueIncompleteOperation(); + break; + case C4JStorage::EMessage_ResultDecline: + case C4JStorage::EMessage_Cancelled: + pClass->LeaveSaveNotificationSection(); + // Set the global flag, so that we don't disable saving again once the message box is complete + app.SetGameHostOption(eGameHostOption_DisableSaving, 1); + StorageManager.CancelIncompleteOperation(); + break; + case C4JStorage::EMessage_ResultThirdOption: + ui.NavigateToScene(iPad, eUIScene_InGameSaveManagementMenu, NULL, eUILayer_Error, eUIGroup_Fullscreen); + break; + } + return 0; +} + +void CConsoleMinecraftApp::ReadLocalDLCList(void) +{ + char szFile[255]; + DWORD dwLength; + // read the local dlc list + File fDLCList(L"CU/DLCXbox1.cmp") ; + if(fDLCList.exists()) + { + dwLength = fDLCList.length(); + byteArray data(dwLength); + + FileInputStream fis(fDLCList); + fis.read(data,0,dwLength); + fis.close(); + + unsigned int uiDecompSize=*(unsigned int *)data.data; + unsigned int uiCompSize=((unsigned int *)data.data)[1]; + + BYTE *pDecompressedData = new BYTE [uiDecompSize]; + + Compression::ECompressionTypes eOriginalCompressionType=Compression::getCompression()->GetDecompressionType(); + Compression::getCompression()->SetDecompressionType(Compression::eCompressionType_LZXRLE); + Compression::getCompression()->Decompress(pDecompressedData,&uiDecompSize,&((unsigned int *)data.data)[2],uiCompSize); + Compression::getCompression()->SetDecompressionType(eOriginalCompressionType); + + ATG::XMLParser xmlParser; + xmlDLCInfoCallback xmlCallback; + + xmlParser.RegisterSAXCallbackInterface( &xmlCallback ); + xmlParser.ParseXMLBuffer((CHAR *)pDecompressedData,uiDecompSize); + + delete pDecompressedData; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Durango_App.h b/Minecraft.Client/Durango/Durango_App.h new file mode 100644 index 0000000..6e02123 --- /dev/null +++ b/Minecraft.Client/Durango/Durango_App.h @@ -0,0 +1,87 @@ +#pragma once + +#define SERVICE_CONFIG_ID L"05c20100-6e60-45d5-878a-4903149e11ae" +#define TITLE_PRODUCT_ID L"582e7bcc-11bc-4702-ab1b-b31566f8e327" // Parent Title's ProductID + + +class CConsoleMinecraftApp : public CMinecraftApp +{ + +public: + ImageFileBuffer m_ThumbnailBuffer; + + CConsoleMinecraftApp(); + +private: + int m_iLastPresenceContext[MAX_LOCAL_PLAYERS]; + PlayerUID m_xuidLastPresencePlayer[MAX_LOCAL_PLAYERS]; +public: + virtual void SetRichPresenceContext(int iPad, int contextId); + + virtual void StoreLaunchData(); + virtual void ExitGame(); + virtual void FatalLoadError(); + + virtual void CaptureSaveThumbnail(); + virtual void GetSaveThumbnail(PBYTE*,DWORD*); + virtual void ReleaseSaveThumbnail(); + virtual void GetScreenshot(int iPad,PBYTE *pbData,DWORD *pdwSize); + + virtual int LoadLocalTMSFile(WCHAR *wchTMSFile); + virtual int LoadLocalTMSFile(WCHAR *wchTMSFile, eFileExtensionType eExt); + int LoadLocalDLCImage(WCHAR *wchName,PBYTE *ppbImageData,DWORD *pdwBytes); + int LoadLocalDLCImages(); + void FreeLocalDLCImages(); + + virtual void FreeLocalTMSFiles(eTMSFileType eType); + virtual int GetLocalTMSFileIndex(WCHAR *wchTMSFile,bool bFilenameIncludesExtension,eFileExtensionType eEXT=eFileExtensionType_PNG); + + // BANNED LEVEL LIST + virtual void ReadBannedList(int iPad, eTMSAction action=(eTMSAction)0, bool bCallback=false) {} + + // TMS++ + void TMSPP_RetrieveFileList(int iPad,C4JStorage::eGlobalStorage eStorageFacility,eTMSAction NextAction); +// void TMSPP_ReadXuidsFile(int iPad,eTMSAction NextAction); +// void TMSPP_ReadConfigFile(int iPad,eTMSAction NextAction); + void TMSPP_ReadDLCFile(int iPad,eTMSAction NextAction); + bool TMSPP_ReadBannedList(int iPad,eTMSAction NextAction); + + static int Callback_TMSPPRetrieveFileList(void *pParam,int iPad, int iUserData, LPVOID lpvData,WCHAR *wchFilename); +// static int Callback_TMSPPReadXuidsFile(void *pParam,int iPad, int iUserData, C4JStorage::PTMSPP_FILEDATA pFileData,LPCSTR szFilename); +// static int Callback_TMSPPReadConfigFile(void *pParam,int iPad, int iUserData, C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename); + static int Callback_TMSPPReadDLCFile(void *pParam,int iPad, int iUserData, LPVOID lpvData,WCHAR *wchFilename); + static int Callback_TMSPPReadBannedList(void *pParam,int iPad, int iUserData, LPVOID lpvData,WCHAR *wchFilename); + virtual bool GetTMSDLCInfoRead() { return m_bRead_TMS_DLCINFO_XML;} + virtual bool GetTMSGlobalFileListRead() { return m_bTMSPP_GlobalFileListRead;} + virtual bool GetTMSUserFileListRead() { return m_bTMSPP_UserFileListRead;} + + static void Callback_SaveGameIncomplete(void *pParam, C4JStorage::ESaveIncompleteType saveIncompleteType); + static int Callback_SaveGameIncompleteMessageBoxReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + + C4JStringTable *GetStringTable() { return NULL;} + + // original code + virtual void TemporaryCreateGameStart(); + + void InitialiseDLCDetails(); + static bool UpdateProductId(XCONTENT_DATA &Data); + + void Shutdown(); + bool getShutdownFlag(); + + void ReadLocalDLCList(void); + static void HandleDLCLicenseChange(); + +private: + DLC_INFO *m_DLCDetailsA; + bool m_bShutdown; + + bool m_bRead_TMS_DLCINFO_XML; // track whether we have already read the TMS DLC.xml file + bool m_bTMSPP_GlobalFileListRead; // track whether we have already read the file list from TMSPP + bool m_bTMSPP_UserFileListRead; // track whether we have already read the file list from TMSPP + +}; + +extern CConsoleMinecraftApp app; + + diff --git a/Minecraft.Client/Durango/Durango_Minecraft.cpp b/Minecraft.Client/Durango/Durango_Minecraft.cpp new file mode 100644 index 0000000..48d5c31 --- /dev/null +++ b/Minecraft.Client/Durango/Durango_Minecraft.cpp @@ -0,0 +1,1173 @@ +// Minecraft.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" + +#include +#include "..\MinecraftServer.h" +#include "..\LocalPlayer.h" +#include "..\..\Minecraft.World\ItemInstance.h" +#include "..\..\Minecraft.World\MapItem.h" +#include "..\..\Minecraft.World\Recipes.h" +#include "..\..\Minecraft.World\Recipy.h" +#include "..\..\Minecraft.World\Language.h" +#include "..\..\Minecraft.World\StringHelpers.h" +#include "..\..\Minecraft.World\AABB.h" +#include "..\..\Minecraft.World\Vec3.h" +#include "..\..\Minecraft.World\Level.h" +#include "..\..\Minecraft.World\net.minecraft.world.level.tile.h" + +#include "Leaderboards\GameProgress.h" +#include "..\ClientConnection.h" +#include "..\User.h" +#include "..\..\Minecraft.World\Socket.h" +#include "..\..\Minecraft.World\ThreadName.h" +#include "..\..\Minecraft.Client\StatsCounter.h" +#include "..\ConnectScreen.h" +#include "Leaderboards\DurangoLeaderboardManager.h" +#include "..\..\Minecraft.Client\Tesselator.h" +#include "..\..\Minecraft.Client\Options.h" +#include "Sentient\SentientManager.h" +#include "..\..\Minecraft.World\IntCache.h" +#include "..\Textures.h" +#include "Resource.h" +#include "..\..\Minecraft.World\compression.h" +#include "..\..\Minecraft.World\OldChunkStorage.h" +#include + +//using namespace Windows::Xbox::Input; +using namespace Windows::Foundation::Collections; + +#define THEME_NAME "584111F70AAAAAAA" +#define THEME_FILESIZE 2797568 + +//#define THREE_MB 3145728 // minimum save size (checking for this on a selected device) +//#define FIVE_MB 5242880 // minimum save size (checking for this on a selected device) +//#define FIFTY_TWO_MB (1024*1024*52) // Maximum TCR space required for a save (checking for this on a selected device) +#define FIFTY_ONE_MB (1000000*51) // Maximum TCR space required for a save is 52MB (checking for this on a selected device) + +//#define PROFILE_VERSION 3 // new version for the interim bug fix 166 TU +#define NUM_PROFILE_VALUES 5 +#define NUM_PROFILE_SETTINGS 4 +DWORD dwProfileSettingsA[NUM_PROFILE_VALUES]= +{ +#ifdef _XBOX + XPROFILE_OPTION_CONTROLLER_VIBRATION, + XPROFILE_GAMER_YAXIS_INVERSION, + XPROFILE_GAMER_CONTROL_SENSITIVITY, + XPROFILE_GAMER_ACTION_MOVEMENT_CONTROL, + XPROFILE_TITLE_SPECIFIC1, +#else + 0,0,0,0,0 +#endif +}; + +//------------------------------------------------------------------------------------- +// Time Since fAppTime is a float, we need to keep the quadword app time +// as a LARGE_INTEGER so that we don't lose precision after running +// for a long time. +//------------------------------------------------------------------------------------- + + +BOOL g_bWidescreen = TRUE; + + +void DefineActions(void) +{ + // The app needs to define the actions required, and the possible mappings for these + + // Split into Menu actions, and in-game actions + + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_A, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_B, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_X, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_Y, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OK, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_CANCEL, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_UP, _360_JOY_BUTTON_DPAD_UP | _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_DOWN, _360_JOY_BUTTON_DPAD_DOWN | _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_LEFT, _360_JOY_BUTTON_DPAD_LEFT | _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT | _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_PAGEUP, _360_JOY_BUTTON_LT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_PAGEDOWN, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_RIGHT_SCROLL, _360_JOY_BUTTON_RB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_LEFT_SCROLL, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_GTC_PAUSE, _360_GTC_PAUSE); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_GTC_RESUME, _360_GTC_PLAY); + + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_STICK_PRESS, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OTHER_STICK_PRESS, _360_JOY_BUTTON_RTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OTHER_STICK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OTHER_STICK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OTHER_STICK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,ACTION_MENU_OTHER_STICK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_JUMP, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_FORWARD, _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_BACKWARD, _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LEFT, _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_RIGHT, _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LOOK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LOOK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LOOK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LOOK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_USE, _360_JOY_BUTTON_LT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_ACTION, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_RIGHT_SCROLL, _360_JOY_BUTTON_RB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_LEFT_SCROLL, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_INVENTORY, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_DROP, _360_JOY_BUTTON_B); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_SNEAK_TOGGLE, _360_JOY_BUTTON_RTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_CRAFTING, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_RENDER_THIRD_PERSON, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_GAME_INFO, _360_JOY_BUTTON_BACK | _360_GTC_VIEW); + + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_DPAD_LEFT, _360_JOY_BUTTON_DPAD_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_DPAD_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_DPAD_UP, _360_JOY_BUTTON_DPAD_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_0,MINECRAFT_ACTION_DPAD_DOWN, _360_JOY_BUTTON_DPAD_DOWN); + + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_A, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_B, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_X, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_Y, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OK, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_CANCEL, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_UP, _360_JOY_BUTTON_DPAD_UP | _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_DOWN, _360_JOY_BUTTON_DPAD_DOWN | _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_LEFT, _360_JOY_BUTTON_DPAD_LEFT | _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT | _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_PAGEUP, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_PAGEDOWN, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_RIGHT_SCROLL, _360_JOY_BUTTON_RB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_LEFT_SCROLL, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_GTC_PAUSE, _360_GTC_PAUSE); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_GTC_RESUME, _360_GTC_PLAY); + + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_STICK_PRESS, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OTHER_STICK_PRESS, _360_JOY_BUTTON_RTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OTHER_STICK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OTHER_STICK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OTHER_STICK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,ACTION_MENU_OTHER_STICK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_JUMP, _360_JOY_BUTTON_RB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_FORWARD, _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_BACKWARD, _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LEFT, _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_RIGHT, _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LOOK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LOOK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LOOK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LOOK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_USE, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_ACTION, _360_JOY_BUTTON_LT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_RIGHT_SCROLL, _360_JOY_BUTTON_DPAD_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_LEFT_SCROLL, _360_JOY_BUTTON_DPAD_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_INVENTORY, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_DROP, _360_JOY_BUTTON_B); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_SNEAK_TOGGLE, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_CRAFTING, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_RENDER_THIRD_PERSON, _360_JOY_BUTTON_RTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_GAME_INFO, _360_JOY_BUTTON_BACK | _360_GTC_VIEW); + + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_DPAD_LEFT, _360_JOY_BUTTON_DPAD_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_DPAD_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_DPAD_UP, _360_JOY_BUTTON_DPAD_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_1,MINECRAFT_ACTION_DPAD_DOWN, _360_JOY_BUTTON_DPAD_DOWN); + + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_A, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_B, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_X, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_Y, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OK, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_CANCEL, _360_JOY_BUTTON_B | _360_GTC_BACK); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_UP, _360_JOY_BUTTON_DPAD_UP | _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_DOWN, _360_JOY_BUTTON_DPAD_DOWN | _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_LEFT, _360_JOY_BUTTON_DPAD_LEFT | _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT | _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_PAGEUP, _360_JOY_BUTTON_DPAD_UP | _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_PAGEDOWN, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_RIGHT_SCROLL, _360_JOY_BUTTON_RB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_LEFT_SCROLL, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_STICK_PRESS, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OTHER_STICK_PRESS, _360_JOY_BUTTON_RTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OTHER_STICK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OTHER_STICK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OTHER_STICK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_OTHER_STICK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_GTC_PAUSE, _360_GTC_PAUSE); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,ACTION_MENU_GTC_RESUME, _360_GTC_PLAY); + + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_JUMP, _360_JOY_BUTTON_LT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_FORWARD, _360_JOY_BUTTON_LSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_BACKWARD, _360_JOY_BUTTON_LSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LEFT, _360_JOY_BUTTON_LSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_RIGHT, _360_JOY_BUTTON_LSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LOOK_LEFT, _360_JOY_BUTTON_RSTICK_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LOOK_RIGHT, _360_JOY_BUTTON_RSTICK_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LOOK_UP, _360_JOY_BUTTON_RSTICK_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LOOK_DOWN, _360_JOY_BUTTON_RSTICK_DOWN); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_USE, _360_JOY_BUTTON_RT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_ACTION, _360_JOY_BUTTON_A); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_RIGHT_SCROLL, _360_JOY_BUTTON_DPAD_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_LEFT_SCROLL, _360_JOY_BUTTON_DPAD_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_INVENTORY, _360_JOY_BUTTON_Y); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_PAUSEMENU, _360_JOY_BUTTON_START | _360_GTC_MENU); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_DROP, _360_JOY_BUTTON_B); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_SNEAK_TOGGLE, _360_JOY_BUTTON_LB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_CRAFTING, _360_JOY_BUTTON_X); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_RENDER_THIRD_PERSON, _360_JOY_BUTTON_LTHUMB); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_GAME_INFO, _360_JOY_BUTTON_BACK | _360_GTC_VIEW); + + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_DPAD_LEFT, _360_JOY_BUTTON_DPAD_LEFT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_DPAD_RIGHT, _360_JOY_BUTTON_DPAD_RIGHT); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_DPAD_UP, _360_JOY_BUTTON_DPAD_UP); + InputManager.SetGameJoypadMaps(MAP_STYLE_2,MINECRAFT_ACTION_DPAD_DOWN, _360_JOY_BUTTON_DPAD_DOWN); +} + +#if 0 +HRESULT InitD3D( IDirect3DDevice9 **ppDevice, + D3DPRESENT_PARAMETERS *pd3dPP ) +{ + IDirect3D9 *pD3D; + + pD3D = Direct3DCreate9( D3D_SDK_VERSION ); + + // Set up the structure used to create the D3DDevice + // Using a permanent 1280x720 backbuffer now no matter what the actual video resolution.right Have also disabled letterboxing, + // which would letterbox a 1280x720 output if it detected a 4:3 video source - we're doing an anamorphic squash in this + // mode so don't need this functionality. + + ZeroMemory( pd3dPP, sizeof(D3DPRESENT_PARAMETERS) ); + XVIDEO_MODE VideoMode; + XGetVideoMode( &VideoMode ); + g_bWidescreen = VideoMode.fIsWideScreen; + pd3dPP->BackBufferWidth = 1280; + pd3dPP->BackBufferHeight = 720; + pd3dPP->BackBufferFormat = D3DFMT_A8R8G8B8; + pd3dPP->BackBufferCount = 1; + pd3dPP->EnableAutoDepthStencil = TRUE; + pd3dPP->AutoDepthStencilFormat = D3DFMT_D24S8; + pd3dPP->SwapEffect = D3DSWAPEFFECT_DISCARD; + pd3dPP->PresentationInterval = D3DPRESENT_INTERVAL_ONE; + //pd3dPP->Flags = D3DPRESENTFLAG_NO_LETTERBOX; + //ERR[D3D]: Can't set D3DPRESENTFLAG_NO_LETTERBOX when wide-screen is enabled + // in the launcher/dashboard. + if(g_bWidescreen) + pd3dPP->Flags=0; + else + pd3dPP->Flags = D3DPRESENTFLAG_NO_LETTERBOX; + + // Create the device. + return pD3D->CreateDevice( + 0, + D3DDEVTYPE_HAL, + NULL, + D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_BUFFER_2_FRAMES, + pd3dPP, + ppDevice ); +} +#endif +//#define MEMORY_TRACKING + +#ifdef MEMORY_TRACKING +void ResetMem(); +void DumpMem(); +void MemPixStuff(); +#else +void MemSect(int sect) +{ +} +#endif + + +HINSTANCE g_hInst = NULL; + +Platform::Agile g_window; +Windows::Foundation::Rect g_windowBounds; + +D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; +D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; +Microsoft::WRL::ComPtr g_d3dDevice; +Microsoft::WRL::ComPtr g_d3dContext; +Microsoft::WRL::ComPtr g_swapChain; +Microsoft::WRL::ComPtr g_renderTargetView; +Microsoft::WRL::ComPtr g_depthStencilView; +Microsoft::WRL::ComPtr g_depthStencil; + +void CreateDevice(); +void CreateResources(); +void InitializeDurango(Windows::UI::Core::CoreWindow^ window) +{ + SetThreadAffinityMask(GetCurrentThread(),1); + + g_window = window; + + CreateDevice(); + + CreateResources(); + + // TODO: Begin asynchronous loading of game assets. + + C4JThread::StaticInit(); +} + +namespace DX +{ + inline void ThrowIfFailed(HRESULT hr) + { + if (FAILED(hr)) + { + // Set a breakpoint on this line to catch DirectX API errors + throw Platform::Exception::CreateException(hr); + } + } +} + +void CreateDevice() +{ + // This flag adds support for surfaces with a different color channel ordering than the API default. + // It is recommended usage, and is required for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_FAST_KICKOFFS; +#ifndef _CONTENT_PACKAGE + creationFlags |= D3D11_CREATE_DEVICE_VALIDATED; + creationFlags |= D3D11_CREATE_DEVICE_INSTRUMENTED; +#endif + + // This array defines the set of DirectX hardware feature levels this app will support. + // Note the ordering should be preserved. + // Don't forget to declare your application's minimum required feature level in its + // description. All applications are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_0 + }; + + // Create the DX11 API device object, and get a corresponding context. + Microsoft::WRL::ComPtr device; + Microsoft::WRL::ComPtr context; + + DX::ThrowIfFailed( + D3D11CreateDevice( + nullptr, // specify null to use the default adapter + D3D_DRIVER_TYPE_HARDWARE, + nullptr, // leave as nullptr unless software device + creationFlags, // optionally set debug and Direct2D compatibility flags + featureLevels, // list of feature levels this app can support + ARRAYSIZE(featureLevels), // number of entries in above list + D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION + &device, // returns the Direct3D device created + &g_featureLevel, // returns feature level of device created + &context // returns the device immediate context + ) + ); + + // Get the DirectX11.1 device by QI off the DirectX11 one. + DX::ThrowIfFailed(device.As(&g_d3dDevice)); + + // And get the corresponding device context in the same way. + DX::ThrowIfFailed(context.As(&g_d3dContext)); +} + +// Allocate all memory resources that change on a window SizeChanged event. +void CreateResources() +{ + // Store the window bounds so the next time we get a SizeChanged event we can + // avoid rebuilding everything if the size is identical. + g_windowBounds = g_window.Get()->Bounds; + + // If the swap chain already exists, resize it, + // otherwise create one. + if(g_swapChain != nullptr) + { + DX::ThrowIfFailed(g_swapChain->ResizeBuffers(2, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)); + } + else + { + // First, retrieve the underlying DXGI Device from the D3D Device + Microsoft::WRL::ComPtr dxgiDevice; + DX::ThrowIfFailed(g_d3dDevice.As(&dxgiDevice)); + + // Identify the physical adapter (GPU or card) this device is running on. + Microsoft::WRL::ComPtr dxgiAdapter; + DX::ThrowIfFailed(dxgiDevice->GetAdapter(&dxgiAdapter)); + + // And obtain the factory object that created it. + Microsoft::WRL::ComPtr dxgiFactory; + DX::ThrowIfFailed(dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory)); + + // Create a descriptor for the swap chain. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0}; + + swapChainDesc.Width = 1920; + swapChainDesc.Height = 1080; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT; + swapChainDesc.BufferCount = 2; + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + swapChainDesc.Flags = 0; + + // Create a SwapChain from a CoreWindow. + DX::ThrowIfFailed(dxgiFactory->CreateSwapChainForCoreWindow(g_d3dDevice.Get(), reinterpret_cast(g_window.Get()), &swapChainDesc, nullptr, &g_swapChain)); + } + + // Obtain the backbuffer for this window which will be the final 3D rendertarget. + Microsoft::WRL::ComPtr backBuffer; + DX::ThrowIfFailed(g_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), &backBuffer)); + + // Create a view interface on the rendertarget to use on bind. + DX::ThrowIfFailed(g_d3dDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, &g_renderTargetView)); + + // Cache the rendertarget dimensions in our helper class for convenient use. + D3D11_TEXTURE2D_DESC backBufferDesc = {0}; + backBuffer->GetDesc(&backBufferDesc); + + // Allocate a 2-D surface as the depth/stencil buffer and + // create a DepthStencil view on this surface to use on bind. + CD3D11_TEXTURE2D_DESC depthStencilDesc(DXGI_FORMAT_D24_UNORM_S8_UINT, backBufferDesc.Width, backBufferDesc.Height, 1, 1, D3D11_BIND_DEPTH_STENCIL); + + Microsoft::WRL::ComPtr depthStencil; + DX::ThrowIfFailed(g_d3dDevice->CreateTexture2D(&depthStencilDesc, nullptr, &depthStencil)); + + CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); + DX::ThrowIfFailed(g_d3dDevice->CreateDepthStencilView(depthStencil.Get(), &depthStencilViewDesc, &g_depthStencilView)); + + // Create a viewport descriptor of the full window size. + CD3D11_VIEWPORT viewPort(0.0f, 0.0f, static_cast(backBufferDesc.Width), static_cast(backBufferDesc.Height)); + + // Set the current viewport using the descriptor. + g_d3dContext->RSSetViewports(1, &viewPort); + g_d3dContext->OMSetRenderTargets( 1, g_renderTargetView.GetAddressOf(), g_depthStencilView.Get() ); +} + +//-------------------------------------------------------------------------------------- +// Render the frame +//-------------------------------------------------------------------------------------- +void Render() +{ + // Just clear the backbuffer + float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha + + g_d3dContext->ClearRenderTargetView( g_renderTargetView.Get(), ClearColor ); + g_swapChain->Present( 0, 0 ); +} + + +void oldWinMainInit() +{ +#if 0 + // Main message loop + MSG msg = {0}; + while( WM_QUIT != msg.message ) + { + if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + else + { + Render(); + } + } + + return (int) msg.wParam; +#endif + + +#ifdef MEMORY_TRACKING + ResetMem(); + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + printf("RESETMEM start: Avail. phys %d\n",memStat.dwAvailPhys/(1024*1024)); +#endif + +#if 0 + // Initialize D3D + hr = InitD3D( &pDevice, &d3dpp ); + g_pD3DDevice = pDevice; + if( FAILED(hr) ) + { + app.DebugPrintf + ( "Failed initializing D3D.\n" ); + return -1; + } + + // Initialize the application, assuming sharing of the d3d interface. + hr = app.InitShared( pDevice, &d3dpp, + XuiPNGTextureLoader ); + + if ( FAILED(hr) ) + { + app.DebugPrintf + ( "Failed initializing application.\n" ); + + return -1; + } + + +#endif + + app.loadMediaArchive(); + + RenderManager.Initialise(g_d3dDevice.Get(), g_swapChain.Get()); + + app.loadStringTable(); + + ui.init(g_d3dDevice,g_d3dContext,g_renderTargetView,g_depthStencilView,1920,1080); + + //////////////// + // Initialise // + //////////////// + +#if 0 + // 4J Stu - XACT was creating these automatically, but we need them for QNet. The setup params + // are just copied from a sample app and may need changed for our purposes + // Start XAudio2 + hr = XAudio2Create( &g_pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ); + if( FAILED( hr ) ) + { + app.DebugPrintf( "Initializing XAudio2 failed (err = 0x%08x)!\n", hr ); + app.FatalLoadError(); + } + + // Create an XAudio2 mastering voice (utilized by XHV2 when voice data is mixed to main speakers) + hr = g_pXAudio2->CreateMasteringVoice(&g_pXAudio2MasteringVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL); + if ( FAILED( hr ) ) + { + app.DebugPrintf( "Creating XAudio2 mastering voice failed (err = 0x%08x)!\n", hr ); + app.FatalLoadError(); + } +#endif + app.InitTime(); + + // Set the number of possible joypad layouts that the user can switch between, and the number of actions + InputManager.Initialise(1,3,MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); + + // Set the default joypad action mappings for Minecraft + DefineActions(); + InputManager.SetJoypadMapVal(0,0); + InputManager.SetKeyRepeatRate(0.3f,0.2f); + + // looks like the Durango controller is a good bit more sensitive than XBOX360/PS3. Defaults in the lib are 10000 for deadzone and 32767 for movement range + InputManager.SetDeadzoneAndMovementRange(10000,20000,32767); + + // Initialise the profile manager with the game Title ID, Offer ID, a profile version number, and the number of profile values and settings + ProfileManager.Initialise(SERVICE_CONFIG_ID, TITLE_PRODUCT_ID); + + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_IDLE,L"Idle"); + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_MENUS,L"InMenus"); + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_MULTIPLAYER,L"PlayingMultiplayer"); + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,L"PlayingMultiplayerOffline"); + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_MULTIPLAYER_1P,L"PlayingAlone"); + ProfileManager.RegisterPresence(CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,L"PlayingAloneOffline"); + + // Fix for XboxOne #165025 - XR-049: Compliance: Title does not display Rich Presence strings for profiles in main menu. + ProfileManager.SetGameActivityForAllActiveUsers(CONTEXT_PRESENCE_IDLE); + + Compression::CreateNewThreadStorage(); + app.ReadLocalDLCList(); + + // initialise the storage manager with a default save display name, a Minimum save size, and a callback for displaying the saving message + StorageManager.Init(0,app.GetString(IDS_DEFAULT_SAVENAME),"savegame.dat",FIFTY_ONE_MB,&CConsoleMinecraftApp::DisplaySavingMessage,(LPVOID)&app, app.UpdateProductId,SERVICE_CONFIG_ID,TITLE_PRODUCT_ID); + + StorageManager.SetMaxSaves(99); + + byteArray baSaveThumbnail = app.getArchiveFile(L"DefaultSaveThumbnail64x64.png"); + + StorageManager.InitialiseProfileData(PROFILE_VERSION_BUILD_JUNE14, + NUM_PROFILE_VALUES, + NUM_PROFILE_SETTINGS, + dwProfileSettingsA, + app.GAME_DEFINED_PROFILE_DATA_BYTES*XUSER_MAX_COUNT, + &app.uiGameDefinedDataChangedBitmask); + + StorageManager.SetDefaultImages((PBYTE)baSaveThumbnail.data, baSaveThumbnail.length); + + // Set function to be called if a save game operation can't complete due to running out of storage space etc. + StorageManager.SetIncompleteSaveCallback(CConsoleMinecraftApp::Callback_SaveGameIncomplete, (LPVOID)&app); + + // set a function to be called when there's a sign in change, so we can exit a level if the primary player signs out + ProfileManager.SetSignInChangeCallback(&CConsoleMinecraftApp::SignInChangeCallback,(LPVOID)&app); + + // Set a callback for the default player options to be set - when there is no profile data for the player + StorageManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback,(LPVOID)&app); + StorageManager.SetOptionsDataCallback(&CConsoleMinecraftApp::OptionsDataCallback,(LPVOID)&app); + + + // Set a callback to deal with old profile versions needing updated to new versions + StorageManager.SetOldProfileVersionCallback(&CConsoleMinecraftApp::OldProfileVersionCallback,(LPVOID)&app); + + g_NetworkManager.Initialise(); + + + // Initialise TLS for tesselator, for this main thread + Tesselator::CreateNewThreadStorage(1024*1024); + // Initialise TLS for AABB and Vec3 pools, for this main thread + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + OldChunkStorage::CreateNewThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + //4J-PB - after the init, which will have read all the local DLC, update the product ids by reading the local dlc file + StorageManager.UpdateDLCProductIDs(); + + Minecraft::main(); + Minecraft *pMinecraft=Minecraft::GetInstance(); + app.InitGameSettings(); + + // read the options here for controller 0 + // 4J-PB - we have no valid user to read a profile for on Xbox One, and there's no EULA anyway + //StorageManager.ReadFromProfile(0); + + app.InitialiseDLCDetails(); + StorageManager.SetLicenseChangeFn(&app.HandleDLCLicenseChange); + + + // debug switch to trial version + ProfileManager.SetDebugFullOverride(true); + + // set default values for controllers +// for(int i=0;i<4;i++) +// { +// //app.SetDefaultOptions(app.GetGameDefinedProfileData(i),i); +// app.SetDefaultOptions(ProfileManager.GetGameDefinedProfileData(i),i); +// } + + +#if 0 + //bool bDisplayPauseMenu=false; + + // set the default gamma level + float fVal=50.0f*327.68f; + RenderManager.UpdateGamma((unsigned short)fVal); + + // load any skins + //app.AddSkinsToMemoryTextureFiles(); + + // set the achievement text for a trial achievement, now we have the string table loaded + ProfileManager.SetTrialTextStringTable(app.GetStringTable(),IDS_CONFIRM_OK, IDS_CONFIRM_CANCEL); + ProfileManager.SetTrialAwardText(eAwardType_Achievement,IDS_UNLOCK_TITLE,IDS_UNLOCK_ACHIEVEMENT_TEXT); + ProfileManager.SetTrialAwardText(eAwardType_GamerPic,IDS_UNLOCK_TITLE,IDS_UNLOCK_GAMERPIC_TEXT); + ProfileManager.SetTrialAwardText(eAwardType_AvatarItem,IDS_UNLOCK_TITLE,IDS_UNLOCK_AVATAR_TEXT); + ProfileManager.SetTrialAwardText(eAwardType_Theme,IDS_UNLOCK_TITLE,IDS_UNLOCK_THEME_TEXT); + ProfileManager.SetUpsellCallback(&app.UpsellReturnedCallback,&app); + + // Set up a debug character press sequence +#ifndef _FINAL_BUILD + app.SetDebugSequence("LRLRYYY"); +#endif + + // Initialise the social networking manager. + CSocialManager::Instance()->Initialise(); + + // Update the base scene quick selects now that the minecraft class exists + //CXuiSceneBase::UpdateScreenSettings(0); +#endif + app.InitialiseTips(); +#if 0 + + DWORD initData=0; + + + +#ifndef _FINAL_BUILD +#ifndef _DEBUG + #pragma message(__LOC__"Need to define the _FINAL_BUILD before submission") +#endif +#endif + + // Set the default sound levels + pMinecraft->options->set(Options::Option::MUSIC,1.0f); + pMinecraft->options->set(Options::Option::SOUND,1.0f); + + app.NavigateToScene(XUSER_INDEX_ANY,eUIScene_Intro,&initData); +#endif + + + + //app.TemporaryCreateGameStart(); + + //Sleep(10000); +#if 0 + // Intro loop ? + while(app.IntroRunning()) + { + ProfileManager.Tick(); + // Tick XUI + app.RunFrame(); + + // 4J : WESTY : Added to ensure we always have clear background for intro. + RenderManager.SetClearColour(D3DCOLOR_RGBA(0,0,0,255)); + RenderManager.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Render XUI + hr = app.Render(); + + // Present the frame. + RenderManager.Present(); + + // Update XUI Timers + hr = XuiTimersRun(); + } +#endif +} +void oldWinMainTick() +{ + static bool bTrialTimerDisplayed=true; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + RenderManager.StartFrame(); +#if 0 + if(pMinecraft->soundEngine->isStreamingWavebankReady() && + !pMinecraft->soundEngine->isPlayingStreamingGameMusic() && + !pMinecraft->soundEngine->isPlayingStreamingCDMusic() ) + { + // play some music in the menus + pMinecraft->soundEngine->playStreaming(L"", 0, 0, 0, 0, 0, false); + } +#endif + app.UpdateTime(); + PIXBeginNamedEvent(0,"Input manager tick"); + InputManager.Tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Profile manager tick"); + ProfileManager.Tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Storage manager tick"); + StorageManager.Tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Render manager tick"); + RenderManager.Tick(); + PIXEndNamedEvent(); + + // Tick the social networking manager. + PIXBeginNamedEvent(0,"Social network manager tick"); +// CSocialManager::Instance()->Tick(); + PIXEndNamedEvent(); + + // Tick sentient. + PIXBeginNamedEvent(0,"Sentient tick"); + MemSect(37); +// SentientManager.Tick(); + MemSect(0); + PIXEndNamedEvent(); + + GameProgress::Tick(); + + PIXBeginNamedEvent(PIX_COLOR_INDEX(2),"Network manager do work #1"); + g_NetworkManager.DoWork(); + PIXEndNamedEvent(); + + LeaderboardManager::Instance()->Tick(); + // Render game graphics. + if(app.GetGameStarted()) + { + pMinecraft->run_middle(); + app.SetAppPaused( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad()) ); + } + else + { + MemSect(28); + pMinecraft->soundEngine->tick(NULL, 0.0f); + MemSect(0); + pMinecraft->textures->tick(true,false); + IntCache::Reset(); + if( app.GetReallyChangingSessionType() ) + { + pMinecraft->tickAllConnections(); // Added to stop timing out when we are waiting after converting to an offline game + } + } + + pMinecraft->soundEngine->playMusicTick(); + + +#ifdef MEMORY_TRACKING + static bool bResetMemTrack = false; + static bool bDumpMemTrack = false; + + MemPixStuff(); + + if( bResetMemTrack ) + { + ResetMem(); + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + printf("RESETMEM: Avail. phys %d\n",memStat.dwAvailPhys/(1024*1024)); + bResetMemTrack = false; + } + + if( bDumpMemTrack ) + { + DumpMem(); + bDumpMemTrack = false; + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + printf("DUMPMEM: Avail. phys %d\n",memStat.dwAvailPhys/(1024*1024)); + printf("Renderer used: %d\n",RenderManager.CBuffSize(-1)); + } +#endif +#if 0 + static bool bDumpTextureUsage = false; + if( bDumpTextureUsage ) + { + RenderManager.TextureGetStats(); + bDumpTextureUsage = false; + } +#endif + ui.tick(); + ui.render(); +#if 0 + app.HandleButtonPresses(); + + // store the minecraft renderstates, and re-set them after the xui render + GetRenderAndSamplerStates(pDevice,RenderStateA,SamplerStateA); + + // Tick XUI + PIXBeginNamedEvent(0,"Xui running"); + app.RunFrame(); + PIXEndNamedEvent(); + + // Render XUI + + PIXBeginNamedEvent(0,"XUI render"); + MemSect(7); + hr = app.Render(); + MemSect(0); + GetRenderAndSamplerStates(pDevice,RenderStateA2,SamplerStateA2); + PIXEndNamedEvent(); + + for(int i=0;i<8;i++) + { + if(RenderStateA2[i]!=RenderStateA[i]) + { + //printf("Reseting RenderStateA[%d] after a XUI render\n",i); + pDevice->SetRenderState(RenderStateModes[i],RenderStateA[i]); + } + } + for(int i=0;i<5;i++) + { + if(SamplerStateA2[i]!=SamplerStateA[i]) + { + //printf("Reseting SamplerStateA[%d] after a XUI render\n",i); + pDevice->SetSamplerState(0,SamplerStateModes[i],SamplerStateA[i]); + } + } + + RenderManager.Set_matrixDirty(); +#endif + +#if 0 // ndef _CONTENT_PACKAGE + if( InputManager.ButtonPressed(0,MINECRAFT_ACTION_DPAD_LEFT) || + InputManager.ButtonPressed(0,ACTION_MENU_STICK_PRESS)) + { + RenderManager.DoScreenGrabOnNextPresent(); + } +#endif + + // Present the frame. + RenderManager.Present(); + + ui.CheckMenuDisplayed(); + + PIXBeginNamedEvent(0,"Profile load check"); + // has the game defined profile data been changed (by a profile load) + if(app.uiGameDefinedDataChangedBitmask!=0) + { + void *pData; + for(int i=0;istats[ i ]->clear(); + pMinecraft->stats[i]->parse(pData); + } + } + +#if 0 + // Check to see if we can post to social networks. + CSocialManager::Instance()->RefreshPostingCapability(); +#endif + + // clear the flag + app.uiGameDefinedDataChangedBitmask=0; + + // Check if any profile write are needed + app.CheckGameSettingsChanged(); + + } + PIXEndNamedEvent(); + app.TickTMSPPFilesRetrieved(); + + app.TickDLCOffersRetrieved(); + + PIXBeginNamedEvent(0,"Network manager do work #2"); + // 4J Stu - Added this back as it was causing horrendous lag (and eventually a crash on a client) when playing with multiple clients + g_NetworkManager.DoWork(); // TODO - consider putting this back in. Removed because we are having trouble with how long the tick is taking on Durango + PIXEndNamedEvent(); + +#if 0 + PIXBeginNamedEvent(0,"Misc extra xui"); + // Update XUI Timers + hr = XuiTimersRun(); +#endif + // Any threading type things to deal with from the xui side? + app.HandleXuiActions(); +#if 0 + PIXEndNamedEvent(); +#endif + + // 4J-PB - Update the trial timer display if we are in the trial version + if(!ProfileManager.IsFullVersion()) + { + // display the trial timer + if(app.GetGameStarted()) + { + // 4J-PB - if the game is paused, add the elapsed time to the trial timer count so it doesn't tick down + if(app.IsAppPaused()) + { + app.UpdateTrialPausedTimer(); + } + ui.UpdateTrialTimer(ProfileManager.GetPrimaryPad()); + } + } + else + { + // need to turn off the trial timer if it was on , and we've unlocked the full version + if(bTrialTimerDisplayed) + { + ui.ShowTrialTimer(false); + bTrialTimerDisplayed=false; + } + } + + // Fix for #7318 - Title crashes after short soak in the leaderboards menu + // A memory leak was caused because the icon renderer kept creating new Vec3's because the pool wasn't reset + Vec3::resetPool(); +} + +#ifdef MEMORY_TRACKING + +int totalAllocGen = 0; +unordered_map allocCounts; +bool trackEnable = false; +bool trackStarted = false; +volatile size_t sizeCheckMin = 1160; +volatile size_t sizeCheckMax = 1160; +volatile int sectCheck = 48; +CRITICAL_SECTION memCS; +DWORD tlsIdx; + +LPVOID XMemAlloc(SIZE_T dwSize, DWORD dwAllocAttributes) +{ + if( !trackStarted ) + { + void *p = XMemAllocDefault(dwSize,dwAllocAttributes); + size_t realSize = XMemSizeDefault(p, dwAllocAttributes); + totalAllocGen += realSize; + return p; + } + + EnterCriticalSection(&memCS); + + void *p=XMemAllocDefault(dwSize + 16,dwAllocAttributes); + size_t realSize = XMemSizeDefault(p,dwAllocAttributes) - 16; + + if( trackEnable ) + { +#if 1 + int sect = ((int) TlsGetValue(tlsIdx)) & 0x3f; + *(((unsigned char *)p)+realSize) = sect; + + if( ( realSize >= sizeCheckMin ) && ( realSize <= sizeCheckMax ) && ( ( sect == sectCheck ) || ( sectCheck == -1 ) ) ) + { + app.DebugPrintf("Found one\n"); + } +#endif + + if( p ) + { + totalAllocGen += realSize; + trackEnable = false; + int key = ( sect << 26 ) | realSize; + int oldCount = allocCounts[key]; + allocCounts[key] = oldCount + 1; + + trackEnable = true; + } + } + + LeaveCriticalSection(&memCS); + + return p; +} + +void* operator new (size_t size) +{ + return (unsigned char *)XMemAlloc(size,MAKE_XALLOC_ATTRIBUTES(0,FALSE,TRUE,FALSE,0,XALLOC_PHYSICAL_ALIGNMENT_DEFAULT,XALLOC_MEMPROTECT_READWRITE,FALSE,XALLOC_MEMTYPE_HEAP)); +} + +void operator delete (void *p) +{ + XMemFree(p,MAKE_XALLOC_ATTRIBUTES(0,FALSE,TRUE,FALSE,0,XALLOC_PHYSICAL_ALIGNMENT_DEFAULT,XALLOC_MEMPROTECT_READWRITE,FALSE,XALLOC_MEMTYPE_HEAP)); +} + +void WINAPI XMemFree(PVOID pAddress, DWORD dwAllocAttributes) +{ + bool special = false; + if( dwAllocAttributes == 0 ) + { + dwAllocAttributes = MAKE_XALLOC_ATTRIBUTES(0,FALSE,TRUE,FALSE,0,XALLOC_PHYSICAL_ALIGNMENT_DEFAULT,XALLOC_MEMPROTECT_READWRITE,FALSE,XALLOC_MEMTYPE_HEAP); + special = true; + } + if(!trackStarted ) + { + size_t realSize = XMemSizeDefault(pAddress, dwAllocAttributes); + XMemFreeDefault(pAddress, dwAllocAttributes); + totalAllocGen -= realSize; + return; + } + EnterCriticalSection(&memCS); + if( pAddress ) + { + size_t realSize = XMemSizeDefault(pAddress, dwAllocAttributes) - 16; + + if(trackEnable) + { + int sect = *(((unsigned char *)pAddress)+realSize); + totalAllocGen -= realSize; + trackEnable = false; + int key = ( sect << 26 ) | realSize; + int oldCount = allocCounts[key]; + allocCounts[key] = oldCount - 1; + trackEnable = true; + + } + XMemFreeDefault(pAddress, dwAllocAttributes); + } + LeaveCriticalSection(&memCS); +} + +SIZE_T WINAPI XMemSize( + PVOID pAddress, + DWORD dwAllocAttributes +) +{ + if( trackStarted ) + { + return XMemSizeDefault(pAddress, dwAllocAttributes) - 16; + } + else + { + return XMemSizeDefault(pAddress, dwAllocAttributes); + } +} + + +void DumpMem() +{ + int totalLeak = 0; + for(AUTO_VAR(it, allocCounts.begin()); it != allocCounts.end(); it++ ) + { + if(it->second > 0 ) + { + app.DebugPrintf("%d %d %d %d\n",( it->first >> 26 ) & 0x3f,it->first & 0x03ffffff, it->second, (it->first & 0x03ffffff) * it->second); + totalLeak += ( it->first & 0x03ffffff ) * it->second; + } + } + app.DebugPrintf("Total %d\n",totalLeak); +} + +void ResetMem() +{ + if( !trackStarted ) + { + trackEnable = true; + trackStarted = true; + totalAllocGen = 0; + InitializeCriticalSection(&memCS); + tlsIdx = TlsAlloc(); + } + EnterCriticalSection(&memCS); + trackEnable = false; + allocCounts.clear(); + trackEnable = true; + LeaveCriticalSection(&memCS); +} + +void MemSect(int section) +{ + unsigned int value = (unsigned int)TlsGetValue(tlsIdx); + if( section == 0 ) // pop + { + value = (value >> 6) & 0x03ffffff; + } + else + { + value = (value << 6) | section; + } + TlsSetValue(tlsIdx, (LPVOID)value); +} + +void MemPixStuff() +{ + const int MAX_SECT = 46; + + int totals[MAX_SECT] = {0}; + + for(AUTO_VAR(it, allocCounts.begin()); it != allocCounts.end(); it++ ) + { + if(it->second > 0 ) + { + int sect = ( it->first >> 26 ) & 0x3f; + int bytes = it->first & 0x03ffffff; + totals[sect] += bytes * it->second; + } + } + + unsigned int allSectsTotal = 0; + for( int i = 0; i < MAX_SECT; i++ ) + { + allSectsTotal += totals[i]; + PIXAddNamedCounter(((float)totals[i])/1024.0f,"MemSect%d",i); + } + + PIXAddNamedCounter(((float)allSectsTotal)/(4096.0f),"MemSect total pages"); +} + +#endif diff --git a/Minecraft.Client/Durango/Durango_UIController.cpp b/Minecraft.Client/Durango/Durango_UIController.cpp new file mode 100644 index 0000000..446ab87 --- /dev/null +++ b/Minecraft.Client/Durango/Durango_UIController.cpp @@ -0,0 +1,195 @@ +#include "stdafx.h" +#include "Durango_UIController.h" + +#define _ENABLEIGGY + +ConsoleUIController ui; + +void ConsoleUIController::init(Microsoft::WRL::ComPtr dev, Microsoft::WRL::ComPtr ctx, Microsoft::WRL::ComPtr pRenderTargetView, Microsoft::WRL::ComPtr pDepthStencilView, S32 w, S32 h) +{ +#ifdef _ENABLEIGGY + m_pRenderTargetView = pRenderTargetView; + m_pDepthStencilView = pDepthStencilView; + + // Shared init + preInit(w,h); + + gdraw_funcs = gdraw_D3D11_CreateContext(dev.Get(), ctx.Get(), w, h); + + if(!gdraw_funcs) + { + app.DebugPrintf("Failed to initialise GDraw!\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + } + + /* For each of the resource types, we specify the size of the cache that + GDraw will use. We specify both the number of possible objects + (the number of "handles") of each type, and the maximum memory + to use for each one. + + For some platforms, we would actually pass + in the memory to use, and the GDraw will strictly obey the resource + request. For D3D, storage is managed by D3D, and GDraw only + approximates the requested storage amount. In fact, you don't + even have to set these at all for D3D, which has some "reasonable" defaults, + but we'll set it here for clarity. + (The storage required for + the handles is separate, and always allocated through the global allocator + specified in IggyInit.) + + The size that's actually needed here depends on the content of your + Flash file. There's more info in the documentation about how to + determine how big they should be. But for now, we'll just set them + really big so if you substitute a different file it should work. */ + gdraw_D3D11_SetResourceLimits(GDRAW_D3D11_RESOURCE_vertexbuffer, 5000, 16 * 1024 * 1024); + gdraw_D3D11_SetResourceLimits(GDRAW_D3D11_RESOURCE_texture , 5000, 128 * 1024 * 1024); + gdraw_D3D11_SetResourceLimits(GDRAW_D3D11_RESOURCE_rendertarget, 10, 32 * 1024 * 1024); + gdraw_D3D11_SetResourceLimits(GDRAW_D3D11_RESOURCE_dynbuffer , 1, 2 * 1024 * 1024); + + /* GDraw is all set, so we'll point Iggy at it. */ + IggySetGDraw(gdraw_funcs); + + // Initialize audio + // TODO: 4J Stu - Currently Iggy crashes if I have audio enabled. Disabling for now. + //IggyAudioUseDefault(); + + // Shared init + postInit(); +#endif +} + +void ConsoleUIController::render() +{ +#ifdef _ENABLEIGGY + /* Now that we've cleared, we need to tell GDraw which + render target to use, what depth/stencil buffer to use, + and where the origin should be. + + If we were using multisampling, we'd also need to give + GDraw a render target view for a non-multisampled texture + the size of main_rtv as a resolve target (this is the third + parameter). But since we're not using multisampling in this + example, no resolve targets are required. */ + gdraw_D3D11_SetTileOrigin( m_pRenderTargetView.Get(), + m_pDepthStencilView.Get(), + NULL, + 0, + 0 ); + + renderScenes(); + + /* Finally we're ready to display the frame. We call GDraw to + let it know we're done rendering, so it can do any finalization + it needs to do. */ + gdraw_D3D11_NoMoreGDrawThisFrame(); +#endif +} + +void ConsoleUIController::beginIggyCustomDraw4J(IggyCustomDrawCallbackRegion *region, CustomDrawData *customDrawRegion) +{ + PIXBeginNamedEvent(0,"Starting Iggy custom draw\n"); + + PIXBeginNamedEvent(0,"Gdraw setup"); + // get the correct object-to-world matrix from GDraw, and set the render state to a normal state + gdraw_D3D11_BeginCustomDraw_4J(region, customDrawRegion->mat); + PIXEndNamedEvent(); +} + +CustomDrawData *ConsoleUIController::setupCustomDraw(UIScene *scene, IggyCustomDrawCallbackRegion *region) +{ + CustomDrawData *customDrawRegion = new CustomDrawData(); + customDrawRegion->x0 = region->x0; + customDrawRegion->x1 = region->x1; + customDrawRegion->y0 = region->y0; + customDrawRegion->y1 = region->y1; + + PIXBeginNamedEvent(0,"Starting Iggy custom draw\n"); + PIXBeginNamedEvent(0,"Setup"); + + PIXBeginNamedEvent(0,"Gdraw setup"); + // get the correct object-to-world matrix from GDraw, and set the render state to a normal state + gdraw_D3D11_BeginCustomDraw_4J(region, customDrawRegion->mat); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Our setup"); + setupCustomDrawGameStateAndMatrices(scene, customDrawRegion); + PIXEndNamedEvent(); + PIXEndNamedEvent(); + + return customDrawRegion; +} + +CustomDrawData *ConsoleUIController::calculateCustomDraw(IggyCustomDrawCallbackRegion *region) +{ + CustomDrawData *customDrawRegion = new CustomDrawData(); + customDrawRegion->x0 = region->x0; + customDrawRegion->x1 = region->x1; + customDrawRegion->y0 = region->y0; + customDrawRegion->y1 = region->y1; + + gdraw_D3D11_CalculateCustomDraw_4J(region, customDrawRegion->mat); + + return customDrawRegion; +} + +void ConsoleUIController::endCustomDraw(IggyCustomDrawCallbackRegion *region) +{ + PIXBeginNamedEvent(0,"Teardown"); + PIXBeginNamedEvent(0,"Our teardown"); + endCustomDrawGameStateAndMatrices(); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Gdraw teardown"); + gdraw_D3D11_EndCustomDraw(region); + PIXEndNamedEvent(); + PIXEndNamedEvent(); + PIXEndNamedEvent(); +} + +void ConsoleUIController::setTileOrigin(S32 xPos, S32 yPos) +{ + gdraw_D3D11_SetTileOrigin( m_pRenderTargetView.Get(), + m_pDepthStencilView.Get(), + NULL, + xPos, + yPos ); +} + +GDrawTexture *ConsoleUIController::getSubstitutionTexture(int textureId) +{ + /* Create a wrapped texture from a shader resource view. + A wrapped texture can be used to let Iggy draw using the contents of a texture + you create and manage on your own. For example, you might render to this texture, + or stream video into it. Wrapped textures take up a handle. They will never be + freed or otherwise modified by GDraw; nor will GDraw change any reference counts. + All this is up to the application. */ + ID3D11ShaderResourceView *tex = RenderManager.TextureGetTexture(textureId); + ID3D11Resource *resource; + tex->GetResource(&resource); + ID3D11Texture2D *tex2d = (ID3D11Texture2D *)resource; + D3D11_TEXTURE2D_DESC desc; + tex2d->GetDesc(&desc); + GDrawTexture *gdrawTex = gdraw_D3D11_WrappedTextureCreate(tex); + return gdrawTex; +} + +void ConsoleUIController::destroySubstitutionTexture(void *destroyCallBackData, GDrawTexture *handle) +{ + /* Destroys the GDraw wrapper for a wrapped texture object. This will free up + a GDraw texture handle but not release the associated D3D texture; that is + up to you. */ + gdraw_D3D11_WrappedTextureDestroy(handle); +} + +void ConsoleUIController::shutdown() +{ +#ifdef _ENABLEIGGY + /* Destroy the GDraw context. This frees all resources, shaders etc. + allocated by GDraw. Note this is only safe to call after all + active Iggy player have been destroyed! */ + gdraw_D3D11_DestroyContext(); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Durango_UIController.h b/Minecraft.Client/Durango/Durango_UIController.h new file mode 100644 index 0000000..28fe08a --- /dev/null +++ b/Minecraft.Client/Durango/Durango_UIController.h @@ -0,0 +1,30 @@ +#pragma once + +#include "..\Common\UI\UIController.h" + +class ConsoleUIController : public UIController +{ +private: + Microsoft::WRL::ComPtr m_pRenderTargetView; + Microsoft::WRL::ComPtr m_pDepthStencilView; +public: + void init(Microsoft::WRL::ComPtr dev, Microsoft::WRL::ComPtr ctx, Microsoft::WRL::ComPtr pRenderTargetView, Microsoft::WRL::ComPtr pDepthStencilView, S32 w, S32 h); + + void render(); + void beginIggyCustomDraw4J(IggyCustomDrawCallbackRegion *region, CustomDrawData *customDrawRegion); + virtual CustomDrawData *setupCustomDraw(UIScene *scene, IggyCustomDrawCallbackRegion *region); + virtual CustomDrawData *calculateCustomDraw(IggyCustomDrawCallbackRegion *region); + virtual void endCustomDraw(IggyCustomDrawCallbackRegion *region); + +protected: + virtual void setTileOrigin(S32 xPos, S32 yPos); + +public: + GDrawTexture *getSubstitutionTexture(int textureId); + void destroySubstitutionTexture(void *destroyCallBackData, GDrawTexture *handle); + +public: + void shutdown(); +}; + +extern ConsoleUIController ui; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.cpp b/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.cpp new file mode 100644 index 0000000..cbedb03 --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.cpp @@ -0,0 +1,717 @@ +#include "stdafx.h" +#include "DurangoLeaderboardManager.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" + +namespace WFC = Windows::Foundation::Collections; +namespace CC = concurrency; + +LeaderboardManager *LeaderboardManager::m_instance = new DurangoLeaderboardManager(); //Singleton instance of the LeaderboardManager + +DurangoLeaderboardManager::DurangoLeaderboardManager() +{ + m_eStatsState = eStatsState_Idle; + InitializeCriticalSection(&m_csStatsState); + + m_openSessions = 0; + m_xboxLiveContext = nullptr; + m_scores = NULL; + m_readCount = 0; + m_maxRank = 0; + m_waitingForProfiles = false; + + m_difficulty = 0; + m_type = eStatsType_UNDEFINED; + m_statNames = ref new PC::Vector(); + m_xboxUserIds = ref new PC::Vector(); + m_leaderboardAsyncOp = nullptr; + m_statsAsyncOp = nullptr; + + for(unsigned int difficulty = 0; difficulty < 4; ++difficulty) + { + m_leaderboardNames[difficulty][eStatsType_Travelling] = L"LeaderboardTravelling" + _toString(difficulty); + m_leaderboardNames[difficulty][eStatsType_Mining] = L"LeaderboardMining" + _toString(difficulty); + m_leaderboardNames[difficulty][eStatsType_Farming] = L"LeaderboardFarming" + _toString(difficulty); + m_leaderboardNames[difficulty][eStatsType_Kills] = L"LeaderboardKills" + _toString(difficulty); + + m_socialLeaderboardNames[difficulty][eStatsType_Travelling] = L"Leaderboard.LeaderboardId.0.DifficultyLevelId." + _toString(difficulty); + m_socialLeaderboardNames[difficulty][eStatsType_Mining] = L"Leaderboard.LeaderboardId.1.DifficultyLevelId." + _toString(difficulty); + m_socialLeaderboardNames[difficulty][eStatsType_Farming] = L"Leaderboard.LeaderboardId.2.DifficultyLevelId." + _toString(difficulty); + m_socialLeaderboardNames[difficulty][eStatsType_Kills] = L"Leaderboard.LeaderboardId.3.DifficultyLevelId." + _toString(difficulty); + + m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.0"); // Walked + m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.2"); // Fallen + m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.4"); // Minecart + m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.5"); // Boat + + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.3"); // Dirt + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.4"); // Cobblestone + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.12"); // Sand + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.1"); // Stone + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.13"); // Gravel + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.82"); // Clay + m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.49"); // Obsidian + + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.344"); // Eggs + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.59"); // Wheat + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.39"); // Mushroom + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.83"); // Sugarcane + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.2.ItemId.335"); // Milk + m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.86"); // Pumpkin + + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.54"); // Zombie + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.51"); // Skeleton + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.50"); // Creeper + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.52"); // Spider + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.49"); // Spider Jockey + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.57"); // Zombie Pigman + m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.55"); // Slime + } +} + +void DurangoLeaderboardManager::Tick() +{ + ReadView view; + + switch( getState() ) + { + case eStatsState_GettingLeaderboardInfo: + break; + case eStatsState_ReceivedLeaderboardInfo: + { + setState(eStatsState_GettingStatsInfo); + + // Get the actual display info for the stats + m_statsAsyncOp = m_xboxLiveContext->UserStatisticsService->GetMultipleUserStatisticsAsync( + m_xboxUserIds->GetView(), // the collection of Xbox user IDs whose stats we want to retrieve + SERVICE_CONFIG_ID, // the service config that contains the stats we want + m_statNames->GetView() // a list of stat names we want + ); + + auto task = concurrency::create_task(m_statsAsyncOp).then( [this] (CC::task^> resultListTask) + { + try + { + app.DebugPrintf("[LeaderboardManager] Second continuation\n"); + m_statsAsyncOp = nullptr; + + WFC::IVectorView^ resultList = resultListTask.get(); + + if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1)); + + int userIndex = 0; + for( MXS::UserStatistics::UserStatisticsResult^ result : resultList ) + { + app.DebugPrintf("XboxUserId: %ls\n", result->XboxUserId->Data()); + + for( UINT index = 0; indexServiceConfigurationStatistics->Size; index++ ) + { + MXS::UserStatistics::ServiceConfigurationStatistic^ configStat = result->ServiceConfigurationStatistics->GetAt(index); + //app.DebugPrintf("ServiceConfigurationId: %ls\n", configStat->ServiceConfigurationId->Data()); + + updateStatsInfo(userIndex, m_difficulty, m_type, configStat->Statistics); + } + ++userIndex; + } + + app.DebugPrintf("[LeaderboardManager] Setting to ready\n"); + setState(eStatsState_Ready); + + } + catch (Platform::Exception^ ex) + { + m_leaderboardAsyncOp = nullptr; + setState(eStatsState_Failed); + + if (ex->HResult == HTTP_E_STATUS_NOT_FOUND) app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 404 Not Found - 0x%0.8x\n", ex->HResult); + else app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 0x%0.8x\n", ex->HResult); + } + catch (...) + { + app.DebugPrintf("[LeaderboardManager] SecondContinuation: Unknown exception.\n"); + } + }); + } + break; + case eStatsState_GettingStatsInfo: + break; + case eStatsState_Ready: + { + // If we're waiting on profiles, don't return scores just yet + if (m_waitingForProfiles) + { + return; + } + else + { + if (m_displayNames.size() == m_readCount) + { + // Add display names to scores + for (int i = 0; i < m_displayNames.size(); i++) + { + m_scores[i].m_name = m_displayNames[i]; + } + } + else + { + // This seem to happen if something went wrong with Xbox Live + app.DebugPrintf("DurangoLeaderboardManager::Tick: Display names count (%i) didn't match read count (%i)", m_displayNames.size(), m_readCount); + } + + m_displayNames.clear(); + } + + //assert(m_scores != NULL || m_readCount == 0); + + view.m_numQueries = m_readCount; + view.m_queries = m_scores; + + // 4J-JEV: Debugging. + //LeaderboardManager::printStats(view); + + eStatsReturn ret = view.m_numQueries > 0 ? eStatsReturn_Success : eStatsReturn_NoResults; + + if (m_readListener != NULL) + { + app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _)\n", ret, m_readCount); + m_readListener->OnStatsReadComplete(ret, m_maxRank, view); + } + + app.DebugPrintf("[LeaderboardManager] Deleting scores\n"); + delete [] m_scores; + m_scores = NULL; + + setState(eStatsState_Idle); + } + break; + + case eStatsState_Failed: + view.m_numQueries = 0; + view.m_queries = NULL; + + if ( m_readListener != NULL ) + { + m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view); + } + + setState(eStatsState_Idle); + + break; + + case eStatsState_Canceled: + app.DebugPrintf("[LeaderboardManager] Setting canceled\n"); + setState(eStatsState_Idle); + break; + + default: // Getting or Idle. + if (m_openSessions == 0 && m_xboxLiveContext != nullptr) + { + m_xboxLiveContext = nullptr; + + app.DebugPrintf("[LeaderboardManager] Tick(): Nulled XboxLiveContext\n"); + } + break; + } +} + +//Open a session +bool DurangoLeaderboardManager::OpenSession() +{ + if (m_openSessions == 0) + { + app.DebugPrintf("[LeaderboardManager] OpenSession()\n"); + + try + { + WXS::User^ user = ProfileManager.GetUser(ProfileManager.GetPrimaryPad()); + if(user != nullptr && user->IsSignedIn && !user->IsGuest) + { + m_xboxLiveContext = ref new MXS::XboxLiveContext(user); + } + else + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, No valid user\n"); + return false; + } + } + catch (Platform::Exception^ ex) + { + m_xboxLiveContext = nullptr; + app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, ret == 0x%08X.\n", ex->HResult); + return false; + } + catch (...) + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): Unknown exception.\n"); + return false; + } + } + else app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1); + + m_openSessions++; + return true; +} + +//Close a session +void DurangoLeaderboardManager::CloseSession() +{ + m_openSessions--; + + if (m_openSessions == 0) + { + if(isIdle()) + { + m_xboxLiveContext = nullptr; + + app.DebugPrintf("[LeaderboardManager] CloseSession(): Nulled XboxLiveContext\n"); + } + } + else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions); +} + +//Delete a session +void DurangoLeaderboardManager::DeleteSession() +{ +} + +//Write the given stats +//This is called synchronously and will not free any memory allocated for views when it is done + +bool DurangoLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views) +{ + return false; +} + +bool DurangoLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (!isIdle()) return false; + if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false; + setState(eStatsState_GettingLeaderboardInfo); + + if( m_xboxLiveContext == nullptr ) + { + throw ref new Platform::InvalidArgumentException(L"A valid User is required"); + } + + // Request the leaderboard to get ranking information + auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardForSocialGroupWithSkipToRankAsync( + ref new P::String(myUID.toString().c_str()), + SERVICE_CONFIG_ID, + ref new P::String(m_socialLeaderboardNames[difficulty][type].c_str()), + MXS::Social::SocialGroupConstants::People, + startIndex, + ref new P::String(L"descending"), + readCount + ); + + runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_Friends); + + return true; +} + +bool DurangoLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (!isIdle()) return false; + if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false; + setState(eStatsState_GettingLeaderboardInfo); + + if( m_xboxLiveContext == nullptr ) + { + throw ref new Platform::InvalidArgumentException(L"A valid User is required"); + } + + P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str()); + + // Request the leaderboard to get ranking information + auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardWithSkipToUserAsync( + SERVICE_CONFIG_ID, + leaderboardName, + ref new P::String(myUID.toString().c_str()), // skip to this user + readCount + ); + + runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_MyScore); + + return true; +} + +bool DurangoLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (!isIdle()) return false; + if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false; + setState(eStatsState_GettingLeaderboardInfo); + + if( m_xboxLiveContext == nullptr ) + { + throw ref new Platform::InvalidArgumentException(L"A valid User is required"); + } + + P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str()); + + // Request the leaderboard to get ranking information + auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardAsync( + SERVICE_CONFIG_ID, + leaderboardName, + startIndex, // skip this many ranks + readCount + ); + + runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_TopRank); + + return true; +} + +//Perform a flush of the stats +void DurangoLeaderboardManager::FlushStats() +{ +} + +//Cancel the current operation +void DurangoLeaderboardManager::CancelOperation() +{ + m_readListener = NULL; + setState(eStatsState_Canceled); + + if(m_leaderboardAsyncOp) m_leaderboardAsyncOp->Cancel(); + if(m_statsAsyncOp) m_statsAsyncOp->Cancel(); + + //if (m_transactionCtx != 0) + //{ + // int ret = sceNpScoreAbortTransaction(m_transactionCtx); + // + // if (ret < 0) + // { + // app.DebugPrintf("[LeaderboardManager] CancelOperation(): Problem encountered aborting current operation, 0x%X.\n", ret); + // } + // else + // { + // app.DebugPrintf("[LeaderboardManager] CancelOperation(): Operation aborted successfully.\n"); + // } + //} + //else app.DebugPrintf("[LeaderboardManager] CancelOperation(): No current operation.\n"); +} + +//Is the leaderboard manager idle. +bool DurangoLeaderboardManager::isIdle() +{ + return getState() == eStatsState_Idle; +} + +void DurangoLeaderboardManager::runLeaderboardRequest(WF::IAsyncOperation^ asyncOp, int difficulty, EStatsType type, unsigned int readCount, EFilterMode filter) +{ + m_leaderboardAsyncOp = asyncOp; + m_difficulty = difficulty; + m_type = type; + + // Build stat names + m_statNames = ref new PC::Vector(); + m_statNames->Clear(); + for(wstring name : m_leaderboardStatNames[difficulty][type]) + { + m_statNames->Append(ref new P::String(name.c_str())); + } + + app.DebugPrintf("[LeaderboardManager] Running request\n"); + CC::create_task(asyncOp) + .then( [this, readCount, difficulty, type, filter] (CC::task resultTask) + { + try + { + app.DebugPrintf("[LeaderboardManager] First continuation.\n"); + + m_leaderboardAsyncOp = nullptr; + + MXSL::LeaderboardResult^ lastResult = resultTask.get(); + + app.DebugPrintf( + "Name: %ls - Filter: %ls - Rows: Retrieved=%d Total=%d Requested=%d.\n", + lastResult->DisplayName->Data(), + LeaderboardManager::filterNames[ (int) filter ].c_str(), + lastResult->Rows->Size, lastResult->TotalRowCount, readCount + ); + + //app.DebugPrintf("Column count: %d, Column: %ls, %ls, %d\n", lastResult->Columns->Size, lastResult->Columns->GetAt(0)->DisplayName->Data(), lastResult->Columns->GetAt(0)->StatisticName->Data(), lastResult->Columns->GetAt(0)->StatisticType); + + // 4J-JEV: Fix for Xbox One #162541 - [CRASH] Code: Leaderboards: Title crashes after pressing [B] Back button while changing Leaderboards' filter to 'My Score' + // 4J-JEV: Fix for X1: #165487 - [CRASH] XR-074: Compliance: The title does not properly handle switching to offline session while browsing the Leaderboards. + if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1)); + + // If this is My_Score, check that the user appears + if (filter == eFM_MyScore) + { + bool userIncluded = false; + for(int i = 0; i < lastResult->Rows->Size; i++) + { + if (lastResult->Rows->GetAt(i)->XboxUserId == m_xboxLiveContext->User->XboxUserId) userIncluded = true; + } + + // If the user isn't included, don't show the results + if (!userIncluded) + { + m_readCount = 0; + throw(ref new P::Exception(INET_E_DATA_NOT_AVAILABLE)); + } + } + + m_maxRank = lastResult->TotalRowCount; + m_readCount = lastResult->Rows->Size; + + if (m_scores != NULL) delete [] m_scores; + m_scores = new ReadScore[m_readCount]; + ZeroMemory(m_scores, sizeof(ReadScore) * m_readCount); + + m_xboxUserIds->Clear(); + + app.DebugPrintf("[LeaderboardManager] Retrieved Scores:\n"); + for( UINT index = 0; index < lastResult->Rows->Size; index++ ) + { + MXSL::LeaderboardRow^ row = lastResult->Rows->GetAt(index); + + app.DebugPrintf( + "\tIndex: %d\tRank: %d\tPercentile: %.1f%%\tXboxUserId: %ls\tValue: %ls.\n", + index, + row->Rank, + row->Percentile * 100, + row->XboxUserId->Data(), + row->Values->GetAt(0)->Data() + ); + + m_scores[index].m_name = row->Gamertag->Data(); + m_scores[index].m_rank = row->Rank; + m_scores[index].m_uid = PlayerUID(row->XboxUserId->Data()); + + // 4J-JEV: Added to help determine if this player's score is hidden due to their privacy settings. + m_scores[index].m_totalScore = (unsigned long) _fromString(row->Values->GetAt(0)->Data()); + + m_xboxUserIds->Append(row->XboxUserId); + } + + if(m_readCount > 0) + { + vector xuids = vector(); + for(int i = 0; i < lastResult->Rows->Size; i++) + { + xuids.push_back(PlayerUID(lastResult->Rows->GetAt(i)->XboxUserId->Data())); + } + m_waitingForProfiles = true; + ProfileManager.GetProfiles(xuids, &GetProfilesCallback, this); + setState(eStatsState_ReceivedLeaderboardInfo); + } + else + { + // we hit the end of the list + app.DebugPrintf("Reach the end\n"); + setState(eStatsState_Ready); + } + } + catch (Platform::Exception^ ex) + { + m_leaderboardAsyncOp = nullptr; + if (ex->HResult == INET_E_DATA_NOT_AVAILABLE) + { + // we hit the end of the list + app.DebugPrintf("ERROR: Reach the end\n"); + setState(eStatsState_Ready); + } + else if(ex->HResult == HTTP_E_STATUS_NOT_FOUND) + { + app.DebugPrintf("Error calling GetLeaderboardAsync function: 404 Not Found - 0x%0.8x\n", ex->HResult); + setState(eStatsState_Failed); + } + else + { + app.DebugPrintf("Error calling GetLeaderboardAsync function: 0x%0.8x\n", ex->HResult); + setState(eStatsState_Failed); + } + } + catch (...) + { + app.DebugPrintf("[LeaderboardManager] Unknown exception.\n"); + } + }); +} + +void DurangoLeaderboardManager::updateStatsInfo(int userIndex, int difficulty, EStatsType type, WFC::IVectorView^ statsResult) +{ + if (m_scores) + { + LeaderboardManager::ReadScore *userScore = &m_scores[userIndex]; + + switch (type) + { + case eStatsType_Farming: userScore->m_statsSize = 6; break; + case eStatsType_Mining: userScore->m_statsSize = 7; break; + case eStatsType_Kills: userScore->m_statsSize = 7; break; + case eStatsType_Travelling: userScore->m_statsSize = 4; break; + } + + int statIndex = 0, sumScores = 0; + for(wstring statName : m_leaderboardStatNames[difficulty][type]) + { + bool found = false; + for(auto result : statsResult) + { + if(statName.compare(result->StatisticName->Data()) == 0) + { + userScore->m_statsData[statIndex] = _fromString(result->Value->Data()); + found = true; + break; + } + } + if(!found) + { + userScore->m_statsData[statIndex] = 0; + } + + sumScores += userScore->m_statsData[statIndex]; + ++statIndex; + } + + if ( (sumScores == 0) && (userScore->m_totalScore > 0) ) + { + app.DebugPrintf("[LeaderboardManager] Player '%s' (rank %d) likely has privacy settings enabled.\n", userScore->m_name.c_str(), userScore->m_rank); + userScore->m_idsErrorMessage = IDS_LEADERBOARD_SCORE_HIDDEN; + } + } +} + +void DurangoLeaderboardManager::GetProfilesCallback(LPVOID param, std::vector profiles) +{ + DurangoLeaderboardManager *dlm = (DurangoLeaderboardManager *)param; + + app.DebugPrintf("[LeaderboardManager] GetProfilesCallback, profiles.size() == %d.\n", profiles.size()); + + if (profiles.size() > 0) + { + dlm->m_displayNames = vector(); + for (int i = 0; i < profiles.size(); i++) + { + dlm->m_displayNames.push_back(profiles[i]->GameDisplayName->Data()); + } + } + else + { + dlm->setState(eStatsState_Failed); + } + + dlm->m_waitingForProfiles = false; +} + +DurangoLeaderboardManager::EStatsState DurangoLeaderboardManager::getState() +{ + return m_eStatsState; +} + +void DurangoLeaderboardManager::setState(EStatsState newState) +{ + EnterCriticalSection(&m_csStatsState); + + bool validTransition = false; + + switch(m_eStatsState) + { + case eStatsState_Idle: + switch(newState) + { + case eStatsState_GettingLeaderboardInfo: + validTransition = true; + break; + }; + break; + case eStatsState_GettingLeaderboardInfo: + switch(newState) + { + case eStatsState_Ready: + case eStatsState_ReceivedLeaderboardInfo: + case eStatsState_Canceled: + case eStatsState_Failed: + validTransition = true; + break; + }; + break; + break; + case eStatsState_ReceivedLeaderboardInfo: + switch(newState) + { + case eStatsState_GettingStatsInfo: + case eStatsState_Canceled: + case eStatsState_Failed: + validTransition = true; + break; + }; + break; + case eStatsState_GettingStatsInfo: + switch(newState) + { + case eStatsState_Ready: + case eStatsState_Canceled: + case eStatsState_Failed: + validTransition = true; + break; + }; + break; + case eStatsState_Failed: + switch(newState) + { + case eStatsState_Idle: + validTransition = true; + break; + }; + break; + case eStatsState_Ready: + switch(newState) + { + case eStatsState_Canceled: + case eStatsState_Idle: + case eStatsState_Failed: + validTransition = true; + break; + }; + break; + case eStatsState_Canceled: + switch(newState) + { + case eStatsState_Ready: + newState = eStatsState_Idle; + case eStatsState_Idle: + validTransition = true; + break; + }; + break; + }; + +#ifndef _CONTENT_PACKAGE + app.DebugPrintf( + "[LeaderboardManager] %s state transition:\t%ls(%d) -> %ls(%d).\n", + (validTransition ? "Valid" : "INVALID"), + stateToString(m_eStatsState).c_str(), m_eStatsState, + stateToString(newState).c_str(), newState + ); +#endif + + if (validTransition) + { + m_eStatsState = newState; + } + + LeaveCriticalSection(&m_csStatsState); +} + +wstring DurangoLeaderboardManager::stateToString(EStatsState eState) +{ + switch (eState) + { + case eStatsState_Idle: return L"eStatsState_Idle"; + case eStatsState_GettingLeaderboardInfo: return L"eStatsState_GettingLeaderboardInfo"; + case eStatsState_ReceivedLeaderboardInfo: return L"eStatsState_ReceivedLeaderboardInfo"; + case eStatsState_GettingStatsInfo: return L"eStatsState_GettingStatsInfo"; + case eStatsState_ReceivedStatsInfo: return L"eStatsState_ReceivedStatsInfo"; + case eStatsState_Failed: return L"eStatsState_Failed"; + case eStatsState_Ready: return L"eStatsState_Ready"; + case eStatsState_Canceled: return L"eStatsState_Canceled"; + case eStatsState_Max: return L"eStatsState_MAX"; + default: return L"UNKNOWN"; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.h b/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.h new file mode 100644 index 0000000..233d8b7 --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/DurangoLeaderboardManager.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Common\Leaderboards\LeaderboardManager.h" + +namespace P = Platform; +namespace PC = Platform::Collections; +namespace WF = Windows::Foundation; +namespace WFC = Windows::Foundation::Collections; +namespace MXSL = Microsoft::Xbox::Services::Leaderboard; + +class DurangoLeaderboardManager : public LeaderboardManager +{ +protected: + enum EStatsState + { + eStatsState_Idle, + eStatsState_GettingLeaderboardInfo, + eStatsState_ReceivedLeaderboardInfo, + eStatsState_GettingStatsInfo, + eStatsState_ReceivedStatsInfo, + eStatsState_Failed, + eStatsState_Ready, + eStatsState_Canceled, + //eStatsState_Writing, + eStatsState_Max + }; + +private: + unsigned short m_openSessions; + + CRITICAL_SECTION m_csStatsState; + EStatsState m_eStatsState; //State of the stats read + + ReadScore *m_scores; + unsigned int m_maxRank; + + MXS::XboxLiveContext^ m_xboxLiveContext; + wstring m_leaderboardNames[4][eStatsType_MAX]; + wstring m_socialLeaderboardNames[4][eStatsType_MAX]; + std::vector m_leaderboardStatNames[4][eStatsType_MAX]; + + // Display names for the current scores + std::vector m_displayNames; + bool m_waitingForProfiles; + + int m_difficulty; + EStatsType m_type; + PC::Vector^ m_statNames; + PC::Vector^ m_xboxUserIds; + WF::IAsyncOperation^ m_leaderboardAsyncOp; + WF::IAsyncOperation^ >^ m_statsAsyncOp; + +public: + DurangoLeaderboardManager(); + + virtual void Tick(); + + //Open a session + virtual bool OpenSession(); + + //Close a session + virtual void CloseSession(); + + //Delete a session + virtual void DeleteSession(); + + //Write the given stats + //This is called synchronously and will not free any memory allocated for views when it is done + + virtual bool WriteStats(unsigned int viewCount, ViewIn views); + + virtual bool ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount); + virtual bool ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount); + virtual bool ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount); + + //Perform a flush of the stats + virtual void FlushStats(); + + //Cancel the current operation + virtual void CancelOperation(); + + //Is the leaderboard manager idle. + virtual bool isIdle(); + + static void GetProfilesCallback(LPVOID param, std::vector profiles); + +private: + void runLeaderboardRequest(WF::IAsyncOperation^ asyncOp, int difficulty, EStatsType type, unsigned int readCount, EFilterMode filter); + void updateStatsInfo(int userIndex, int difficulty, EStatsType type, WFC::IVectorView^ statsResult); + + EStatsState getState(); + void setState(EStatsState newState); + wstring stateToString(EStatsState eState); +}; diff --git a/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.cpp b/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.cpp new file mode 100644 index 0000000..caa5857 --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.cpp @@ -0,0 +1,487 @@ +#include "stdafx.h" + +#include "..\Minecraft.World\Tile.h" +#include "..\Minecraft.World\Item.h" + +#include "..\Minecraft.World\DurangoStats.h" + +#include "..\Minecraft.World\EntityIO.h" + +#include "..\Minecraft.World\StringHelpers.h" + +#include "Common\Console_Awards_enum.h" + +#include "DurangoStatsDebugger.h" + +namespace WFC = Windows::Foundation::Collections; +namespace CC = concurrency; + +StatParam::StatParam(const wstring &base) +{ + m_base = base; + //m_numArgs = numArgs; + m_args = vector(); + + unsigned int count=0; + wstring::size_type pos =base.find(L"*"); + while(pos!=string::npos) + { + count++; + pos=base.find(L"*",pos+1); + } + + m_numArgs = count; +} + +void StatParam::addArgs(int v1, ...) +{ + va_list argptr; + va_start(argptr, v1); + m_args.push_back(v1); + for (int i=0; i<(m_numArgs-1); i++) + { + int vi = va_arg(argptr, int); + m_args.push_back(vi); + } + va_end(argptr); +} + +vector *StatParam::getStats() +{ + vector *out = new vector(); + + static const int MAXSIZE = 256; + static const wstring SUBSTR = L"*"; + + wstring wstr_itr, wstr_num; + + if (m_args.size() <= 0 || m_numArgs <= 0) + { + out->push_back(m_base); + } + else + { + for (int i = 0; i < m_args.size(); i += m_numArgs) + { + wstr_itr = m_base; + + if (m_numArgs > 0) + { + for (int j=0; jpush_back( wstr_itr ); + } + } + + return out; +} + +DurangoStatsDebugger *DurangoStatsDebugger::instance = NULL; + +DurangoStatsDebugger::DurangoStatsDebugger() +{ + InitializeCriticalSection(&m_retrievedStatsLock); +} + +vector *DurangoStatsDebugger::getStats() +{ + vector *out = new vector(); + + for (auto it = m_stats.begin(); it!=m_stats.end(); it++) + { + vector *sublist = (*it)->getStats(); + out->insert(out->end(), sublist->begin(), sublist->end()); + } + + return out; +} + +DurangoStatsDebugger *DurangoStatsDebugger::Initialize() +{ + DurangoStatsDebugger *out = new DurangoStatsDebugger(); + + StatParam *sp = new StatParam(L"McItemAcquired.AcquisitionMethodId.*.ItemId.*.ItemAux.*"); + sp->addArgs(1, Tile::dirt_Id, 0); // works + sp->addArgs(2, Tile::dirt_Id, 0); // works + + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 0); // fixed (+ach 'Rainbow Collection') + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 1); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 2); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 3); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 4); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 5); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 6); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 7); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 8); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 9); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 10); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 11); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 12); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 13); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 14); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 15); + + out->m_stats.push_back(sp); + + sp = new StatParam(L"McItemAcquired.AcquisitionMethodId.*.ItemId.*"); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::dirt_Id); // works + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::milk_Id); // works. + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Tile::dirt_Id); // works. + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::porkChop_cooked_Id); // BROKEN! (ach 'Pork Chop' configured incorrectly) + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::cake_Id); // works. (ach 'The Lie' configured incorrectly) + sp->addArgs(DsItemEvent::eAcquisitionMethod_Bought, Item::emerald_Id); // fixed (+ach) + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::ironIngot_Id); // works. (+ach 'Acquired Hardware') + sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Item::fish_raw_Id); // works. (+ach 'Delicious Fish') + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::fish_cooked_Id); // works. (+ach 'Delicious Fish') + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::sign_Id); + sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::flowerPot_Id); // FIXING! + out->m_stats.push_back(sp); + + sp = new StatParam(L"McItemAcquired.DifficultyLevelId.*.AcquisitionMethodId.*.ItemId.*"); + sp->addArgs(1, 1, Tile::dirt_Id); // works + sp->addArgs(2, 2, Tile::dirt_Id); // works + out->m_stats.push_back(sp); + + sp = new StatParam(L"McItemUsed.ItemId.*.ItemAux.*"); + //sp->addArgs(Item::apple_Id, 0); + //sp->addArgs(Item::cake_Id, 0); + sp->addArgs(Item::beef_raw_Id, 0); // works + sp->addArgs(Item::porkChop_cooked_Id, 0); // works + out->m_stats.push_back(sp); + + sp = new StatParam(L"MinHungerWhenEaten.ItemId.*"); + //sp->addArgs(Item::apple_Id); + //sp->addArgs(Item::cake_Id); + sp->addArgs(Item::beef_raw_Id); // works + sp->addArgs(Item::rotten_flesh_Id); // works (+ach IronBelly) + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockBroken.BlockId.*"); + sp->addArgs( Tile::dirt_Id ); + sp->addArgs( Tile::rock_Id ); + sp->addArgs( Tile::emeraldOre_Id ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockBroken.BlockId.*.BlockAux.*"); + sp->addArgs( Tile::dirt_Id, 0 ); + sp->addArgs( Tile::rock_Id, 0 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockBroken.DifficultyLevelId.*.BlockId.*"); + sp->addArgs( 1, Tile::dirt_Id ); + sp->addArgs( 2, Tile::dirt_Id ); + sp->addArgs( 1, Tile::rock_Id ); + sp->addArgs( 2, Tile::rock_Id ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockBroken"); + sp->addArgs( -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockPlaced.BlockId.*"); + sp->addArgs( Tile::dirt_Id ); + sp->addArgs( Tile::stoneBrick_Id ); + sp->addArgs( Tile::sand_Id ); // works + sp->addArgs( Tile::sign_Id ); // fixed + sp->addArgs( Tile::wallSign_Id ); // fixed + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilled.KillTypeId.*.EnemyRoleId.*.PlayerWeaponId.*"); // BROKEN! + sp->addArgs( /*MELEE*/ 0, ioid_Cow, 0 ); + sp->addArgs( /*MELEE*/ 0, ioid_Cow, Item::sword_stone_Id ); + sp->addArgs( /*MELEE*/ 0, ioid_Pig, Item::sword_stone_Id ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MaxKillDistance.KillTypeId.*.EnemyRoleId.*.PlayerWeaponId.*"); // BROKEN! + sp->addArgs( /*MELEE*/ 0, ioid_Cow, Item::sword_stone_Id ); + sp->addArgs( /*MELEE*/ 0, ioid_Pig, Item::sword_stone_Id ); + sp->addArgs( /*RANGE*/ 1, ioid_Creeper, ioid_Arrow ); // FIXING! + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilled.KillTypeId.*.EnemyRoleId.*"); // BROKEN! + sp->addArgs( /*MELEE*/ 0, ioid_Cow ); + sp->addArgs( /*MELEE*/ 0, ioid_Pig ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilled.EnemyRoleId.*"); // BROKEN! + sp->addArgs( ioid_Cow ); + sp->addArgs( ioid_Pig ); + sp->addArgs( ioid_Zombie ); + sp->addArgs( ioid_Spiderjocky ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilledTotal.DifficultyLevelId.*.EnemyRoleId.*"); // BROKEN! + sp->addArgs( 1, ioid_Cow ); + sp->addArgs( 2, ioid_Cow ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilledTotal"); // BROKEN! + sp->addArgs(-1); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobInteract.MobId.*.Interaction.*"); + // sp->addArgs( ioid_Cow, /*MILKED*/ 6 ); no longer an interaction type. + sp->addArgs( ioid_Cow, /*BRED*/ 1 ); // fixed (+ach) + sp->addArgs( ioid_Sheep, /*SHEARED*/ 5 ); // works (+ach) + sp->addArgs( ioid_VillagerGolem, /*CRAFTED*/ 4 ); // works (+ach) + sp->addArgs( ioid_Ozelot, /*TAMED*/ 2 ); // works (+ach) + sp->addArgs( ioid_Zombie, /*CURED*/ 3 ); // fixed (+ach) + out->m_stats.push_back(sp); + + sp = new StatParam(L"EnteredNewBiome.BiomeId.*"); + for (int i=0; i<20; i++) + sp->addArgs( i ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"AchievementGet.AchievementId.*"); + sp->addArgs( eAward_TakingInventory ); // works (+ach) + sp->addArgs( eAward_WhenPigsFly ); // works (+ach) + sp->addArgs( eAward_InToTheNether ); // works (+ach) + sp->addArgs( eAward_theEnd ); // works (+ach) + sp->addArgs( eAward_winGame ); // fixed (+ach) + sp->addArgs( eAward_diamondsToYou ); + sp->addArgs( eAward_stayinFrosty ); // works (achievement configured incorrectly) + sp->addArgs( eAward_ironMan ); // fixed (+ach) + sp->addArgs( eAward_renewableEnergy ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"TimePlayed.DifficultyLevelId.*"); // BROKEN! + for (int i=0; i<4; i++) sp->addArgs(i); // Difficulty Levels. + out->m_stats.push_back(sp); + + sp = new StatParam(L"DistanceTravelled.DifficultyLevelId.*.TravelMethodId.*"); + for (int i = 0; i<4; i++) + { + sp->addArgs( i, DsTravel::eMethod_walk ); + sp->addArgs( i, DsTravel::eMethod_climb ); + sp->addArgs( i, DsTravel::eMethod_fall ); + sp->addArgs( i, DsTravel::eMethod_minecart ); + sp->addArgs( i, DsTravel::eMethod_swim ); + sp->addArgs( i, DsTravel::eMethod_pig ); + } + out->m_stats.push_back(sp); + + sp = new StatParam(L"DistanceTravelled"); + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"PlayedMusicDisc.DiscId.*"); // works (+ach) + for (int i = 2256; i < 2268; i++) + sp->addArgs( i ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"ChestfulOfCobblestone"); // works. (+ach) + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"Overkill"); // works. (+ach) + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"OnARail"); // works. (+ach) + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"Leaderboard.LeaderboardId.*.DifficultyLevelId.*"); + for (int i = 0; i < 4; i++) + { + sp->addArgs( eLeaderboardId_FARMING, i ); + sp->addArgs( eLeaderboardId_MINING, i ); + sp->addArgs( eLeaderboardId_TRAVELLING, i ); + sp->addArgs( eLeaderboardId_KILLING, i ); + } + out->m_stats.push_back(sp); + + + // Debugging 1 // + + sp = new StatParam(L"DistanceTravelled"); + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"BlockBroken"); + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + sp = new StatParam(L"MobKilledTotal"); + sp->addArgs( /*NO ARGUMENTS*/ -1 ); + out->m_stats.push_back(sp); + + return out; +} + +void DurangoStatsDebugger::PrintStats(int iPad) +{ + if (instance == NULL) instance = Initialize(); + + vector *tmp = instance->getStats(); + instance->m_printQueue.insert(instance->m_printQueue.end(), tmp->begin(), tmp->end()); + + // app.DebugPrintf("[DEBUG] START\n"); + // for (wstring t : *tmp) app.DebugPrintf("[DEBUG] %s\n", wstringtofilename(t)); + // app.DebugPrintf("[DEBUG] END\n"); + + instance->retrieveStats(iPad); + + app.DebugPrintf("[DurangoStatsDebugger] (%i) Results returned, starting printing.\n", instance->m_retrievedStats.size()); + for (StatResult result : instance->m_retrievedStats) + { + app.DebugPrintf("[DSB] '%s' == ", wstringtofilename(result.m_statName)); + app.DebugPrintf("%s\n", wstringtofilename(result.m_score)); + } + + // Empty list. + EnterCriticalSection(&instance->m_retrievedStatsLock); + instance->m_retrievedStats.erase( + instance->m_retrievedStats.begin(), + instance->m_retrievedStats.end() + ); + LeaveCriticalSection(&instance->m_retrievedStatsLock); + +} + +void DurangoStatsDebugger::retrieveStats(int iPad) +{ + MXS::XboxLiveContext ^xboxLiveContext; + try + { + WFC::IVectorView^ userList = Windows::Xbox::System::User::Users; + if( userList != nullptr ) + { + for (Windows::Xbox::System::User^ user : userList) + { + if( user->IsSignedIn && !user->IsGuest ) + { + xboxLiveContext = ref new MXS::XboxLiveContext(user); + break; + } + } + + if (xboxLiveContext == nullptr) + { + app.DebugPrintf("[DurangoStatsDebugger] Problem occured while creating 'XboxLiveContext'.\n"); return; + } + } + } + catch (Platform::Exception^ ex) + { + app.DebugPrintf("[DurangoStatsDebugger] Problem occured while creating 'XboxLiveContext'.\n"); return; + } + + if ( xboxLiveContext ) { app.DebugPrintf("[DurangoStatsDebugger] 'XboxLiveContext' created successfully.\n"); } + else { app.DebugPrintf("[DurangoStatsDebugger] 'XboxLiveContext' not created.\n"); return; } + + PlayerUID xuid; + ProfileManager.GetXUID(iPad, &xuid, true); + + const int readCount = 1, difficulty = 1, type = 0; + + // ----------------------------------------- // + + byte runningThreads = 0; + byte *r_runningThreads = &runningThreads; + + if (xuid.toString().compare(L"") == 0) + { + app.DebugPrintf("[DurangoStatsDebugger] NO LOGGED IN USER!\n"); + return; + } + + string plrname= wstringtofilename(xuid.toString()); + app.DebugPrintf( + "[DurangoStatsDebugger] Retrieving (%i) stats for '%s'.\n", + m_printQueue.size(), + plrname.c_str() + ); + + static const unsigned short R_SIZE = 20; + + // Create Stat retrieval threads until there is no long any stats to start retrieving. + while ( !instance->m_printQueue.empty() ) + { + vector *printing = new vector(); + + if (m_printQueue.size() > R_SIZE) + { + printing->insert( printing->end(), m_printQueue.begin(), m_printQueue.begin() + R_SIZE ); + m_printQueue.erase( m_printQueue.begin(), m_printQueue.begin() + R_SIZE ); + } + else + { + printing->insert( printing->end(), m_printQueue.begin(), m_printQueue.end() ); + m_printQueue.erase( m_printQueue.begin(), m_printQueue.end() ); + } + + // ------------------------------------------ // + + app.DebugPrintf("[DurangoStatsDebugger] Starting retrieval operation (%i/%i stats).\n", printing->size(), R_SIZE); + + runningThreads++; + + // Fill statNames string. + PC::Vector^ statNames = ref new PC::Vector(); + for ( auto it = printing->begin(); it != printing->end(); it++ ) + statNames->Append( ref new P::String(it->c_str()) ); + + // Create vector of the XboxId we want. + PC::Vector^ xboxUserIds = ref new PC::Vector(); + xboxUserIds->Append(ref new P::String(xuid.toString().c_str())); + + auto asyncOp = xboxLiveContext->UserStatisticsService->GetMultipleUserStatisticsAsync( + xboxUserIds->GetView(), // the collection of Xbox user IDs whose stats we want to retrieve + SERVICE_CONFIG_ID, // the service config that contains the stats we want + statNames->GetView() // a list of stat names we want + ); + + CC::create_task(asyncOp) + .then( [this,printing,iPad,r_runningThreads] (CC::task^> resultListTask) + { + try + { + WFC::IVectorView^ resultList = resultListTask.get(); + int userIndex = 0; + for( MXS::UserStatistics::UserStatisticsResult^ result : resultList ) + { + for( UINT index = 0; indexServiceConfigurationStatistics->Size; index++ ) + { + MXS::UserStatistics::ServiceConfigurationStatistic^ configStat = result->ServiceConfigurationStatistics->GetAt(index); + + app.DebugPrintf("[DurangoStatsDebugger] Retrieve complete, %i results returned.\n", configStat->Statistics->Size); + + for (auto result : configStat->Statistics) + { + StatResult sr = { iPad, result->StatisticName->Data(), result->Value->Data() }; + this->addRetrievedStat(sr); + } + } + ++userIndex; + } + + + } + catch (Platform::Exception ^ex) + { + app.DebugPrintf("[DurangoStatsDebugger] resultListTask.get() encountered an exception:\n\t'%s'\n", ex->ToString()->Data() ); + } + + (*r_runningThreads)--; + }); + } + + while (runningThreads > 0) Sleep(5); +} + +void DurangoStatsDebugger::addRetrievedStat(StatResult result) +{ + EnterCriticalSection(&m_retrievedStatsLock); + m_retrievedStats.push_back(result); + LeaveCriticalSection(&m_retrievedStatsLock); +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.h b/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.h new file mode 100644 index 0000000..f2ac038 --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.h @@ -0,0 +1,94 @@ +#pragma once + +#include "stdafx.h" + +namespace P = Platform; +namespace PC = Platform::Collections; +namespace WF = Windows::Foundation; +namespace WFC = Windows::Foundation::Collections; +namespace MXSL = Microsoft::Xbox::Services::Leaderboard; + +class StatParam +{ +private: + wstring m_base; + int m_numArgs; + + vector m_args; + +public: + StatParam(const wstring &base); + + void addArgs(int v1, ...); + + vector *getStats(); + +}; + + +class DurangoStatsDebugger +{ +protected: + static DurangoStatsDebugger *instance; + + enum + { + ioid_Arrow = 10, + + ioid_Spiderjocky = 49, + ioid_Creeper = 50, + ioid_Skeleton, + ioid_Spider, + ioid_Giant, + ioid_Zombie, + ioid_Slime, + ioid_Ghast, + ioid_PigZombie, + ioid_Enderman, + ioid_CaveSpider, + ioid_Silverfish, + ioid_Blaze, + ioid_LavaSlime, + ioid_EnderDragon, + + ioid_Pig = 90, + ioid_Sheep, + ioid_Cow, + ioid_Chicken, + ioid_Squid, + ioid_Wolf, + ioid_MushroomCow, + ioid_SnowMan, + ioid_Ozelot, + ioid_VillagerGolem, + }; + + DurangoStatsDebugger(); + + vector m_stats; + + vector *getStats(); + +public: + static DurangoStatsDebugger *Initialize(); + + static void PrintStats(int iPad); + +private: + vector m_printQueue; + + void retrieveStats(int iPad); + + typedef struct + { + int m_iPad; + wstring m_statName; + wstring m_score; + } StatResult; + + CRITICAL_SECTION m_retrievedStatsLock; + + vector m_retrievedStats; + + void addRetrievedStat(StatResult result); +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Leaderboards/GameProgress.cpp b/Minecraft.Client/Durango/Leaderboards/GameProgress.cpp new file mode 100644 index 0000000..dd07f3e --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/GameProgress.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" + +#include "Durango\ServiceConfig\Events-XBLA.8-149E11AEEvents.h" + +#include "..\Minecraft.World\DurangoStats.h" + +#include "GameProgress.h" + +namespace WFC = Windows::Foundation::Collections; +namespace MXSA = Microsoft::Xbox::Services::Achievements; +namespace CC = concurrency; + +GameProgress *GameProgress::instance = NULL; + +void GameProgress::Flush(int iPad) +{ + if (instance == NULL) + instance = new GameProgress(); + + instance->updatePlayer(iPad); +} + +void GameProgress::Tick() +{ + if (instance == NULL) + instance = new GameProgress(); + + long long currentTime = System::currentTimeMillis(); + if ( (currentTime - instance->m_lastUpdate) > (UPDATE_FREQUENCY / 4) ) + { + instance->updatePlayer(instance->m_nextPad); + instance->m_nextPad = ++instance->m_nextPad % MAX_LOCAL_PLAYERS; + instance->m_lastUpdate = currentTime; + } +} + +GameProgress::GameProgress() +{ + m_nextPad = 0; + m_lastUpdate = 0; +} + +void GameProgress::updatePlayer(int iPad) +{ + if ( ProfileManager.IsGuest(iPad) || !ProfileManager.IsSignedInLive(iPad) ) return; + + PlayerUID uid; + ProfileManager.GetXUID(iPad, &uid, true); + + WXS::User^ user = ProfileManager.GetUser(iPad); + + if (user == nullptr) return; + + MXS::XboxLiveContext ^xlc = ref new MXS::XboxLiveContext(user); + + // Get these while they are still valid. + LPCGUID playerSession = DurangoStats::getPlayerSession(); + + CC::create_task( + xlc->AchievementService->GetAchievementsForTitleIdAsync( + ref new Platform::String(uid.toString().c_str()), // Xuid + 0x149E11AE, // TitleId + MXSA::AchievementType::Persistent, // Use regular achievements (not challenges) + false, // Unlocked only + MXSA::AchievementOrderBy::UnlockTime, // Order (we don't really care) + 0, // skipItems (start index) + 200 // MaxItems + ) + ).then( [this,iPad,uid,playerSession] (CC::task resultTask) + { + try + { + int achievementsUnlocked = 0; + + MXSA::AchievementsResult^ result = resultTask.get(); + if(result) + { + for (unsigned int i = 0, iMax = result->Items->Size; i < iMax; i++) + { + MXSA::Achievement^ ach = result->Items->GetAt(i); + if (ach->ProgressState == MXSA::AchievementProgressState::Achieved) + achievementsUnlocked++; + } + + float gameprogress; + if (EventWriteGameProgress( + uid.toString().c_str(), + playerSession, + gameprogress = calcGameProgress(achievementsUnlocked) ) + == 0) + { + app.DebugPrintf("<%ls> GameProgress(%.1f)\n", uid.toString().c_str(), gameprogress); + } + } + } + catch (Platform::Exception ^ex) + { + app.DebugPrintf("GameProgress:: Error, couldn't contact the achievments service (?): %ls", ex->Message->Data()); + } + }); + +} + +float GameProgress::calcGameProgress(int achievementsUnlocked) +{ + return (float) achievementsUnlocked / 0.60f; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Leaderboards/GameProgress.h b/Minecraft.Client/Durango/Leaderboards/GameProgress.h new file mode 100644 index 0000000..8ebb271 --- /dev/null +++ b/Minecraft.Client/Durango/Leaderboards/GameProgress.h @@ -0,0 +1,23 @@ +#pragma once + +class GameProgress +{ +private: + static GameProgress *instance; + +public: + static const long long UPDATE_FREQUENCY = 64 * 1000; + + static void Tick(); + static void Flush(int iPad); + +protected: + GameProgress(); + + int m_nextPad; + long long m_lastUpdate; + + void updatePlayer(int iPad); + + float calcGameProgress(int achievementsUnlocked); +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Minecraft_Macros.h b/Minecraft.Client/Durango/Minecraft_Macros.h new file mode 100644 index 0000000..4f1f096 --- /dev/null +++ b/Minecraft.Client/Durango/Minecraft_Macros.h @@ -0,0 +1,42 @@ + +#pragma once + +// 3 bit user index +// 5 bits alpha +// 1 bit decoration +// 3 bits poptime +// 8 bits unused // was 11 bits aux val but needed 15 bits for potions so moved to item bitmask +// 6 bits count +// 6 bits scale + +// uiCount is up to 64, but can't ever be 0, so to make it 6 bits, subtract one from the packing, and add one on the unpacking +#define MAKE_SLOTDISPLAY_DATA_BITMASK(uiUserIndex,uiAlpha,bDecorations,uiCount,uiScale,uiPopTime) ((((uiUserIndex&0x7)<<29) | (uiAlpha&0x1F)<<24) | (bDecorations?0x800000:0) | ((uiPopTime&0x7)<<20) | ((uiCount-1)<<6) | (uiScale&0x3F)) + +#define GET_SLOTDISPLAY_USERINDEX_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>29)&0x7) +#define GET_SLOTDISPLAY_ALPHA_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>24)&0x1F) +#define GET_SLOTDISPLAY_DECORATIONS_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x800000)?true:false) +//#define GET_SLOTDISPLAY_AUXVAL_FROM_DATA_BITMASK(uiBitmask) ((((unsigned long)uiBitmask)>>12)&0x7FF) +#define GET_SLOTDISPLAY_COUNT_FROM_DATA_BITMASK(uiBitmask) (((((unsigned int)uiBitmask)>>6)&0x3F)+1) +#define GET_SLOTDISPLAY_SCALE_FROM_DATA_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0x3F) +#define GET_SLOTDISPLAY_POPTIME_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>20)&0x7) + +// 16 bits for id (either item id or xzp icon id) +// 15 bits for aux value +// 1 bit for foil +#define MAKE_SLOTDISPLAY_ITEM_BITMASK(uiId,uiAuxValue,bFoil) ( (uiId & 0xFFFF) | ((uiAuxValue & 0x7FFF) << 16) | (bFoil?0x80000000:0) ) + +#define GET_SLOTDISPLAY_ID_FROM_ITEM_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0xFFFF) +#define GET_SLOTDISPLAY_AUXVAL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>16) & 0x7FFF) +#define GET_SLOTDISPLAY_FOIL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x80000000)?true:false) + + +// For encoding the players skin selection in their profile +// bDlcSkin = false is a players skin, bDlcSkin = true is a DLC skin +#define MAKE_SKIN_BITMASK(bDlcSkin, dwSkinId) ( (bDlcSkin?0x80000000:0) | (dwSkinId & 0x7FFFFFFF) ) +#define IS_SKIN_ID_IN_RANGE(dwSkinId) (dwSkinId <= 0x7FFFFFFF) + +#define GET_DLC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFFF) +#define GET_UGC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFE0) +#define GET_DEFAULT_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x0000001F) +#define GET_IS_DLC_SKIN_FROM_BITMASK(uiBitmask) ((((DWORD)uiBitmask)&0x80000000)?true:false) + diff --git a/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp new file mode 100644 index 0000000..232b4dd --- /dev/null +++ b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp @@ -0,0 +1,805 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved +#include "stdafx.h" +#include "ChatIntegrationLayer.h" +#include "DQRNetworkManager.h" +#include + +using namespace Windows::Foundation; +using namespace Windows::Xbox::System; + +// To integrate the Chat DLL in your game, you can use this ChatIntegrationLayer class with modifications, +// or create your own design your own class using the code in this file a guide. +std::shared_ptr GetChatIntegrationLayer() +{ + static std::shared_ptr chatIntegrationLayerInstance; + if (chatIntegrationLayerInstance == nullptr) + { + chatIntegrationLayerInstance.reset( new ChatIntegrationLayer() ); + } + + return chatIntegrationLayerInstance; +} + +ChatIntegrationLayer::ChatIntegrationLayer() +{ + ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) ); +} + +void ChatIntegrationLayer::InitializeChatManager( + __in bool combineCaptureBuffersIntoSinglePacket, + __in bool useKinectAsCaptureSource, + __in bool applySoundEffectsToCapturedAudio, + __in bool applySoundEffectsToChatRenderedAudio, + DQRNetworkManager *pDQRNet + ) +{ + m_pDQRNet = pDQRNet; + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) ); + } + + m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager(); + m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose; + + // Optionally, change the default settings below as desired by commenting out and editing any of the following lines + // Otherwise these defaults are used. + // + // m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager( ChatSessionPeriod::ChatPeriodOf40Milliseconds ); + // m_chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40; + // m_chatManager->ChatSettings->AudioThreadAffinityMask = XAUDIO2_DEFAULT_PROCESSOR; // <- this means is core 5, same as the default XAudio2 core + // m_chatManager->ChatSettings->AudioThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; + // m_chatManager->ChatSettings->AudioEncodingQuality = Windows::Xbox::Chat::EncodingQuality::Normal; + // m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose; + m_chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket = combineCaptureBuffersIntoSinglePacket; // if unset, it defaults to TRUE + m_chatManager->ChatSettings->UseKinectAsCaptureSource = useKinectAsCaptureSource; // if unset, it defaults to FALSE + m_chatManager->ChatSettings->PreEncodeCallbackEnabled = applySoundEffectsToCapturedAudio; // if unset, it defaults to FALSE + m_chatManager->ChatSettings->PostDecodeCallbackEnabled = applySoundEffectsToChatRenderedAudio; // if unset, it defaults to FALSE + + InitializeCriticalSection(&m_csAddedUsers); + + std::weak_ptr weakPtrToThis = shared_from_this(); + +#ifdef PROFILE + m_chatManager->ChatSettings->PerformanceCountersEnabled = true; +#endif + + // Upon enter constrained mode, mute everyone. + // Upon leaving constrained mode, unmute everyone who was previously muted. + m_tokenResourceAvailabilityChanged = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged += + ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Platform::Object^ ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained) + { + if( sharedPtrToThis->m_chatManager != nullptr ) + { + sharedPtrToThis->m_chatManager->MuteAllUsersFromAllChannels(); + } + } + else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full) + { + if( sharedPtrToThis->m_chatManager != nullptr ) + { + sharedPtrToThis->m_chatManager->UnmuteAllUsersFromAllChannels(); + + // The title should remember who was muted so when the Resume even occurs + // to avoid unmuting users who has been previously muted. Simply re-mute them here + } + } + } + }); + + m_tokenOnDebugMessage = m_chatManager->OnDebugMessage += + ref new Windows::Foundation::EventHandler( + [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->OnDebugMessageReceived(args); + } + }); + + m_tokenOnOutgoingChatPacketReady = m_chatManager->OnOutgoingChatPacketReady += + ref new Windows::Foundation::EventHandler( + [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->OnOutgoingChatPacketReady(args); + } + }); + + m_tokenOnCompareUniqueConsoleIdentifiers = m_chatManager->OnCompareUniqueConsoleIdentifiers += + ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler( + [weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + return sharedPtrToThis->CompareUniqueConsoleIdentifiers(obj1, obj2); + } + else + { + return false; + } + }); + + m_tokenUserAudioDeviceAdded = WXS::User::AudioDeviceAdded += + ref new Windows::Foundation::EventHandler( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceAddedEventArgs^ value ) + { + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + m_tokenUserAudioDeviceRemoved = WXS::User::AudioDeviceRemoved += + ref new Windows::Foundation::EventHandler( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceRemovedEventArgs^ value ) + { + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + m_tokenUserAudioDeviceChanged = WXS::User::AudioDeviceChanged += + ref new Windows::Foundation::EventHandler( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceChangedEventArgs^ value ) + { + std::shared_ptr sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + //m_tokenSuspending = Windows::ApplicationModel::Core::CoreApplication::Suspending += + // ref new EventHandler< Windows::ApplicationModel::SuspendingEventArgs^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args) + //{ + // // Upon Suspending, nothing needs to be done + //}); + + //m_tokenResuming = Windows::ApplicationModel::Core::CoreApplication::Resuming += + // ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args) + //{ + // // Upon Resuming, re-initialize the network, and reinitialize the chat session + //}); +} + + +void ChatIntegrationLayer::Shutdown() +{ + if( m_chatManager != nullptr ) + { + m_chatManager->OnDebugMessage -= m_tokenOnDebugMessage; + m_chatManager->OnOutgoingChatPacketReady -= m_tokenOnOutgoingChatPacketReady; + m_chatManager->OnCompareUniqueConsoleIdentifiers -= m_tokenOnCompareUniqueConsoleIdentifiers; + Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= m_tokenResourceAvailabilityChanged; + if( m_chatManager->ChatSettings->PreEncodeCallbackEnabled ) + { + m_chatManager->OnPreEncodeAudioBuffer -= m_tokenOnPreEncodeAudioBuffer; + } + if( m_chatManager->ChatSettings->PostDecodeCallbackEnabled ) + { + m_chatManager->OnPostDecodeAudioBuffer -= m_tokenOnPostDecodeAudioBuffer; + } + WXS::User::AudioDeviceAdded -= m_tokenUserAudioDeviceAdded; + WXS::User::AudioDeviceRemoved -= m_tokenUserAudioDeviceRemoved; + WXS::User::AudioDeviceChanged -= m_tokenUserAudioDeviceChanged; + + DeleteCriticalSection(&m_csAddedUsers); + + m_chatManager = nullptr; + } +} + +void ChatIntegrationLayer::OnDebugMessageReceived( + __in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args + ) +{ + // To integrate the Chat DLL in your game, + // change this to false and remove the LogComment calls, + // or integrate with your game's own UI/debug message logging system + bool outputToUI = false; + + if( outputToUI ) + { + if (args->ErrorCode == S_OK ) + { + m_pDQRNet->LogComment(L"GameChat: " + args->Message); + } + else + { + m_pDQRNet->LogCommentWithError(L"GameChat: " + args->Message, args->ErrorCode); + } + } + else + { + // The string appear in the Visual Studio Output window +#ifndef _CONTENT_PACKAGE + OutputDebugString( args->Message->Data() ); +#endif + } +} + +void ChatIntegrationLayer::GameUI_RecordPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ) +{ + uint32 messageTypeInt = static_cast(messageType); + if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage ) + { + return; + } + + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + m_chatVoicePacketsStatistic[static_cast(chatPacketType)][messageTypeInt]++; + } +} + +int ChatIntegrationLayer::GameUI_GetPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ) +{ + uint32 messageTypeInt = static_cast(messageType); + if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage ) + { + return 0; + } + + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + return m_chatVoicePacketsStatistic[static_cast(chatPacketType)][messageTypeInt]; + } +} + +void ChatIntegrationLayer::OnOutgoingChatPacketReady( + __in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args + ) +{ + byte *bytes; + int byteCount; + + GetBufferBytes(args->PacketBuffer, &bytes); + byteCount = args->PacketBuffer->Length; + unsigned int address = 0; + if( !args->SendPacketToAllConnectedConsoles ) + { + address = safe_cast(args->UniqueTargetConsoleIdentifier); + } + m_pDQRNet->SendBytesChat(address, bytes, byteCount, args->SendReliable, args->SendInOrder, args->SendPacketToAllConnectedConsoles); + + GameUI_RecordPacketStatistic( args->ChatMessageType, ChatPacketType::OutgoingPacket ); + +} + +void ChatIntegrationLayer::OnIncomingChatMessage( + unsigned int sessionAddress, + Platform::Array^ message + ) +{ + // To integrate the Chat DLL in your game, change the following code to use your game's network layer. + // Ignore the OnChatMessageReceived event as that is specific to this sample's simple network layer. + // Instead your title should upon receiving a packet, extract the chat message from it, and then call m_chatManager->ProcessIncomingChatMessage as shown below + // You will need to isolate chat messages to be unique from the rest of you game's other message types. + + // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session. + // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it. + + // This is how you would convert from byte array to a IBuffer^ + // + // Windows::Storage::Streams::IBuffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( sourceByteBufferSize ); + // byte* destBufferBytes = nullptr; + // GetBufferBytes( destBuffer, &destBufferBytes ); + // errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize ); + // THROW_HR_IF(err != 0, E_FAIL); + // destBuffer->Length = sourceByteBufferSize; + + // This is how you would convert from an int to a Platform::Object^ + // Platform::Object obj = IntToPlatformObject(5); + + Windows::Storage::Streams::IBuffer^ chatMessage = ArrayToBuffer(message); + Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)sessionAddress; + + if( m_chatManager != nullptr ) + { + Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = m_chatManager->ProcessIncomingChatMessage(chatMessage, uniqueRemoteConsoleIdentifier); + + GameUI_RecordPacketStatistic( chatMessageType, ChatPacketType::IncomingPacket ); + } +} + +// Only add people who intend to play. +void ChatIntegrationLayer::AddAllLocallySignedInUsersToChatClient( + __in uint8 channelIndex, + __in Windows::Foundation::Collections::IVectorView^ locallySignedInUsers + ) +{ + // To integrate the Chat DLL in your game, + // add all locally signed in users to the chat client + for each( Windows::Xbox::System::User^ user in locallySignedInUsers ) + { + if( user != nullptr ) + { +// LogComment(L"Adding Local User to Chat Client"); + AddLocalUserToChatChannel( channelIndex, user ); + } + } +} + +ChatIntegrationLayer::AddedUser::AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio) +{ + m_user = user; + m_canCaptureAudio = canCaptureAudio; +} + + +void ChatIntegrationLayer::AddLocalUser( __in Windows::Xbox::System::IUser^ user ) +{ + // Check we haven't added already + for( int i = 0; i < m_addedUsers.size(); i++ ) + { + if( m_addedUsers[i]->m_user->XboxUserId == user->XboxUserId ) + { + return; + } + } + + bool kinectAvailable = false; + Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault(); + if( sensor ) + { + sensor->Open(); + if( sensor->IsAvailable ) + { + kinectAvailable = true; + m_pDQRNet->LogComment(L"Evaluated that kinect is available\n"); + } + sensor->Close(); + } + + EnterCriticalSection(&m_csAddedUsers); + // First establish whether we have an appropriate audio device at this time + bool canCaptureAudio = false; + for each( WXS::IAudioDeviceInfo^ audioDevice in user->AudioDevices ) + { + m_pDQRNet->LogComment(L"Evaluating device " + audioDevice->DeviceCategory.ToString() + L" " + + audioDevice->DeviceType.ToString() + L" " + + audioDevice->Id + L" " + + audioDevice->Sharing.ToString() + L" " + + audioDevice->IsMicrophoneMuted.ToString() + L"\n"); + + // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not + if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) ) + { + canCaptureAudio = true; + } + } + + // If we can capture audio initially, then register with the chat session. Otherwise we'll reevaluate this situation when audio devices change + if( canCaptureAudio ) + { + AddLocalUserToChatChannel( 0 , user ); + } + + // Add to vector of users that we are tracking in the chat system + m_addedUsers.push_back(new AddedUser(user, canCaptureAudio)); + LeaveCriticalSection(&m_csAddedUsers); +} + +// Remove from our list of tracked users, if the user is already there +void ChatIntegrationLayer::RemoveLocalUser( __in Windows::Xbox::System::IUser^ user ) +{ + EnterCriticalSection(&m_csAddedUsers); + for( auto it = m_addedUsers.begin(); it != m_addedUsers.end(); it++ ) + { + if( (*it)->m_user->XboxUserId == user->XboxUserId ) + { + delete (*it); + m_addedUsers.erase(it); + LeaveCriticalSection(&m_csAddedUsers); + return; + } + } + LeaveCriticalSection(&m_csAddedUsers); +} + +// This is called when the audio devices for a user change in any way, and establishes whether we can now capture audio. Any change in this status from before will cause the user to be added/removed from the chat session. +void ChatIntegrationLayer::EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user ) +{ + bool kinectAvailable = false; + Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault(); + if( sensor ) + { + sensor->Open(); + if( sensor->IsAvailable ) + { + kinectAvailable = true; + m_pDQRNet->LogComment(L"Evaluated that kinect is available\n"); + } + sensor->Close(); + } + + EnterCriticalSection(&m_csAddedUsers); + for( int i = 0; i < m_addedUsers.size(); i++ ) + { + AddedUser *addedUser = m_addedUsers[i]; + if( addedUser->m_user->XboxUserId == user->XboxUserId ) + { + bool canCaptureAudio = false; + for each( WXS::IAudioDeviceInfo^ audioDevice in addedUser->m_user->AudioDevices ) + { + // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not + if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) ) + { + canCaptureAudio = true; + break; + } + } + if( canCaptureAudio != addedUser->m_canCaptureAudio ) + { + if( canCaptureAudio ) + { + AddLocalUserToChatChannel(0, addedUser->m_user ); + } + else + { + RemoveUserFromChatChannel(0, addedUser->m_user ); + } + addedUser->m_canCaptureAudio = canCaptureAudio; + LeaveCriticalSection(&m_csAddedUsers); + return; + } + } + } + LeaveCriticalSection(&m_csAddedUsers); +} + +void ChatIntegrationLayer::AddLocalUserToChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ) +{ + // Adds a local user to a specific channel. + + // This is helper function waits for the task to cm_chatManageromplete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + m_pDQRNet->LogComment( L">>>>>>>>>>>>> AddLocalUserToChatChannel" ); + if( m_chatManager != nullptr ) + { + auto asyncOp = m_chatManager->AddLocalUserToChatChannelAsync( channelIndex, user ); + concurrency::create_task( asyncOp ) + .then( [this] ( concurrency::task t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"AddLocalUserToChatChannelAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::RemoveRemoteConsole( + unsigned int address + ) +{ + // uniqueConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session. + // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it. + + // This is how you would convert from an int to a Platform::Object^ + // Platform::Object obj = IntToPlatformObject(5); + + // This is helper function waits for the task to complete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)address; + + if( m_chatManager != nullptr ) + { + auto asyncOp = m_chatManager->RemoveRemoteConsoleAsync( uniqueRemoteConsoleIdentifier ); + concurrency::create_task( asyncOp ).then( [this] ( concurrency::task t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"RemoveRemoteConsoleAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::RemoveUserFromChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ) +{ + if( m_chatManager != nullptr ) + { + // This is helper function waits for the task to complete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + auto asyncOp = m_chatManager->RemoveLocalUserFromChatChannelAsync( channelIndex, user ); + concurrency::create_task( asyncOp ).then( [this] ( concurrency::task t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"RemoveLocalUserFromChatChannelAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::OnNewSessionAddressAdded( + __in unsigned int address + ) +{ + m_pDQRNet->LogCommentFormat( L">>>>>>>>>>>>> OnNewSessionAddressAdded (%d)",address ); + Platform::Object^ uniqueConsoleIdentifier = (Platform::Object^)address; + /// Call this when a new console connects. + /// This adds this console to the chat layer + if( m_chatManager != nullptr ) + { + m_chatManager->HandleNewRemoteConsole(uniqueConsoleIdentifier ); + } +} + +Windows::Foundation::Collections::IVectorView^ ChatIntegrationLayer::GetChatUsers() +{ + if( m_chatManager != nullptr ) + { + return m_chatManager->GetChatUsers(); + } + + return nullptr; +} + +bool ChatIntegrationLayer::HasMicFocus() +{ + if( m_chatManager != nullptr ) + { + return m_chatManager->HasMicFocus; + } + + return false; +} + +Platform::Object^ ChatIntegrationLayer::IntToPlatformObject( + __in int val + ) +{ + return (Platform::Object^)val; + + // You can also do the same using a PropertyValue. + //return Windows::Foundation::PropertyValue::CreateInt32(val); +} + +int ChatIntegrationLayer::PlatformObjectToInt( + __in Platform::Object^ uniqueRemoteConsoleIdentifier + ) +{ + return safe_cast( uniqueRemoteConsoleIdentifier ); + + // You can also do the same using a PropertyValue. + //return safe_cast(uniqueRemoteConsoleIdentifier)->GetInt32(); +} + +void ChatIntegrationLayer::HandleChatChannelChanged( + __in uint8 oldChatChannelIndex, + __in uint8 newChatChannelIndex, + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ) +{ + // We remember if the local user was currently muted from all channels. And when we switch channels, + // we ensure that the state persists. For remote users, title should implement this themselves + // based on title game design if they want to persist the muting state. + + bool wasUserMuted = false; + IUser^ userBeingRemoved = nullptr; + + if (chatUser != nullptr && chatUser->IsLocal) + { + wasUserMuted = chatUser->IsMuted; + userBeingRemoved = chatUser->User; + if (userBeingRemoved != nullptr) + { + RemoveUserFromChatChannel(oldChatChannelIndex, userBeingRemoved); + AddLocalUserToChatChannel(newChatChannelIndex, userBeingRemoved); + } + } + + // If the local user was muted earlier, get the latest chat users and mute him again on the newly added channel. + if (wasUserMuted && userBeingRemoved != nullptr) + { + auto chatUsers = GetChatUsers(); + if (chatUsers != nullptr ) + { + for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++) + { + Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex); + if( chatUser != nullptr && (chatUser->XboxUserId == userBeingRemoved->XboxUserId) ) + { + m_chatManager->MuteUserFromAllChannels(chatUser); + break; + } + } + } + } +} + +void ChatIntegrationLayer::ChangeChatUserMuteState( + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ) +{ + /// Helper function to swap the mute state of a specific chat user + if( m_chatManager != nullptr && chatUser != nullptr) + { + if (chatUser->IsMuted) + { + m_chatManager->UnmuteUserFromAllChannels(chatUser); + } + else + { + m_chatManager->MuteUserFromAllChannels(chatUser); + } + } +} + +Microsoft::Xbox::GameChat::ChatUser^ ChatIntegrationLayer::GetChatUserByXboxUserId( + __in Platform::String^ xboxUserId + ) +{ + Windows::Foundation::Collections::IVectorView^ chatUsers = GetChatUsers(); + for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) + { + if (chatUser != nullptr && ( chatUser->XboxUserId == xboxUserId ) ) + { + return chatUser; + } + } + + return nullptr; +} + + +bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers( + __in Platform::Object^ uniqueRemoteConsoleIdentifier1, + __in Platform::Object^ uniqueRemoteConsoleIdentifier2 + ) +{ + if (uniqueRemoteConsoleIdentifier1 == nullptr || uniqueRemoteConsoleIdentifier2 == nullptr) + { + return false; + } + + // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // We're using XRNS addresses, which are unsigned ints + unsigned int address1 = safe_cast(uniqueRemoteConsoleIdentifier1); + unsigned int address2 = safe_cast(uniqueRemoteConsoleIdentifier2); + return address1 == address2; +} + +Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatIntegrationLayer::GetChatPerformanceCounters() +{ + if( m_chatManager != nullptr && + m_chatManager->ChatSettings != nullptr && + m_chatManager->ChatSettings->PerformanceCountersEnabled ) + { + return m_chatManager->ChatPerformanceCounters; + } + + return nullptr; +} + +void ChatIntegrationLayer::OnControllerPairingChanged( Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args ) +{ +#if 0 + auto controller = args->Controller; + if ( controller ) + { + if ( controller->Type == L"Windows.Xbox.Input.Gamepad" ) + { + // Either add the user or sign one in + User^ user = args->User; + if ( user != nullptr ) + { + g_sampleInstance->GetLoggingUI()->LogCommentToUI("OnControllerPairingChanged: " + user->DisplayInfo->Gamertag); + AddLocalUserToChatChannel( + g_sampleInstance->GetUISelectionChatChannelIndex(), + user + ); + } + } + } +#endif +} + +void ChatIntegrationLayer::ToggleRenderTargetVolume() +{ + // Simple toggle logic to just show usage of LocalRenderTargetVolume property + static bool makeRenderTargetVolumeQuiet = false; + makeRenderTargetVolumeQuiet = !makeRenderTargetVolumeQuiet; + + auto chatUsers = GetChatUsers(); + if (chatUsers != nullptr ) + { + for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++) + { + Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex); + if( chatUser != nullptr && chatUser->IsLocal ) + { + chatUser->LocalRenderTargetVolume = ( makeRenderTargetVolumeQuiet ) ? 0.1f : 1.0f; + } + } + } +} + + +void ChatIntegrationLayer::GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut ) +{ + if ( ppOut == nullptr || buffer == nullptr ) + { + throw ref new Platform::InvalidArgumentException(); + } + + *ppOut = nullptr; + + Microsoft::WRL::ComPtr srcBufferInspectable(reinterpret_cast( buffer )); + Microsoft::WRL::ComPtr srcBufferByteAccess; + srcBufferInspectable.As(&srcBufferByteAccess); + srcBufferByteAccess->Buffer(ppOut); +} + +Windows::Storage::Streams::IBuffer^ ChatIntegrationLayer::ArrayToBuffer( __in Platform::Array^ array ) +{ + Windows::Storage::Streams::DataWriter^ writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(array); + return writer->DetachBuffer(); +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/ChatIntegrationLayer.h b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.h new file mode 100644 index 0000000..80b4e10 --- /dev/null +++ b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.h @@ -0,0 +1,244 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved +#pragma once + +//#include "UserController.h" + +class DQRNetworkManager; + +enum ChatPacketType +{ + IncomingPacket = 0, + OutgoingPacket = 1 +}; + +class ChatIntegrationLayer + : public std::enable_shared_from_this // shared_from_this is needed to use a weak ref to 'this' when handling delegate callbacks +{ +public: + ChatIntegrationLayer(); + + DQRNetworkManager *m_pDQRNet; + /// + /// Initializes the chat manager + /// + void InitializeChatManager( + __in bool combineCaptureBuffersIntoSinglePacketEnabled, + __in bool useKinectAsCaptureSource, + __in bool applySoundEffectToCapture, + __in bool applySoundEffectToRender, + DQRNetworkManager *pDQRNet + ); + + /// + /// Shuts down the chat manager + /// + void Shutdown(); + + class AddedUser + { + public: + AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio); + Windows::Xbox::System::IUser^ m_user; + bool m_canCaptureAudio; + }; + + void AddLocalUser( __in Windows::Xbox::System::IUser^ user ); + void RemoveLocalUser( __in Windows::Xbox::System::IUser^ user ); + void EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user ); + + vector m_addedUsers; + CRITICAL_SECTION m_csAddedUsers; + +private: + /// + /// Adds a local user to a specific channel + /// This is helper function waits for the task to complete so shouldn't be called from the UI thread + /// + /// The channel to add the user to + /// The local user to add + void AddLocalUserToChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ); + + /// + /// Removes a local user from a specific channel + /// This is helper function waits for the task to complete so shouldn't be called from the UI thread + /// + /// The channel to remove the user from + /// The local user to remove + void RemoveUserFromChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ); +public: + + /// + /// Removes a remote console from chat + /// This is helper function waits for the task to complete so shouldn't be called from the UI thread + /// + /// A unique ID for the remote console + void RemoveRemoteConsole( + unsigned int address + ); + + /// + /// Handles incoming chat messages from the game's network layer + /// + /// A buffer containing the chat message + /// A unique ID for the remote console + void OnIncomingChatMessage( + unsigned int sessionAddress, + Platform::Array^ message + ); + + /// + /// Returns a list of chat users in the chat session + /// + Windows::Foundation::Collections::IVectorView^ GetChatUsers(); + + /// + /// Returns true if the game has mic focus. Otherwise another app has mic focus + /// + bool HasMicFocus(); + + /// + /// Helper function to swap the mute state of a specific chat user + /// + void ChangeChatUserMuteState( + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ); + + /// + /// Helper function to change the channel of a specific chat user + /// + void HandleChatChannelChanged( + __in uint8 oldChatChannelIndex, + __in uint8 newChatChannelIndex, + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ); + + /// + /// Call this when a new console connects. + /// This adds this console to the chat layer + /// + void OnNewSessionAddressAdded( + __in unsigned int address + ); + + /// + /// Adds a list of locally signed in users that have intent to play to the chat session on a specific channel index. + /// Avoid adding any user who is signed in that doesn't have intent to play otherwise users who are biometrically + /// signed in automatically will be added to the chat session + /// + /// The channel to add the users to + /// A list of locally signed in users that have intent to play + void AddAllLocallySignedInUsersToChatClient( + __in uint8 channelIndex, + __in Windows::Foundation::Collections::IVectorView^ locallySignedInUsers + ); + + /// + /// Handles when a debug message is received. Send this to the UI and OutputDebugString. Games should integrate with their existing log system. + /// + /// Contains the debug message to log + void OnDebugMessageReceived( + __in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args + ); + + /// + /// Send the chat packet to all connected consoles + /// + /// To integrate the Chat DLL in your game, change the following code to use your game's network layer. + /// You will need to isolate chat messages to be unique from the rest of you game's other message types. + /// When args->SendPacketToAllConnectedConsoles is true, your game should send the chat message to each connected console using the game's network layer. + /// It should send the chat message with Reliable UDP if args->SendReliable is true. + /// It should send the chat message in order (if that feature is available) if args->SendInOrder is true + /// + /// Describes the packet to send + void OnOutgoingChatPacketReady( + __in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args + ); + + /// + /// Example of how to cast an int to a Platform::Object^ + /// + Platform::Object^ IntToPlatformObject( + __in int val + ); + + /// + /// Example of how to cast an Platform::Object^ to an int + /// + int PlatformObjectToInt( + __in Platform::Object^ obj + ); + + /// + /// Helper function to get specific ChatUser by xboxUserId + /// + Microsoft::Xbox::GameChat::ChatUser^ GetChatUserByXboxUserId( + __in Platform::String^ xboxUserId + ); + + /// + /// Helper function to get specific ChatUser by xboxUserId + /// + bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers( + __in Platform::Object^ uniqueRemoteConsoleIdentifier1, + __in Platform::Object^ uniqueRemoteConsoleIdentifier2 + ); + + /// + /// Helper function to return the ChatPerformanceCounters^ from the ChatManager so perf numbers can be shown on the UI + /// These numbers will only be valid if m_chatManager->ChatSettings->PerformanceCountersEnabled is set to true. + /// + Microsoft::Xbox::GameChat::ChatPerformanceCounters^ GetChatPerformanceCounters(); + + /// + /// Returns a count of the number of chat packets of a specific type that have been either sent or received. + /// It is useful to monitor this number in the UI / logs to debug network issues. + /// + int GameUI_GetPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ); + + void OnControllerPairingChanged( + __in Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args + ); + + void ToggleRenderTargetVolume(); + +private: + void GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut ); + Windows::Storage::Streams::IBuffer^ ArrayToBuffer( __in Platform::Array^ array ); + + Concurrency::critical_section m_lock; + Microsoft::Xbox::GameChat::ChatManager^ m_chatManager; + Windows::Foundation::EventRegistrationToken m_tokenOnDebugMessage; + Windows::Foundation::EventRegistrationToken m_tokenOnOutgoingChatPacketReady; + Windows::Foundation::EventRegistrationToken m_tokenOnCompareUniqueConsoleIdentifiers; + Windows::Foundation::EventRegistrationToken m_tokenResourceAvailabilityChanged; + Windows::Foundation::EventRegistrationToken m_tokenOnPreEncodeAudioBuffer; + Windows::Foundation::EventRegistrationToken m_tokenOnPostDecodeAudioBuffer; + Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceAdded; + Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceRemoved; + Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceChanged; + + // Debug stats for chat packets. Use Debug_GetPacketStatistic() to get the values. + /// It is useful to monitor this number in the UI / logs to debug network issues. + void GameUI_RecordPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ); + Concurrency::critical_section m_chatPacketStatsLock; + int m_chatVoicePacketsStatistic[2][(int)Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage+1]; +}; + +std::shared_ptr GetChatIntegrationLayer(); \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager.cpp b/Minecraft.Client/Durango/Network/DQRNetworkManager.cpp new file mode 100644 index 0000000..f13cc4a --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager.cpp @@ -0,0 +1,3093 @@ +#include "stdafx.h" + +#include "DQRNetworkManager.h" +#include "PartyController.h" +#include +#include +#include +#include "..\Minecraft.World\StringHelpers.h" +#include "base64.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +#include "ChatIntegrationLayer.h" + + +using namespace Concurrency; +using namespace Windows::Foundation::Collections; + +DQRNetworkManager::ePartyProcessType DQRNetworkManager::m_partyProcess = DQRNetworkManager::DNM_PARTY_PROCESS_NONE; + +bool DQRNetworkManager::m_inviteReceived = false; +int DQRNetworkManager::m_bootUserIndex; +wstring DQRNetworkManager::m_bootSessionName; +wstring DQRNetworkManager::m_bootServiceConfig; +wstring DQRNetworkManager::m_bootSessionTemplate; +DQRNetworkManager * DQRNetworkManager::s_pDQRManager = NULL; + +//using namespace Windows::Xbox::Networking; + +DQRNetworkManager::SessionInfo::SessionInfo(wstring& sessionName, wstring& serviceConfig, wstring& sessionTemplate) +{ + m_detailsValid = true; + m_sessionName = sessionName; + m_serviceConfig = serviceConfig; + m_sessionTemplate = sessionTemplate; +} + +DQRNetworkManager::SessionInfo::SessionInfo() +{ + m_detailsValid = false; +} + +// This maps internal to extern states, and needs to match element-by-element the eSQRNetworkManagerInternalState enumerated type +const DQRNetworkManager::eDQRNetworkManagerState DQRNetworkManager::m_INTtoEXTStateMappings[DQRNetworkManager::DNM_INT_STATE_COUNT] = +{ + DNM_STATE_INITIALISING, // DNM_INT_STATE_INITIALISING + DNM_STATE_INITIALISE_FAILED, // DNM_INT_STATE_INITIALISE_FAILED + DNM_STATE_IDLE, // DNM_INT_STATE_IDLE + DNM_STATE_HOSTING, // DNM_INT_STATE_HOSTING + DNM_STATE_HOSTING, // DNM_INT_STATE_HOSTING_WAITING_TO_PLAY + DNM_STATE_HOSTING, // DNM_INT_STATE_HOSTING_FAILED + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_GET_SDA + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_WAITING_FOR_SDA + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_CREATE_SESSION + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_SENDING_UNRELIABLE + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_FAILED_TIDY_UP + DNM_STATE_JOINING, // DNM_INT_STATE_JOINING_FAILED + DNM_STATE_STARTING, // DNM_INT_STATE_STARTING + DNM_STATE_PLAYING, // DNM_INT_STATE_PLAYING + DNM_STATE_LEAVING, // DNM_INT_STATE_LEAVING + DNM_STATE_LEAVING, // DNM_INT_STATE_LEAVING_FAILED + DNM_STATE_ENDING, // DNM_INT_STATE_ENDING +}; + +DQRNetworkManager::DQRNetworkManager(IDQRNetworkManagerListener *listener) +{ + s_pDQRManager = this; + m_listener = listener; + m_eventHandlers = ref new DQRNetworkManagerEventHandlers(this); + m_XRNS_Session = nullptr; + m_multiplayerSession = nullptr; + m_sda = nullptr; + m_currentSmallId = 0; + m_hostSmallId = 0; + m_isHosting = false; + m_isInSession = false; + m_partyController = new PartyController(this); + m_partyController->RegisterEventHandlers(); + memset(m_sessionAddressFromSmallId,0,sizeof(m_sessionAddressFromSmallId)); + memset(m_channelFromSmallId,0,sizeof(m_channelFromSmallId)); + + memset(&m_roomSyncData, 0, sizeof(m_roomSyncData)); + memset(m_players, 0, sizeof(m_players)); + + m_CreateSessionThread = NULL; + m_GetFriendPartyThread = NULL; + m_UpdateCustomSessionDataThread = NULL; + + m_CheckPartyInviteThread = NULL; + m_notifyForFullParty = false; + + m_customDataDirtyUpdateTicks = 0; + m_sessionResultCount = 0; + m_sessionSearchResults = NULL; + m_joinSessionUserMask = 0; + m_cancelJoinFromSearchResult = false; + + InitializeCriticalSection(&m_csStateChangeQueue); + InitializeCriticalSection(&m_csHostGamertagResolveResults); + InitializeCriticalSection(&m_csRTSMessageQueueIncoming); + InitializeCriticalSection(&m_csRTSMessageQueueOutgoing); + InitializeCriticalSection(&m_csSendBytes); + InitializeCriticalSection(&m_csVecChatPlayers); + InitializeCriticalSection(&m_csRoomSyncData); + InitializeCriticalSection(&m_csPartyViewVector); + + m_joinSessionXUIDs = ref new Platform::Array(4); + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_IDLE; + m_removeLocalPlayerState = DNM_REMOVE_PLAYER_STATE_IDLE; + + m_playersLeftParty = 0; + + m_handleForcedSignOut = false; + + m_RTS_Stat_totalBytes = 0; + m_RTS_Stat_totalSends = 0; + + m_RTS_DoWorkThread = new C4JThread(DQRNetworkManager::_RTSDoWorkThread, this, "Realtimesession processing"); + m_RTS_DoWorkThread->SetProcessor(CPU_CORE_DQR_REALTIMESESSION); + m_RTS_DoWorkThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); + m_RTS_DoWorkThread->Run(); +} + +void DQRNetworkManager::Initialise() +{ + m_associationTemplate = WXN::SecureDeviceAssociationTemplate::GetTemplateByName( L"MultiplayerUdp" ); + + m_state = DNM_INT_STATE_IDLE; + m_stateExternal = DNM_STATE_IDLE; + + m_chat = GetChatIntegrationLayer(); + m_chat->InitializeChatManager(true, true, false, false, this); +} + +// This method can be called on any xbox live context, to enable tracing of the service calls that go on internally when anything is done using that context +void DQRNetworkManager::EnableDebugXBLContext(MXS::XboxLiveContext^ XBLContext) +{ +#ifndef _CONTENT_PACKAGE + // Turn on debug logging to Output debug window for Xbox Services + XBLContext->Settings->DiagnosticsTraceLevel = MXS::XboxServicesDiagnosticsTraceLevel::Verbose; + + // Show service calls from Xbox Services on the UI for easy debugging + XBLContext->Settings->EnableServiceCallRoutedEvents = true; + XBLContext->Settings->ServiceCallRouted += ref new Windows::Foundation::EventHandler( + [=]( Platform::Object^, Microsoft::Xbox::Services::XboxServiceCallRoutedEventArgs^ args ) + { + //if( args->HttpStatus != 200 ) + { + LogComment(L"[URL]: " + args->HttpMethod + " " + args->Url->AbsoluteUri); + if( !args->RequestBody->IsEmpty() ) + { + LogComment(L"[RequestBody]: " + args->RequestBody ); + } + LogComment(L""); + LogComment(L"[Response]: " + args->HttpStatus.ToString() + " " + args->ResponseBody); + LogComment(L""); + } + }); +#endif +} + +// This is the top level method called when starting to host a network game. Most of the functionality is asynchronously run in a separate thread kicked off here - see ::HostGameThreadProc +void DQRNetworkManager::CreateAndJoinSession(int usersMask, unsigned char *customSessionData, unsigned int customSessionDataSize, bool offline) +{ + m_isHosting = true; + m_isInSession = true; + m_isOfflineGame = offline; + m_currentUserMask = usersMask; + SetState(DNM_INT_STATE_HOSTING); + m_customSessionData = customSessionData; + m_customSessionDataSize = customSessionDataSize; + + m_CreateSessionThread = new C4JThread(&DQRNetworkManager::_HostGameThreadProc, this, "Create session"); + m_CreateSessionThread->Run(); +} + +// Flag that the custom session data has been updated - this isn't actually updated here since updating is an asynchronous process and we may already be in the middle of doing an +// update. Instead the custom data is flagged flagged as dirty here, and it will be considered for updated when next appropriate during a tick. +void DQRNetworkManager::UpdateCustomSessionData() +{ + if( !m_isHosting) return; + if( m_isOfflineGame ) return; + + // Update data on next tick + m_customDataDirtyUpdateTicks = 1; +} + +// This is the main method for finishing joining a game session itself, by any method. +// By the point this is called, we should already have a reserved slot in the game session, by virtue +// of adding our local players to the party, having this noticed by the host, and the host add reserved slots for us in the game session. +// At this point we need to: +// (1) Set out players state in the session to active, so that they won't timeout & be removed +// (2) Get the network details of the host that we need to connect to +// (3) Set state up so that in the next tick we'll attempt to set up the network communications for this endpoint +// Note that the reason that the final setting up of the network endpoint isn't just directly in this method itself, is that we have seen it fail in +// the past and so we need to be able to retry it, which is simpler if it is part of our general state machine to be able to repeat this operation. +void DQRNetworkManager::JoinSession(int playerMask) +{ + // Establish a primary user & xbox live context for this user. We can use these for operations which aren't particular to any specific user on the local console + m_primaryUser = ProfileManager.GetUser(0); + if( m_primaryUser == nullptr ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting primary user\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + + m_primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(m_primaryUser); + if( m_primaryUserXboxLiveContext == nullptr ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting primary context\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + EnableDebugXBLContext(m_primaryUserXboxLiveContext); + + SetState(DNM_INT_STATE_JOINING); + + m_partyController->RefreshPartyView(); + m_isInSession = true; + m_isOfflineGame = false; + + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + // Get the game session associated with our party. We need to get this once for every person joining to set them individually to be active + if( playerMask & ( 1 << i ) ) + { + MXSM::MultiplayerSession^ session = nullptr; + + // Get user & xbox live context for this specific local user that we are attempting to join + WXS::User^ joiningUser = ProfileManager.GetUser(i); + if( joiningUser == nullptr ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting joining user\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + + MXS::XboxLiveContext^ joiningUserXBLContext = ref new MXS::XboxLiveContext(joiningUser); + if( joiningUserXBLContext == nullptr ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting joining context\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + + if( m_partyController->GetPartyView() == nullptr ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting party view\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + + // Get a copy of the session document, for this user + auto multiplayerSessionAsync = joiningUserXBLContext->MultiplayerService->GetCurrentSessionAsync( ConvertToMicrosoftXboxServicesMultiplayerSessionReference(m_partyController->GetPartyView()->GameSession)); + create_task(multiplayerSessionAsync).then([&session,this](task t) + { + try + { + session = t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"MultiplayerSession failed", ex->HResult ); + } + }) + .wait(); + + // If we found the session, then set the status of this member to be active (should be reserved). This will stop our slot timing out and us being dropped out of the session. + if( session != nullptr ) + { + if(!IsPlayerInSession(joiningUser->XboxUserId, session, NULL) ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED didn't find required player in session\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + session->SetCurrentUserStatus(MXSM::MultiplayerSessionMemberStatus::Active); + HRESULT hr = S_OK; + session = WriteSessionHelper( joiningUserXBLContext, session, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr ); + HandleSessionChange(session); + } + else + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED didn't find session\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + return; + } + } + } + + MXSM::MultiplayerSession^ session = m_multiplayerSession; + + if( session != nullptr ) + { + // Get the secure device address for the host player, and then attempt to create a association with it + int hostSessionIndex = GetSessionIndexAndSmallIdForHost(&m_hostSmallId); + + MXSM::MultiplayerSessionMember^ member = m_multiplayerSession->Members->GetAt(hostSessionIndex); + + m_secureDeviceAddressBase64 = member->SecureDeviceAddressBase64; + + m_isHosting = false; + + sockaddr_in6 localSocketAddressStorage; + + ZeroMemory(&localSocketAddressStorage, sizeof(localSocketAddressStorage)); + + localSocketAddressStorage.sin6_family = AF_INET6; + localSocketAddressStorage.sin6_port = htons(m_associationTemplate->AcceptorSocketDescription->BoundPortRangeLower); + + memcpy(&localSocketAddressStorage.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + + m_localSocketAddress = Platform::ArrayReference(reinterpret_cast(&localSocketAddressStorage), sizeof(localSocketAddressStorage)); + + m_joinCreateSessionAttempts = 0; + + m_joinSmallIdMask = playerMask; + + SetState(DNM_INT_STATE_JOINING_GET_SDA); + } + else + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED getting session\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + } +} + +void DQRNetworkManager::JoinSessionFromInviteInfo(int playerMask) +{ + // Gather set of XUIDs for the players that we are joining with + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + WXS::User^ user = ProfileManager.GetUser(i); + if( user == nullptr ) + { + return; + } + m_joinSessionXUIDs[i] = user->XboxUserId; + } + else + { + m_joinSessionXUIDs[i] = nullptr; + } + } + + // It is possible that in addition to the player that has been invited (and will already have a slot in the session) that we added more local player(s) from the quadrant sign in. + // In this case, then we need to attempt to add these to the party at this stage, and then wait for another gameSession ready event(s) before trying to progress to getting the whole + // set of local players into the game + + bool playerAdded = m_partyController->AddLocalUsersToParty( playerMask, ProfileManager.GetUser(0) ); + + if( playerAdded ) + { + app.DebugPrintf("Joining from invite, but extra non-party user(s) found so waiting for reservations\n"); + // Wait until we get notification via game session ready that our newly added party members have slots, then proceed to join session + m_isInSession = true; + m_startedWaitingForReservationsTime = System::currentTimeMillis(); + m_joinSessionUserMask = playerMask; + m_currentUserMask = 0; + m_isOfflineGame = false; + + SetState(DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS); + } + else + { + app.DebugPrintf("Joining from invite, no extra non-party users required\n"); + // No further players added - continue directly on with joining + JoinSession(playerMask); + } +} + + +// Add one or more local users (specified by bits set in playerMask) to the session. We use this as a client in a network game. At the stage this +// is called, the players being added should already have reserved slots in the session - ie we've already put the plyers in the party, this has +// been detected by the host, and it has added the reserved slots in the sesion that we require. +// +// At this stage we need to: +// (1) Set the player's state in the session to active +// (2) Send the small Id of the player to the host - we've already got reliable network communications to the host at this point. This lets the +// host know that there is an active player on this communication channel (we are multiplexing 4 channels, one for each local player) + +bool DQRNetworkManager::AddUsersToSession(int playerMask, MXSM::MultiplayerSessionReference^ sessionRef ) +{ + if( m_isHosting ) + { + return false; + } + + bool bSuccess = true; + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + // We need to get a MultiplayerSession for each player that is joining + + MXSM::MultiplayerSession^ session = nullptr; + + WXS::User^ newUser = ProfileManager.GetUser(i); + if( newUser == nullptr ) + { + bSuccess = false; + continue; + } + MXS::XboxLiveContext^ newUserXBLContext = ref new MXS::XboxLiveContext(newUser); + + auto multiplayerSessionAsync = newUserXBLContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(multiplayerSessionAsync).then([&session,this](task t) + { + try + { + session = t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"MultiplayerSession failed", ex->HResult ); + } + }) + .wait(); + + // If we found the session, then set the status of this member to be active (should be reserved). This will stop our slot timing out and us being dropped out of the session. + if( session != nullptr ) + { + int smallId = -1; + if(!IsPlayerInSession(newUser->XboxUserId, session, &smallId) ) + { + bSuccess = false; + continue; + } + session->SetCurrentUserStatus(MXSM::MultiplayerSessionMemberStatus::Active); + HRESULT hr = S_OK; + session = WriteSessionHelper( newUserXBLContext, session, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr ); + HandleSessionChange(session); + + SendSmallId(true, 1 << i); + } + } + } + return bSuccess; +} + +bool DQRNetworkManager::AddLocalPlayerByUserIndex(int userIndex) +{ + // We need to handle this differently for the host and other machines. As the procedure for adding a reserved slot for a local player whilst on the host doesn't seem to work + // + // On the host machine, we: + // + // (1) Get a MPSD for the player that is being added + // (2) Call the join method + // (3) Write the MPSD + // (4) Update the player sync data, and broadcast out to all clients + + // On remote machines, we: + // + // (1) join the party + // (2) the host should (if a slot is available) put a reserved slot in the session and attempt to pull reserved players + // (3) the client will respond to the gamesessionready event that is then received to set the added player to be active + + // If we're already in some of the asynchronous processing for adding as player, then we can't add another one - just fail straight away + if( m_addLocalPlayerState != DNM_ADD_PLAYER_STATE_IDLE ) return false; + + if( m_isHosting ) + { + WXS::User^ newUser = ProfileManager.GetUser(userIndex); + if( newUser == nullptr ) + { + return false; + } + + if( !m_isOfflineGame ) + { + // This is going to involve some async processing + + MXS::XboxLiveContext^ newUserXBLContext = ref new MXS::XboxLiveContext(newUser); + if( newUserXBLContext == nullptr ) + { + return false; + } + + EnableDebugXBLContext( newUserXBLContext); + + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_PROCESSING; + + auto asyncOp = newUserXBLContext->MultiplayerService->GetCurrentSessionAsync( m_multiplayerSession->SessionReference ); + create_task(asyncOp) + .then([this,newUserXBLContext,userIndex] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession = t.get(); + + // Don't attempt to join a player if we've no slots left in the session (this will include reserved slots) + if( currentSession->Members->Size < currentSession->SessionConstants->MaxMembersInSession ) + { + int smallId = m_currentSmallId; + MXSM::MultiplayerSessionMember ^member = currentSession->Join(GetNextSmallIdAsJsonString(), false); + currentSession->SetCurrentUserStatus( MXSM::MultiplayerSessionMemberStatus::Active ); + m_currentUserMask |= (1 << userIndex ); + + // Get device ID for current user & set in the session + Platform::String^ secureDeviceAddress = WXN::SecureDeviceAddress::GetLocal()->GetBase64String(); + currentSession->SetCurrentUserSecureDeviceAddressBase64( secureDeviceAddress ); + + HRESULT result; + WriteSessionHelper(newUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, result); // ************ WAITING ************** + + DQRNetworkPlayer* pPlayer = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_LOCAL, true, userIndex, m_XRNS_Session->LocalSessionAddress); + pPlayer->SetSmallId(smallId); + pPlayer->SetName(ProfileManager.GetUser(userIndex)->DisplayInfo->Gamertag->Data()); + pPlayer->SetDisplayName(ProfileManager.GetDisplayName(userIndex)); + pPlayer->SetUID(PlayerUID(ProfileManager.GetUser(userIndex)->XboxUserId->Data())); + + // Also add to the party so that our friends can find us. The host will get notified of this additional player in the party, but we should ignore since we're already in the session + m_partyController->AddLocalUsersToParty(1 << userIndex, ProfileManager.GetUser(0)); + + m_addLocalPlayerSuccessPlayer = pPlayer; + m_addLocalPlayerSuccessIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_SUCCESS; + } + else + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL_FULL; + } + } + catch ( Platform::COMException^ ex ) + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL; + } + catch ( Platform::Exception ^ex ) + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL; + } + }); + + return true; + } + else + { + DQRNetworkPlayer* pPlayer = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_LOCAL, true, userIndex, 0); + pPlayer->SetSmallId(m_currentSmallId++); + pPlayer->SetName(ProfileManager.GetUser(userIndex)->DisplayInfo->Gamertag->Data()); + pPlayer->SetDisplayName(ProfileManager.GetDisplayName(userIndex)); + pPlayer->SetUID(PlayerUID(ProfileManager.GetUser(userIndex)->XboxUserId->Data())); + + // TODO - could this add fail? + if(AddRoomSyncPlayer( pPlayer, 0, userIndex)) + { + SendRoomSyncInfo(); + m_listener->HandlePlayerJoined(pPlayer); // This is for notifying of local players joining in an offline game + } + else + { + // Can fail (notably if m_roomSyncData contains players who've left) + assert(0); + return false; + } + } + return true; + } + else + { + // Check if there's any available slots before attempting to add the player to the party. We can still fail joining later if + // the host can't add a reserved slot for us for some reason but better checking on the client side before even attempting. + + WXS::User^ newUser = ProfileManager.GetUser(userIndex); + + MXS::XboxLiveContext^ newUserXBLContext = ref new MXS::XboxLiveContext(newUser); + if( newUserXBLContext == nullptr ) + { + return false; + } + + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_PROCESSING; + auto asyncOp = newUserXBLContext->MultiplayerService->GetCurrentSessionAsync( m_multiplayerSession->SessionReference ); + create_task(asyncOp) + .then([this,newUserXBLContext,userIndex] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession = t.get(); + + if( currentSession->Members->Size == currentSession->SessionConstants->MaxMembersInSession ) + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL_FULL; + } + else + { + m_joinSessionUserMask |= (1 << userIndex); + m_joinSessionXUIDs[userIndex] = ProfileManager.GetUser(userIndex)->XboxUserId; + m_partyController->AddLocalUsersToParty(1 << userIndex, ProfileManager.GetUser(0)); + + m_addLocalPlayerSuccessPlayer = NULL; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_SUCCESS; + } + } + catch( Platform::COMException^ ex ) + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL; + } + catch ( Platform::Exception ^ex ) + { + m_addLocalPlayerFailedIndex = userIndex; + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_COMPLETE_FAIL; + } + }); + + return true; + } +} + +bool DQRNetworkManager::RemoveLocalPlayerByUserIndex(int userIndex) +{ + // We need to handle this differently for the host and other machines. + // + // On the host machine, we: + // + // (1) Get a MPSD for the player that is being removed + // (2) Call the leave method + // (3) Write the MPSD + // (4) Leave the party + // (5) Update the player sync data, and broadcast out to all clients + + // On remote machines, we: + // + // (1) Get a MPSD for the player that is being removed + // (2) Call the leave method + // (3) Write the MPSD + // (4) Leave the party + // (5) send message to the host that this player has left + // (6) host should respond to this message by removing the player from the player sync data, and notifying all clients of updated players + // (7) the client should respond to the player leaving that will happen and this will actually notify the game that the player has left + + // TODO - this should be rearranged so that the async stuff isn't waited on here, and so that the callbacks don't get called from the task's thread + + if( m_removeLocalPlayerState != DNM_REMOVE_PLAYER_STATE_IDLE ) return false; + + WXS::User^ leavingUser = ProfileManager.GetUser(userIndex, true); + if( leavingUser == nullptr ) + { + return false; + } + + if( !m_isOfflineGame ) + { + if( m_chat ) + { + m_chat->RemoveLocalUser(leavingUser); + } + MXS::XboxLiveContext^ leavingUserXBLContext = ref new MXS::XboxLiveContext(leavingUser); + if( leavingUserXBLContext == nullptr ) + { + return false; + } + EnableDebugXBLContext( leavingUserXBLContext); + m_removeLocalPlayerState = DNM_REMOVE_PLAYER_STATE_PROCESSING; + auto asyncOp = leavingUserXBLContext->MultiplayerService->GetCurrentSessionAsync( m_multiplayerSession->SessionReference ); + create_task(asyncOp) + .then([this,leavingUserXBLContext,userIndex,leavingUser] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession = t.get(); + + // Remove from the party + LogComment(L"Removing user from party"); + m_partyController->RemoveLocalUserFromParty(leavingUser); + LogComment(L"Removed user from party, now leaving session"); + + // Then leave & update the session + currentSession->Leave(); + HRESULT result; + WriteSessionHelper(leavingUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, result); + m_currentUserMask &= ~(1<HResult); + + m_removeLocalPlayerState = DNM_REMOVE_PLAYER_STATE_COMPLETE_FAIL; + m_removeLocalPlayerIndex = userIndex; + } + }); + } + else + { + DQRNetworkPlayer* pPlayer = GetLocalPlayerByUserIndex( userIndex ); + RemoveRoomSyncPlayer( pPlayer ); + } + return true; +} + +bool DQRNetworkManager::IsHost() +{ + return m_isHosting; +} + +// Consider as "in session" from the moment that a game is created or joined, until the point where the game itself has been told via state change that we are now idle. The +// game code requires IsInSession to return true as soon as it has asked to do one of these things (even if the state system hasn't really caught up with this request yet), and +// it also requires that it is informed of the state changes leading up to not being in the session, before this should report false. +bool DQRNetworkManager::IsInSession() +{ + return m_isInSession; +} + +// Get count of players currently in the session +int DQRNetworkManager::GetPlayerCount() +{ + return m_roomSyncData.playerCount; +} + +// Get count of players who are in the session, but not local to this machine +int DQRNetworkManager::GetOnlinePlayerCount() +{ + int count = 0; + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_players[i] ) + { + if( !m_players[i]->IsLocal() ) + { + count++; + } + } + } + return count; +} + + +DQRNetworkPlayer *DQRNetworkManager::GetPlayerByIndex(int idx) +{ + return m_players[idx]; + +} + +DQRNetworkPlayer *DQRNetworkManager::GetPlayerBySmallId(int idx) +{ + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_players[i] ) + { + if( m_players[i]->GetSmallId() == idx) + { + return m_players[i]; + } + } + } + return NULL; +} + +DQRNetworkPlayer *DQRNetworkManager::GetPlayerByXuid(PlayerUID xuid) +{ + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_players[i] ) + { + if( m_players[i]->GetUID() == xuid) + { + return m_players[i]; + } + } + } + return NULL; +} + +// Retrieve player display name by gamertag +wstring DQRNetworkManager::GetDisplayNameByGamertag(wstring gamertag) +{ + if (m_displayNames.find(gamertag) != m_displayNames.end()) + { + return m_displayNames[gamertag]; + } + else + { + return gamertag; + } +} + +DQRNetworkPlayer *DQRNetworkManager::GetLocalPlayerByUserIndex(int idx) +{ + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_players[i] ) + { + if( m_players[i]->IsLocal() ) + { + if( m_players[i]->GetLocalPlayerIndex() == idx ) + { + return m_players[i]; + } + } + } + } + return NULL; +} + +DQRNetworkPlayer *DQRNetworkManager::GetHostPlayer() +{ + return GetPlayerBySmallId(m_hostSmallId); +} + + +int DQRNetworkManager::GetSessionIndex(DQRNetworkPlayer *player) +{ + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_players[i] == player ) + { + return i; + } + } + return 0; +} + +void DQRNetworkManager::SetState(DQRNetworkManager::eDQRNetworkManagerInternalState state) +{ + eDQRNetworkManagerState oldState = m_INTtoEXTStateMappings[m_state]; + eDQRNetworkManagerState newState = m_INTtoEXTStateMappings[state]; + m_state = state; + + // Queue any important (ie externally relevant) state changes - we will do a call back for these in our main tick. Don't do it directly here + // as we could be coming from any thread at this stage, with any stack size etc. and so we don't generally want to expect the game to be able to handle itself in such circumstances. + if( newState != oldState ) + { + EnterCriticalSection(&m_csStateChangeQueue); + m_stateChangeQueue.push(StateChangeInfo(oldState,newState)); + LeaveCriticalSection(&m_csStateChangeQueue); + } +} + +DQRNetworkManager::eDQRNetworkManagerState DQRNetworkManager::GetState() +{ + return m_stateExternal;; +} + +void DQRNetworkManager::Tick() +{ + Tick_XRNS(); + Tick_VoiceChat(); + Tick_Party(); + Tick_CustomSessionData(); + Tick_AddAndRemoveLocalPlayers(); + Tick_ResolveGamertags(); + Tick_PartyProcess(); + Tick_StateMachine(); + Tick_CheckInviteParty(); +} + +void DQRNetworkManager::Tick_XRNS() +{ + ProcessRTSMessagesIncoming(); +} + +void DQRNetworkManager::Tick_VoiceChat() +{ +#if 0 + static int chatDumpCount = 0; + chatDumpCount++; + if( ( chatDumpCount % 40 ) == 0 ) + { + if( m_chat ) + { + LogCommentFormat(L"ChatManager: hasFocus:%d\n",m_chat->HasMicFocus()); + IVectorView^ chatUsers = m_chat->GetChatUsers(); + for( int i = 0; i < chatUsers->Size; i++ ) + { + Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(i); + LogCommentFormat(L"local: %d muted: %d type:%s restriction:%s mode:%s volume:%f [xuid:%s]\n", + chatUser->IsLocal,chatUser->IsMuted,chatUser->ParticipantType.ToString()->Data(),chatUser->RestrictionMode.ToString()->Data(), chatUser->TalkingMode.ToString()->Data(),chatUser->Volume, + chatUser->XboxUserId->Data()); + } + } + } +#endif + // If we have to inform the chat integration layer of any players that have joined, do that now + EnterCriticalSection(&m_csVecChatPlayers); + for( int i = 0; i < m_vecChatPlayersJoined.size(); i++ ) + { + int idx = m_vecChatPlayersJoined[i]; + if( m_chat ) + { + WXS::User^ user = ProfileManager.GetUser(idx); + if( user != nullptr ) + { + m_chat->AddLocalUser(user); + } + } + } + m_vecChatPlayersJoined.clear(); + LeaveCriticalSection(&m_csVecChatPlayers); +} + +void DQRNetworkManager::Tick_Party() +{ + // If the primary player has been flagged as having left the party, then we don't respond immediately as it is possible we are just transitioning from one party to another, and it would be much + // nicer to handle this kind of transition directly. If we do get a new party within this time period, then we'll handle by asking the user if they want to leave the game they are currently in etc. + if( m_playersLeftParty ) + { + if( ( System::currentTimeMillis() - m_playersLeftPartyTime ) > PRIMARY_PLAYER_LEAVING_PARTY_WAIT_MS ) + { + // We've waited long enough. User must (hopefully) have just left the party + // Previously we'd switch to offline but that causes a world of pain with forced sign-outs + if( m_playersLeftParty & 1 ) + { + // Before we switch to an offline game, check to see if there is currently a new party. If this is the case and + // we're here, then its because we were added to a party, but didn't receive a gamesessionready event. So if we have + // a party here that we've joined, and the number of players in the party (including us) is more than MAX_PLAYERS_IN_TEMPLATE, + // then it seems reasonable to assume that the reason we're not in the game is due to lack of space, and we can inform the + // user of this when converting to an offline game + + m_partyController->RefreshPartyView(); + WXM::PartyView^ partyView = m_partyController->GetPartyView(); + if( partyView ) + { + int partySize = partyView->Members->Size; + if( partySize > MAX_PLAYERS_IN_TEMPLATE ) + { + g_NetworkManager.SetFullSessionMessageOnNextSessionChange(); + } + } + + DQRNetworkManager::LogComment(L"Primary player on this system has left the party, switching to offline\n"); + app.SetAction(0, eAppAction_EthernetDisconnected); + } + else + { + // Secondary player(s) leaving, just remove as if they had chosen to exit themselves from the game + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_playersLeftParty & ( 1 << i ) ) + { + RemoveLocalPlayerByUserIndex(i); + } + } + } + + m_playersLeftParty = 0; + } + } + + // Forced sign out + if (m_handleForcedSignOut) + { + HandleForcedSignOut(); + m_handleForcedSignOut = false; + } +} + +void DQRNetworkManager::Tick_CustomSessionData() +{ + // If there was a thread updaing our custom session data, then clear it up if it is done + if( m_UpdateCustomSessionDataThread != NULL ) + { + if( !m_UpdateCustomSessionDataThread->isRunning() ) + { + delete m_UpdateCustomSessionDataThread; + m_UpdateCustomSessionDataThread = NULL; + } + } + + // If our custom data is dirty, and we aren't currently updating, then kick off a thread to update it + if( m_isHosting && ( !m_isOfflineGame ) ) + { + if( m_UpdateCustomSessionDataThread == NULL ) + { + if( m_customDataDirtyUpdateTicks ) + { + m_customDataDirtyUpdateTicks--; + if( m_customDataDirtyUpdateTicks == 0 ) + { + m_UpdateCustomSessionDataThread = new C4JThread(&DQRNetworkManager::_UpdateCustomSessionDataThreadProc, this, "Updating custom data"); + m_UpdateCustomSessionDataThread->Run(); + } + } + } + } + else + { + m_customDataDirtyUpdateTicks = 0; + } +} + +void DQRNetworkManager::Tick_AddAndRemoveLocalPlayers() +{ + // A lot of handling adding local players is handled asynchronously. Trying to avoid having the callbacks that may result from this being called from the task threads, so handling this aspect of it here in the tick + if( m_addLocalPlayerState == DNM_ADD_PLAYER_STATE_COMPLETE_SUCCESS ) + { + // If we've completed, and we're the host, then we should have the new player to create stored here in m_localPlayerSuccessCreated. For clients, this will just be NULL as the actual + // adding of the player happens as part of a longer process of the host creating us a reserved slot etc. etc. + if( m_addLocalPlayerSuccessPlayer ) + { + if( AddRoomSyncPlayer( m_addLocalPlayerSuccessPlayer, m_XRNS_Session->LocalSessionAddress, m_addLocalPlayerSuccessIndex) ) + { + SendRoomSyncInfo(); + m_listener->HandlePlayerJoined(m_addLocalPlayerSuccessPlayer); // This is notifying local players joining, when online (host only) + } + } + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_IDLE; + } + else if( m_addLocalPlayerState == DNM_ADD_PLAYER_STATE_COMPLETE_FAIL ) + { + m_listener->HandleAddLocalPlayerFailed(m_addLocalPlayerFailedIndex, false); + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_IDLE; + } + else if( m_addLocalPlayerState == DNM_ADD_PLAYER_STATE_COMPLETE_FAIL_FULL ) + { + m_listener->HandleAddLocalPlayerFailed(m_addLocalPlayerFailedIndex, true); + m_addLocalPlayerState = DNM_ADD_PLAYER_STATE_IDLE; + } + + // Similarly for removing local players - avoiding having callbacks from the async task threads, so this aspect of the process is handled here + + if( m_removeLocalPlayerState == DNM_REMOVE_PLAYER_STATE_COMPLETE_SUCCESS || m_removeLocalPlayerState == DNM_REMOVE_PLAYER_STATE_COMPLETE_FAIL ) + { + // Note: we now remove the player from the room sync data even if remove from session/party failed, + // either way we need to clean up + + // On host, directly remove from the player sync data. On client, send a message to the host which will do this + if( m_isHosting) + { + DQRNetworkPlayer* pPlayer = GetLocalPlayerByUserIndex( m_removeLocalPlayerIndex ); + RemoveRoomSyncPlayer( pPlayer ); + SendRoomSyncInfo(); + } + else + { + // Check if this player actually exists yet on this machine. If it is, then we need to send a message to the host to unassign it which + // ultimately will end up with this player being removed when the host syncs back with us. If it hasn't then there isn't anything to + // unassign with the host + DQRNetworkPlayer* pPlayer = GetLocalPlayerByUserIndex( m_removeLocalPlayerIndex ); + if( pPlayer ) + { + SendUnassignSmallId(m_removeLocalPlayerIndex); + } + } + m_removeLocalPlayerState = DNM_REMOVE_PLAYER_STATE_IDLE; + } +} + +void DQRNetworkManager::Tick_ResolveGamertags() +{ + // Host only - if there are any player display names which have been resolved (or failed to resolve), then this is the last stage in the player becoming active on the host's side and so do a few things here + EnterCriticalSection(&m_csHostGamertagResolveResults); + while( !m_hostGamertagResolveResults.empty() ) + { + HostGamertagResolveDetails *details = m_hostGamertagResolveResults.front(); + + details->m_pPlayer->SetName(details->m_name.c_str()); + + LogComment("Adding a player"); + if( AddRoomSyncPlayer(details->m_pPlayer, details->m_sessionAddress, details->m_channel ) ) + { + LogComment("Adding a player - success"); + m_listener->HandlePlayerJoined(details->m_pPlayer); // This is for notifying of remote players joining, when online (when we are the host), as we have resolved their names + // The last name to be resolve in any one atomic set (ie that comes in from a single DQR_INTERNAL_ASSIGN_SMALL_IDS message) will have this flag set, so we know this is the point + // to synchronise out to the clients + if( details->m_sync ) + { + LogComment("Synchronising players with clients"); + SendRoomSyncInfo(); + } + } + else + { + LogComment("Adding a player - failed"); + delete details->m_pPlayer; + + // TODO - what to do if adding a player failed here? + assert(false); + } + + delete details; + m_hostGamertagResolveResults.pop(); + } + LeaveCriticalSection(&m_csHostGamertagResolveResults); +} + +void DQRNetworkManager::Tick_PartyProcess() +{ + // On starting up the game, there's 3 options of what we need to do... + // (1) Attempt to join a game session that was passed in on activation (this will have happened if we were started from a game ready notification) + // (2) Attempt to join whatever game the party is associated with (this will happen if we were started in response to a party invite) + switch( m_partyProcess ) + { + case DNM_PARTY_PROCESS_NONE: + break; + case DNM_PARTY_PROCESS_JOIN_PARTY: + if( GetBestPartyUserIndex() ) + { + m_listener->HandleInviteReceived(0, new SessionInfo()); + } + break; + case DNM_PARTY_PROCESS_JOIN_SPECIFIED: + m_listener->HandleInviteReceived(m_bootUserIndex, new SessionInfo(m_bootSessionName, m_bootServiceConfig, m_bootSessionTemplate)); + break; + } + m_partyProcess = DNM_PARTY_PROCESS_NONE; +} + +void DQRNetworkManager::Tick_StateMachine() +{ + switch( m_state ) + { + case DNM_INT_STATE_JOINING_GET_SDA: + { + SetState(DNM_INT_STATE_JOINING_WAITING_FOR_SDA); + auto asyncOp = m_associationTemplate->CreateAssociationAsync(WXN::SecureDeviceAddress::FromBase64String(m_secureDeviceAddressBase64), WXN::CreateSecureDeviceAssociationBehavior::Reevaluate); + create_task(asyncOp).then([this](task t) + { + m_sda = nullptr; + try + { + m_sda = t.get(); + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"CreateAssociationAsync failed", ex->HResult ); + } + // If this succeeded, then make a store of all the things we'll need to initiate the network communication endpoint for this machine (our local address, remove address, secure device association etc.) + if( m_sda) + { + m_remoteSocketAddress = ref new Platform::Array(sizeof(SOCKADDR_STORAGE)); + m_sda->GetRemoteSocketAddressBytes(m_remoteSocketAddress); + SetState(DNM_INT_STATE_JOINING_CREATE_SESSION); + } + else + { + SetState(DNM_INT_STATE_JOINING_FAILED); + } + }); + } + break; + case DNM_INT_STATE_JOINING_CREATE_SESSION: + RTS_StartCient(); + SetState(DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION); + break; + case DNM_INT_STATE_JOINING_SENDING_UNRELIABLE: + { + __int64 timeNow = System::currentTimeMillis(); + // m_firstUnreliableSendTime of 0 indicates that we haven't tried sending an unreliable packet yet so need to send one and initialise things + if( m_firstUnreliableSendTime == 0 ) + { + m_firstUnreliableSendTime = timeNow; + m_lastUnreliableSendTime = timeNow; + + SendSmallId(false, m_joinSmallIdMask); + } + else + { + // Timeout if we've exceeded the threshold for this + if( (timeNow - m_firstUnreliableSendTime) > JOIN_PACKET_RESEND_TIMEOUT_MS ) + { + app.DebugPrintf("DNM_INT_STATE_JOINING_FAILED unreliable resend timeout\n"); + SetState(DNM_INT_STATE_JOINING_FAILED); + } + else + { + // Possibly send another packet if it has been long enough + if( (timeNow - m_lastUnreliableSendTime ) > JOIN_PACKET_RESEND_DELAY_MS ) + { + LogComment("Resending unreliable packet\n"); + m_lastUnreliableSendTime = timeNow; + SendSmallId(false, m_joinSmallIdMask); + } + } + } + } + break; + case DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS: + { + // Timeout if we've been waiting for reserved slots for our joining players for too long. This is most likely because the host doesn't have room for all the slots we wanted, and we weren't able to determine this + // when we went to join the game (ie someone else was joining at the same time). At this point we need to remove any local players that did already join, from both the session and the party. + __int64 timeNow = System::currentTimeMillis(); + if( ( timeNow - m_startedWaitingForReservationsTime ) > JOIN_RESERVATION_WAIT_TIME ) + { + SetState(DNM_INT_STATE_JOINING_FAILED_TIDY_UP); + TidyUpFailedJoin(); + } + } + break; + case DNM_INT_STATE_ENDING: + SetState(DNM_INT_STATE_IDLE); + break; + case DNM_INT_STATE_HOSTING_WAITING_TO_PLAY: + delete m_CreateSessionThread; + m_CreateSessionThread = NULL; + // If the game is offline we can transition straight to playing + if (m_isOfflineGame) StartGame(); + break; + case DNM_INT_STATE_JOINING_FAILED: + SetState(DNM_INT_STATE_JOINING_FAILED_TIDY_UP); + TidyUpFailedJoin(); + break; + case DNM_INT_STATE_HOSTING_FAILED: + m_multiplayerSession = nullptr; + LogComment("Error DNM_INT_STATE_HOSTING_FAILED\n"); + SetState(DNM_INT_STATE_IDLE); + break; + case DNM_INT_STATE_LEAVING_FAILED: + m_multiplayerSession = nullptr; + LogComment("Error DNM_INT_STATE_LEAVING_FAILED\n"); + SetState(DNM_INT_STATE_IDLE); + break; + } + + EnterCriticalSection(&m_csStateChangeQueue); + while(m_stateChangeQueue.size() > 0 ) + { + if( m_listener ) + { + m_listener->HandleStateChange(m_stateChangeQueue.front().m_oldState, m_stateChangeQueue.front().m_newState); + if( m_stateChangeQueue.front().m_newState == DNM_STATE_IDLE ) + { + m_isInSession = false; + } + } + m_stateExternal = m_stateChangeQueue.front().m_newState; + m_stateChangeQueue.pop(); + } + LeaveCriticalSection(&m_csStateChangeQueue); +} + +void DQRNetworkManager::Tick_CheckInviteParty() +{ + if( m_inviteReceived ) + { + if( m_CheckPartyInviteThread ) + { + if( !m_CheckPartyInviteThread->isRunning() ) + { + delete m_CheckPartyInviteThread; + m_CheckPartyInviteThread = NULL; + } + } + if( m_CheckPartyInviteThread == NULL ) + { + m_inviteReceived = false; + m_CheckPartyInviteThread = new C4JThread(&DQRNetworkManager::_CheckInviteThreadProc, this, "Check invite thread"); + m_CheckPartyInviteThread->Run(); + } + } +} + +bool DQRNetworkManager::ShouldMessageForFullSession() +{ + bool retval = m_notifyForFullParty; + m_notifyForFullParty = false; + return retval; +} + +void DQRNetworkManager::FlagInvitedToFullSession() +{ + m_notifyForFullParty = true; +} + +void DQRNetworkManager::UnflagInvitedToFullSession() +{ + m_notifyForFullParty = false; +} + +void DQRNetworkManager::AddPlayerFailed(Platform::String ^xuid) +{ + // A request to add a player (via the party) has been rejected by the host. If this is a player that we were waiting to join, then we can now: + // (1) stop waiting + // (2) remove from the party + // (3) inform the game of the failure + LogCommentFormat(L"AddPlayerFailed received, for XUID %s", xuid->Data()); + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( m_joinSessionUserMask & ( 1 << i ) ) + { + if( m_joinSessionXUIDs[i] == xuid ) + { + LogCommentFormat(L"AddPlayerFailed received, XUID matched with joining player so handling (index %d)",i); + m_joinSessionUserMask &= ~( 1 << i ); + m_joinSessionXUIDs[i] = nullptr; + m_partyController->RemoveLocalUsersFromParty(m_primaryUser, 1 << i, m_multiplayerSession->SessionReference ); + m_listener->HandleAddLocalPlayerFailed(i, true); + } + } + } +} + +// Utility method to remove the braces at the start and end of a GUID and return the remaining string +Platform::String^ DQRNetworkManager::RemoveBracesFromGuidString(__in Platform::String^ guid ) +{ + std::wstring strGuid = guid->ToString()->Data(); + + if(strGuid.length() > 0 && strGuid[0] == L'{') + { + // Remove the { + strGuid.erase(0, 1); + } + + if(strGuid.length() > 0 && strGuid[strGuid.length() - 1] == L'}') + { + // Remove the } + strGuid.erase(strGuid.end() - 1, strGuid.end()); + } + + return ref new Platform::String(strGuid.c_str()); +} + +void DQRNetworkManager::HandleSessionChange(MXSM::MultiplayerSession^ multiplayerSession) +{ + // 4J-JEV: Fix for Durango #152208 - [CRASH] Code: Gameplay: Title crashes during loading screen after signing in when prompted. + if (multiplayerSession != nullptr) + { + // 4J-JEV: This id is needed to link stats together. + // I thought setting the value from here would be less intrusive than adding an accessor. + ((DurangoStats*)GenericStats::getInstance())->setMultiplayerCorrelationId(multiplayerSession->MultiplayerCorrelationId); + } + else + { + ((DurangoStats*)GenericStats::getInstance())->setMultiplayerCorrelationId( nullptr ); + } + + m_multiplayerSession = multiplayerSession; +} + +// Utility method to update a session on the server, synchronously. +MXSM::MultiplayerSession^ DQRNetworkManager::WriteSessionHelper( MXS::XboxLiveContext^ xboxLiveContext, MXSM::MultiplayerSession^ multiplayerSession, MXSM::MultiplayerSessionWriteMode writeMode, HRESULT& hr ) +{ + if (multiplayerSession == nullptr) + { + return nullptr; + } + + auto asyncOpWriteSessionAsync = xboxLiveContext->MultiplayerService->WriteSessionAsync( multiplayerSession, writeMode ); + + MXSM::MultiplayerSession^ outputMultiplayerSession = nullptr; + + create_task(asyncOpWriteSessionAsync) + .then([&outputMultiplayerSession, &hr](task t) + { + try + { + outputMultiplayerSession = t.get(); // if t.get() didn't throw, it succeeded + } + catch ( Platform::COMException^ ex ) + { + hr = ex->HResult; + } + }) + .wait(); + + if( outputMultiplayerSession != nullptr && + outputMultiplayerSession->SessionReference != nullptr ) + { + app.DebugPrintf( "Session written OK\n" ); + } + + return outputMultiplayerSession; +} + +MXSM::MultiplayerSessionMember^ DQRNetworkManager::GetUserMemberInSession( Windows::Xbox::System::User^ user, MXSM::MultiplayerSession^ session) +{ + for each (MXSM::MultiplayerSessionMember^ member in session->Members) + { + if( _wcsicmp(member->XboxUserId->Data(), user->XboxUserId->Data()) == 0) + { + return member; + } + } + + return nullptr; +} + +bool DQRNetworkManager::IsPlayerInSession( Platform::String^ xboxUserId, MXSM::MultiplayerSession^ session, int *smallId ) +{ + if( session == nullptr ) + { + LogComment(L"IsPlayerInSession: invalid session."); + return false; + } + + for each (MXSM::MultiplayerSessionMember^ member in session->Members) + { + if( _wcsicmp(xboxUserId->Data(), member->XboxUserId->Data() ) == 0 ) + { + if( smallId ) + { + // Get small Id for this member + try + { + Windows::Data::Json::JsonObject^ customConstant = Windows::Data::Json::JsonObject::Parse(member->MemberCustomConstantsJson); + Windows::Data::Json::JsonValue^ customValue = customConstant->GetNamedValue(L"smallId"); + *smallId = (int)(customValue->GetNumber()) & 255; + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"Custom constant Parse/GetNamedValue failed", ex->HResult ); + } + } + + return true; + } + } + + return false; +} + +WXM::MultiplayerSessionReference^ DQRNetworkManager::ConvertToWindowsXboxMultiplayerSessionReference(MXSM::MultiplayerSessionReference^ sessionRef ) +{ + return ref new WXM::MultiplayerSessionReference( + sessionRef->SessionName, + sessionRef->ServiceConfigurationId, + sessionRef->SessionTemplateName + ); +} + +MXSM::MultiplayerSessionReference^ DQRNetworkManager::ConvertToMicrosoftXboxServicesMultiplayerSessionReference(WXM::MultiplayerSessionReference^ sessionRef ) +{ + return ref new MXSM::MultiplayerSessionReference( + sessionRef->ServiceConfigurationId, + sessionRef->SessionTemplateName, + sessionRef->SessionName + ); +} + +// This is called on the client, when new room sync data is received. By comparing this with the existing room sync data, +// this method is able to work out who has been added or removed from the game session, and notify the game of these events. +void DQRNetworkManager::UpdateRoomSyncPlayers(RoomSyncData *pNewSyncData) +{ + vector tempPlayers; + vector newPlayers; + + EnterCriticalSection(&m_csRoomSyncData); + + // Make temporary vector with all the current players in + for( int j = 0; j < m_roomSyncData.playerCount; j++ ) + { + tempPlayers.push_back(m_players[j]); + m_players[j] = NULL; + } + + // For each new player, it's either: + // (1) In the temp player array already, so we already knew about it. + // (2) Not in the temp player array already, so its a new player. Need to inform the game. + // And when we are done, anything left in the temporary vector must be a player that left + for( int i = 0; i < pNewSyncData->playerCount; i++ ) + { + PlayerSyncData *pNewPlayer = &pNewSyncData->players[i]; + bool bAlreadyExisted = false; + for( AUTO_VAR(it, tempPlayers.begin()); it != tempPlayers.end(); it++ ) + { + if( pNewPlayer->m_smallId == (*it)->GetSmallId() ) + { + m_players[i] = (*it); + it = tempPlayers.erase(it); + bAlreadyExisted = true; + break; + } + } + if( !bAlreadyExisted ) + { + // Create the new player, and tell the game etc. about it - the game is now free to send data via this player since it is active + if( i == 0 ) + { + // Player 0 is always the host + m_players[i] = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_HOST, false, pNewPlayer->m_channel, pNewPlayer->m_sessionAddress); + } + else + { + if( pNewPlayer->m_sessionAddress == m_XRNS_LocalAddress ) + { + m_players[i] = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_LOCAL, false, pNewPlayer->m_channel, pNewPlayer->m_sessionAddress); + } + else + { + m_players[i] = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_REMOTE, false, pNewPlayer->m_channel, pNewPlayer->m_sessionAddress); + } + } + + LogCommentFormat(L"Adding new player, index %d - type %d, small Id %d, name %s, xuid %s\n",i,m_players[i]->m_type,pNewPlayer->m_smallId,pNewPlayer->m_name,pNewPlayer->m_XUID); + + m_players[i]->SetSmallId(pNewPlayer->m_smallId); + m_players[i]->SetName(pNewPlayer->m_name); + m_players[i]->SetUID(PlayerUID(pNewPlayer->m_XUID)); + if (m_players[i]->IsLocal()) + { + m_players[i]->SetDisplayName(ProfileManager.GetDisplayName(i)); + } + + newPlayers.push_back( m_players[i] ); + } + } + for( int i = 0; i < m_roomSyncData.playerCount; i++ ) + { + delete [] m_roomSyncData.players[i].m_XUID; + } + memcpy(&m_roomSyncData, pNewSyncData, sizeof(m_roomSyncData)); + + for( int i = 0; i < tempPlayers.size(); i++ ) + { + m_listener->HandlePlayerLeaving(tempPlayers[i]); + delete tempPlayers[i]; + } + for( int i = 0; i < newPlayers.size(); i++ ) + { + m_listener->HandlePlayerJoined(newPlayers[i]); // For clients, this is where we get notified of local and remote players joining + } + LeaveCriticalSection(&m_csRoomSyncData); +} + +// This is called from the host, to add a new player into the room sync data that is then sent out to the clients. +bool DQRNetworkManager::AddRoomSyncPlayer(DQRNetworkPlayer *pPlayer, unsigned int sessionAddress, int channel) +{ + if( m_roomSyncData.playerCount == MAX_ONLINE_PLAYER_COUNT ) return false; + + EnterCriticalSection(&m_csRoomSyncData); + // Find the first entry that isn't us, to decide what to sync before. Don't consider entry #0 as this is reserved to indicate the host. + int insertAtIdx = m_roomSyncData.playerCount; + for( int i = 1; i < m_roomSyncData.playerCount; i++ ) + { + if( m_roomSyncData.players[i].m_sessionAddress != sessionAddress ) + { + insertAtIdx = i; + break; + } + else + { + // Don't add the same local index twice + if( m_roomSyncData.players[i].m_channel == channel ) + { + LeaveCriticalSection(&m_csRoomSyncData); + return false; + } + } + } + + // Make room for a new entry... + for( int i = m_roomSyncData.playerCount; i > insertAtIdx; i-- ) + { + m_roomSyncData.players[i] = m_roomSyncData.players[i-1]; + m_players[i] = m_players[i - 1]; + } + m_roomSyncData.players[insertAtIdx].m_channel = channel; + m_roomSyncData.players[insertAtIdx].m_sessionAddress = sessionAddress; + int xuidLength = pPlayer->GetUID().toString().length() + 1; // +1 for terminator + m_roomSyncData.players[insertAtIdx].m_XUID = new wchar_t [xuidLength]; + wcsncpy(m_roomSyncData.players[insertAtIdx].m_XUID, pPlayer->GetUID().toString().c_str(), xuidLength); + m_roomSyncData.players[insertAtIdx].m_smallId = pPlayer->GetSmallId(); + wcscpy_s(m_roomSyncData.players[insertAtIdx].m_name, pPlayer->GetName()); + m_players[insertAtIdx] = pPlayer; + + + m_roomSyncData.playerCount++; + + LeaveCriticalSection(&m_csRoomSyncData); + return true; +} + +// This is called from the host to remove players from the room sync data that is sent out to the clients. +// This method removes any players sharing a session address, and is used when one machine leaves the network game. +void DQRNetworkManager::RemoveRoomSyncPlayersWithSessionAddress(unsigned int sessionAddress) +{ + EnterCriticalSection(&m_csRoomSyncData); + vector removedPlayers; + int iWriteIdx = 0; + for( int i = 0; i < m_roomSyncData.playerCount; i++ ) + { + if( m_roomSyncData.players[i].m_sessionAddress == sessionAddress ) + { + removedPlayers.push_back(m_players[i]); + delete [] m_roomSyncData.players[i].m_XUID; + } + else + { + m_roomSyncData.players[iWriteIdx] = m_roomSyncData.players[i]; + m_players[iWriteIdx] = m_players[i]; + iWriteIdx++; + } + } + m_roomSyncData.playerCount = iWriteIdx; + + for( int i = 0; i < removedPlayers.size(); i++ ) + { + m_listener->HandlePlayerLeaving(removedPlayers[i]); + delete removedPlayers[i]; + memset(&m_roomSyncData.players[m_roomSyncData.playerCount + i], 0, sizeof(PlayerSyncData)); + m_players[m_roomSyncData.playerCount + i] = NULL; + } + LeaveCriticalSection(&m_csRoomSyncData); +} + +// This is called from the host a remove player from the room sync data that is sent out to the clients. +void DQRNetworkManager::RemoveRoomSyncPlayer(DQRNetworkPlayer *pPlayer) +{ + vector removedPlayers; + int iWriteIdx = 0; + for( int i = 0; i < m_roomSyncData.playerCount; i++ ) + { + if( m_players[i] == pPlayer ) + { + removedPlayers.push_back(m_players[i]); + delete [] m_roomSyncData.players[i].m_XUID; + } + else + { + m_roomSyncData.players[iWriteIdx] = m_roomSyncData.players[i]; + m_players[iWriteIdx] = m_players[i]; + iWriteIdx++; + } + } + m_roomSyncData.playerCount = iWriteIdx; + + for( int i = 0; i < removedPlayers.size(); i++ ) + { + m_listener->HandlePlayerLeaving(removedPlayers[i]); + delete removedPlayers[i]; + memset(&m_roomSyncData.players[m_roomSyncData.playerCount + i], 0, sizeof(PlayerSyncData)); + m_players[m_roomSyncData.playerCount + i] = NULL; + } +} + +// Broadcast RoomSyncData to all clients (host only) +void DQRNetworkManager::SendRoomSyncInfo() +{ + if( m_isOfflineGame ) return; + + EnterCriticalSection(&m_csRoomSyncData); + // Calculate the size of data we are going to be sending. This is composed of: + // (1) 2 byte general header + // (2) A single byte internal data tag + // (3) An unsigned int encoding the size of the combined size of all the strings in stage (5) + // (4) The RoomSyncData structure itself + // (5) A wchar NULL terminated string for every active player to encode the XUID + unsigned int xuidBytes = 0; + for( int i = 0 ; i < m_roomSyncData.playerCount; i++ ) + { + LogCommentFormat(L"Sending room sync info for player with XUID %s",m_roomSyncData.players[i].m_XUID); + xuidBytes += ( wcslen(m_roomSyncData.players[i].m_XUID) + 1 ) * sizeof(wchar_t); // 2 bytes per character, including 0 terminator + } + + unsigned int internalBytes = 1 + 4 + sizeof(RoomSyncData) + xuidBytes; + unsigned int totalBytes = 2 + internalBytes; + + unsigned char *data = new unsigned char[totalBytes]; + + uint32_t sizeHigh = internalBytes >> 8; + uint32_t sizeLow = internalBytes & 0xff; + + data[0] = 0x80 | sizeHigh; // Header - flag as internal data (0x80), sending + data[1] = sizeLow; // Data following has the a single byte to say what it is, followed by the room sync data itself + data[2] = DQR_INTERNAL_PLAYER_TABLE; + + memcpy(data + 3, &xuidBytes, 4); + memcpy(data + 7, &m_roomSyncData, sizeof(RoomSyncData)); + unsigned char *pucCurr = data + 7 + sizeof(RoomSyncData); + + for( int i = 0 ; i < m_roomSyncData.playerCount; i++ ) + { + unsigned int thisBytes = ( wcslen(m_roomSyncData.players[i].m_XUID) + 1 ) * sizeof(wchar_t); + memcpy(pucCurr, m_roomSyncData.players[i].m_XUID, thisBytes); + pucCurr += thisBytes; + } + + SendBytesRaw(-1, data, totalBytes, true); + + delete [] data; + LeaveCriticalSection(&m_csRoomSyncData); +} + +// Broadcast the fact that joining a particular XUID has failed (host only) +void DQRNetworkManager::SendAddPlayerFailed(Platform::String^ xuid) +{ + if( m_isOfflineGame ) return; + + // Calculate the size of data we are going to be sending. This is composed of: + // (1) 2 byte general header + // (2) A single byte internal data tag + // (3) An unsigned int encoding the size size of the string + // (5) A wchar NULL terminated string storing the xuid of the player which has failed to join + + unsigned int xuidBytes = sizeof(wchar_t) * ( xuid->Length() + 1 ); + + unsigned int internalBytes = 1 + 4 + xuidBytes; + unsigned int totalBytes = 2 + internalBytes; + + unsigned char *data = new unsigned char[totalBytes]; + + uint32_t sizeHigh = internalBytes >> 8; + uint32_t sizeLow = internalBytes & 0xff; + + data[0] = 0x80 | sizeHigh; // Header - flag as internal data (0x80), sending + data[1] = sizeLow; // Data following has the a single byte to say what it is, followed by the room sync data itself + data[2] = DQR_INTERNAL_ADD_PLAYER_FAILED; + + memcpy(data + 3, &xuidBytes, 4); + memcpy(data + 7, xuid->Data(), xuidBytes); + + SendBytesRaw(-1, data, totalBytes, true); + + delete [] data; +} + +// This method is used by the client to send a small Id to the host. When the host receives this on a particular communication channel, +// it then knows the association between communication channel & small Id, and that a player is actitve. +void DQRNetworkManager::SendSmallId(bool reliableAndSequential, int playerMask) +{ + // Send data with small Id setting mode - see full comment in DQRNetworkManagerEventHandlers::DataReceivedHandler + BYTE data[8]; + data[0] = 0x80; // Send 6 bytes, internal mode. Send on channel 0 but this is ignored. + data[1] = 6; + data[2] = DQR_INTERNAL_ASSIGN_SMALL_IDS; // Internal data type + data[3] = playerMask; // Actual id + data[4] = 0; + data[5] = 0; + data[6] = 0; + data[7] = 0; + + bool bError = false; + for( int j = 0; j < MAX_LOCAL_PLAYER_COUNT; j++ ) + { + if( playerMask & ( 1 << j ) ) + { + bool bFound = false; + for( unsigned int i = 0; i < m_multiplayerSession->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = m_multiplayerSession->Members->GetAt(i); + + if( member->XboxUserId == m_joinSessionXUIDs[j] ) + { + BYTE smallId = 0; + try + { + Windows::Data::Json::JsonObject^ customConstant = Windows::Data::Json::JsonObject::Parse(member->MemberCustomConstantsJson); + Windows::Data::Json::JsonValue^ customValue = customConstant->GetNamedValue(L"smallId"); + smallId = (BYTE)(customValue->GetNumber()); + bFound = true; + } + catch (Platform::COMException^ ex) + { + bError = true; + LogCommentWithError( L"Custom constant Parse/GetNamedValue failed", ex->HResult ); + } + + m_channelFromSmallId[smallId] = j; + + data[ 4 + j ] = smallId; + m_connectionInfoClient.m_smallId[ j ] = smallId; + LogCommentFormat( L"SendSmallId, channel %d, id %d\n", m_channelFromSmallId[smallId], smallId); + } + } + if( !bFound ) + { + bError = true; + } + } + } + + assert(bError == false ); + + SendBytesRaw(0, data, 8, reliableAndSequential); +} + +// This is sent by the client to the host, acting to undo a previous SendSmallId call +void DQRNetworkManager::SendUnassignSmallId(int playerIndex) +{ + LogCommentFormat( L"SendUnassignSmallId, channel %d\n", playerIndex); + // Send data with small Id setting mode - see full comment in DQRNetworkManagerEventHandlers::DataReceivedHandler + BYTE data[4]; + data[0] = 0x80 | ( playerIndex << 5 ); // Send 1 byte, internal mode + data[1] = 1; + data[2] = DQR_INTERNAL_UNASSIGN_SMALL_ID; // Internal data type + + SendBytesRaw(0, data, 3, true); +} + +// This method gets the player index within the session document for a particular small Id. +int DQRNetworkManager::GetSessionIndexForSmallId(unsigned char smallId) +{ + for( unsigned int i = 0; i < m_multiplayerSession->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = m_multiplayerSession->Members->GetAt(i); + BYTE smallIdMember = 0; + try + { + Windows::Data::Json::JsonObject^ customConstant = Windows::Data::Json::JsonObject::Parse(member->MemberCustomConstantsJson); + Windows::Data::Json::JsonValue^ customValue = customConstant->GetNamedValue(L"smallId"); + smallIdMember = (BYTE)(customValue->GetNumber()); + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"Custom constant Parse/GetNamedValue failed", ex->HResult ); + } + if( smallIdMember == smallId ) + { + return i; + } + } + return -1; +} + +// This method gets the player index and small id for the host, which is sent with a small id that has 256 added to it +int DQRNetworkManager::GetSessionIndexAndSmallIdForHost(unsigned char *smallId) +{ + for( unsigned int i = 0; i < m_multiplayerSession->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = m_multiplayerSession->Members->GetAt(i); + int smallIdMember = 0; + try + { + Windows::Data::Json::JsonObject^ customConstant = Windows::Data::Json::JsonObject::Parse(member->MemberCustomConstantsJson); + Windows::Data::Json::JsonValue^ customValue = customConstant->GetNamedValue(L"smallId"); + smallIdMember = customValue->GetNumber(); + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"Custom constant Parse/GetNamedValue failed", ex->HResult ); + } + if( smallIdMember > 255 ) + { + *smallId = (BYTE)(smallIdMember); + return i; + } + } + return -1; +} + +// Connection info is used to store the current state of a communcation endpoint, on both host & client +DQRConnectionInfo::DQRConnectionInfo() +{ + Reset(); +} + +void DQRConnectionInfo::Reset() +{ + m_currentChannel = 0; + m_internalFlag = false; + m_bytesRemaining = 0; + m_internalDataState = ConnectionState_InternalHeaderByte; + for( int i = 0; i < 4; i++ ) + { + m_smallId[i] = 0; + m_channelActive[i] = false; + } + m_state = ConnectionState_HeaderByte0; + m_initialPacketReceived = false; +} + +// This method allocates the next available small id, returning it as a json formatted named value so that it can be inserted as custom data in the session document +Platform::String^ DQRNetworkManager::GetNextSmallIdAsJsonString() +{ + Windows::Data::Json::JsonObject^ customConstant = ref new Windows::Data::Json::JsonObject(); + + // The host sends it small Id with 256 added to it, so we can determine which player is the host easily at the client side + int smallIdToSend = m_currentSmallId; + if( smallIdToSend == m_hostSmallId ) + { + smallIdToSend += 256; + } + customConstant->Insert(L"smallId", Windows::Data::Json::JsonValue::CreateNumberValue( smallIdToSend )); + m_sessionAddressFromSmallId[m_currentSmallId++] = 0; + + return customConstant->Stringify(); +} + +int DQRNetworkManager::_HostGameThreadProc( void* lpParameter ) +{ + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->HostGameThreadProc(); +} + +// This is the main asynchronous method that is called when hosting a game (initiated by calling ::createAndJoinSession). This is called for +// both online & offline games, and it must: +// (1) Create a new multiplayer session document, with active players for all local players starting the game +// (2) Create a new game party, with matching players in it (possibly clearing any existing party) +// (3) Get a device token for the host & assign to the session +// (4) Initialise the room sync data, and inform the game of all local players joining at this stage + +int DQRNetworkManager::HostGameThreadProc() +{ + Platform::String^ sessionName; + + // Get primary user that we are going to create the session with, and create an xbox live context from it + + WXS::User^ primaryUser = ProfileManager.GetUser(0); + m_primaryUser = primaryUser; + m_primaryUserXboxLiveContext = nullptr; + if( primaryUser == nullptr ) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + + int localSessionAddress = 0; + + if( !m_isOfflineGame ) + { + MXS::XboxLiveContext^ primaryUserXBLContext = ref new MXS::XboxLiveContext(primaryUser); + m_primaryUserXboxLiveContext = primaryUserXBLContext; + if( primaryUserXBLContext == nullptr ) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + + EnableDebugXBLContext(m_primaryUserXboxLiveContext); + + // Get a globally unique identifier to use as our session name that we are going to create + GUID sessionNameGUID; + CoCreateGuid( &sessionNameGUID ); + Platform::Guid sessionGuidName = Platform::Guid( sessionNameGUID ); + sessionName = RemoveBracesFromGuidString( sessionGuidName.ToString() ); + + MXSM::MultiplayerSession^ session = nullptr; + // Actually create the session (locally), using the primary player's context + try + { + session = ref new MXSM::MultiplayerSession( primaryUserXBLContext, + ref new MXSM::MultiplayerSessionReference( SERVICE_CONFIG_ID, MATCH_SESSION_TEMPLATE_NAME, sessionName ), + 0, // this means that it will use the maxMembers specified in the session template. + false, + MXSM::MultiplayerSessionVisibility::Open, + nullptr, + nullptr + ); + } + catch (Platform::COMException^ ex) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + + // Set custom property with the game session data + session->SetSessionCustomPropertyJson( L"GameSessionData", Windows::Data::Json::JsonValue::CreateStringValue( base64_encode(m_customSessionData, m_customSessionDataSize ) )->Stringify() ); + + // More custom data, for the XUIDs of the players to match our room sync data. This isn't itself set up at t:his point but we know what is going in it. + Windows::Data::Json::JsonArray ^currentXuidArray = ref new Windows::Data::Json::JsonArray(); + for( int i = 0 ; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( m_currentUserMask & ( 1 << i ) ) + { + WXS::User^ newUser = ProfileManager.GetUser(i); + if( newUser != nullptr ) + { + currentXuidArray->Append( Windows::Data::Json::JsonValue::CreateStringValue( newUser->XboxUserId ) ); + } + else + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + } + } + session->SetSessionCustomPropertyJson( L"RoomSyncData", currentXuidArray->Stringify() ); + + // Join session with the primary user for whom the session was created (via their xbox live context) + // user to store the small Id + m_hostSmallId = m_currentSmallId; + m_sessionAddressFromSmallId[m_hostSmallId] = 0; + + session->Join( GetNextSmallIdAsJsonString(), true ); + session->SetCurrentUserStatus( MXSM::MultiplayerSessionMemberStatus::Active ); + + // Get device ID for current user & set in the session + Platform::String^ secureDeviceAddress = WXN::SecureDeviceAddress::GetLocal()->GetBase64String(); + session->SetCurrentUserSecureDeviceAddressBase64( secureDeviceAddress ); + LogComment(L"Setting host secure device: " + secureDeviceAddress + L"\n"); + + // If there is a party currently, then remove all our local players from it so that we can start our own one + m_partyController->RefreshPartyView(); + WXM::PartyView^ partyView = m_partyController->GetPartyView(); + + if( partyView != nullptr ) + { + m_partyController->RemoveLocalUsersFromParty(primaryUser); + } + + m_partyController->AddLocalUsersToParty(m_currentUserMask, primaryUser); + partyView = m_partyController->GetPartyView(); + + // If there is no party by this stage, then we can't proceed. + if( partyView == nullptr ) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + m_partyController->SetJoinability(m_listener->IsSessionJoinable()); + + // Add reservations for anyone in the party, who isn't the primary player. Just adding local players for now, but perhaps this should add + // other party members at this stage? + for ( WXM::PartyMember^ member : partyView->Members ) + { + if( member->IsLocal ) + { + if ( _wcsicmp(primaryUser->XboxUserId->Data(), member->XboxUserId->Data()) != 0) + { + session->AddMemberReservation( member->XboxUserId, GetNextSmallIdAsJsonString(), true ); + LogCommentFormat( L"Added %s to session\n", member->XboxUserId->Data()); + } + } + } + + // This is everything now set up locally as we want to start the session. Now attempt to write the session data to the server - will return a valid new session object if we succeeeded. + HRESULT hr = S_OK; + session = WriteSessionHelper( primaryUserXBLContext, session, MXSM::MultiplayerSessionWriteMode::UpdateOrCreateNew, hr ); + + // It is important to set the session as soon as we have written it, so that if we get any incoming events (such as the game session ready one) then we will be aware that we are actually already in the session + HandleSessionChange(session); + + // If this was successful, then do further set up of the session + if( session != nullptr ) + { + // Get reference to the current user within the session + MXSM::MultiplayerSessionMember^ hostMember = GetUserMemberInSession(primaryUser, session); + + // Set the device token of the host from this user (since we're hosting) + session->SetHostDeviceToken( hostMember->DeviceToken ); + + m_partyController->RegisterGamePlayersChangedEventHandler(); + + // Update session on the server + HRESULT hr = S_OK; + session = WriteSessionHelper( primaryUserXBLContext, session, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr ); + + if ( FAILED(hr) ) + { + app.DebugPrintf("Failed setting host device token"); + + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + + // Convert the session reference (in Microsoft::Xbox::Services::Multiplayer namespace) to Windows::Xbox::Multiplayer names space so we can use in the party system + WXM::MultiplayerSessionReference^ winSessionRef = ConvertToWindowsXboxMultiplayerSessionReference(session->SessionReference); + + // Register this multiplayer session as the current session for the party + auto registerSessionAsync = WXM::Party::RegisterGameSessionAsync(primaryUser, winSessionRef); + create_task(registerSessionAsync).then([this](task t) + { + try + { + t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"RegisterGameSessionAsync failed", ex->HResult ); + + SetState(DNM_INT_STATE_HOSTING_FAILED); + } + }) + .wait(); + if( m_state == DNM_INT_STATE_HOSTING_FAILED) return 0; + + m_partyController->RefreshPartyView(); + + // We've now created the session with our local player in it, and reserved slots for everyone else in the party. We've also set the party to say that + // the current game the party are playing is this session. We can now request that the reserved player's be pulled into the game. For people not currently running the + // game, this will ask them if they want to start playing, and for people already in the title it will send an even to say that the game is ready. + auto pullPlayersAsync = WXM::Party::PullReservedPlayersAsync(primaryUser, winSessionRef); + create_task(pullPlayersAsync).then([this](task t) + { + try + { + t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"PullReservedPlayersAsync failed", ex->HResult ); + + // SetState(DNM_INT_STATE_HOSTING_FAILED); // This does seem to fail (harmlessly?) with a code of 0x87cc0008 sometimes + } + }) + .wait(); + if( m_state == DNM_INT_STATE_HOSTING_FAILED) return 0; + + sockaddr_in6 localSocketAddressStorage; + + ZeroMemory(&localSocketAddressStorage, sizeof(localSocketAddressStorage)); + + localSocketAddressStorage.sin6_family = AF_INET6; + localSocketAddressStorage.sin6_port = htons(m_associationTemplate->AcceptorSocketDescription->BoundPortRangeLower); + + memcpy(&localSocketAddressStorage.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + + m_localSocketAddress = Platform::ArrayReference(reinterpret_cast(&localSocketAddressStorage), sizeof(localSocketAddressStorage)); + + // This shouldn't ever happen, but seems worth checking that we don't have a pre-existing session in case there's any way to get here with one already running + if( m_XRNS_Session ) + { + RTS_Terminate(); + do + { + Sleep(20); + } while ( m_XRNS_Session != nullptr ); + } + + // Start a new session (this actually happens in the RTS processing thread), and wait until it exists + RTS_StartHost(); + do + { + Sleep(20); + } while ( m_XRNS_Session == nullptr ); + + // Set any other local players that we added as reserved in the session, to be active. This must be done by getting and writing the multiplayer session + // document from each player's xbox live context in turn + + for( int i = 1; i < 4; i++ ) + { + if( m_currentUserMask & ( 1 << i ) ) + { + WXS::User^ extraUser = ProfileManager.GetUser(i); + if( extraUser == nullptr ) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + MXS::XboxLiveContext^ extraUserXBLContext = ref new MXS::XboxLiveContext(extraUser); + if( extraUserXBLContext == nullptr ) + { + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + + auto asyncOp = extraUserXBLContext->MultiplayerService->GetCurrentSessionAsync( session->SessionReference ); + create_task(asyncOp).then([this, extraUserXBLContext, &session] (task t) + { + try + { + MXSM::MultiplayerSession^ currentSession = t.get(); + + currentSession->SetCurrentUserStatus(MXSM::MultiplayerSessionMemberStatus::Active); + HRESULT hr = S_OK; + session = WriteSessionHelper(extraUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr); + } + catch ( Platform::COMException^ ex ) + { + } + }).wait(); + } + } + + HandleSessionChange(session); + } + else + { + app.DebugPrintf("Error creating session"); + SetState(DNM_INT_STATE_HOSTING_FAILED); + return 0; + } + m_XRNS_LocalAddress = m_XRNS_Session->LocalSessionAddress; + m_XRNS_OldestAddress = m_XRNS_Session->OldestSessionAddress; + localSessionAddress = m_XRNS_Session->LocalSessionAddress; + } + else + { + m_hostSmallId = m_currentSmallId; + // Offline game - get small id incremented to the same value that it would have ended up with + for( int i = 0; i < 4; i++ ) + { + if( m_currentUserMask & ( 1 << i ) ) + { + m_currentSmallId++; + } + } + } + + // At this stage, set the local players up. We know we'll have created these with smallIds from m_hostSmallId (for the host) to a max of m_hostSmallId+3 for the other local players + int smallId = m_hostSmallId; + for( int i = 0; i < 4; i++ ) + { + // If user is present in mask and currently signed in + if( m_currentUserMask & ( 1 << i ) && ProfileManager.IsSignedIn(i)) + { + auto user = ProfileManager.GetUser(i); + wstring displayName = ProfileManager.GetDisplayName(i); + + DQRNetworkPlayer* pPlayer = new DQRNetworkPlayer(this, ( ( smallId == m_hostSmallId ) ? DQRNetworkPlayer::DNP_TYPE_HOST : DQRNetworkPlayer::DNP_TYPE_LOCAL ), true, i, localSessionAddress); + pPlayer->SetSmallId(smallId); + pPlayer->SetName(user->DisplayInfo->Gamertag->Data()); + pPlayer->SetDisplayName(displayName); + pPlayer->SetUID(PlayerUID(user->XboxUserId->Data())); + + AddRoomSyncPlayer( pPlayer, localSessionAddress, i); + + m_listener->HandlePlayerJoined(pPlayer); + + smallId++; + } + } + + SetState(DNM_INT_STATE_HOSTING_WAITING_TO_PLAY); + + return 0; +} + +int DQRNetworkManager::_LeaveRoomThreadProc( void* lpParameter ) +{ + + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->LeaveRoomThreadProc(); +} + +int DQRNetworkManager::LeaveRoomThreadProc() +{ + WXS::User^ primaryUser = ProfileManager.GetUser(0, true); + + LogComment(L"LeaveRoomThreadProc"); + + if( !m_isOfflineGame && m_multiplayerSession != nullptr ) + { + // If we are hosting, then we need to disassociate the gaming session from the party. We also need to make sure that we don't subscribe to gameplayer events anymore + // or else if we subsequently become a client in another game, things will get confused + if( m_isHosting ) + { + m_partyController->DisassociateSessionFromParty( m_multiplayerSession->SessionReference ); + + m_partyController->UnregisterGamePlayersEventHandler(); + } + + // Remove local players from the party + m_partyController->RemoveLocalUsersFromParty(primaryUser, m_currentUserMask, m_multiplayerSession->SessionReference); + + // Request RTS to be terminated + RTS_Terminate(); + + // Now leave the game session. We need to do this for each player in turn, writing each time + bool bError = false; + for( int i = 0; i < 4; i++ ) + { + if( m_currentUserMask & ( 1 << i ) ) + { + LogCommentFormat(L"DQRNetworkManager::LeaveRoomThreadProc: Attempting to remove player %d from session",i); + WXS::User^ leavingUser = ProfileManager.GetUser(i); + if( leavingUser == nullptr ) + { + bError = true; + continue; + } + if( m_chat ) + { + m_chat->RemoveLocalUser(leavingUser); + } + + MXS::XboxLiveContext^ leavingUserXBLContext = ref new MXS::XboxLiveContext(leavingUser); + if( leavingUserXBLContext == nullptr ) + { + bError = true; + continue; + } + + auto asyncOp = leavingUserXBLContext->MultiplayerService->GetCurrentSessionAsync( m_multiplayerSession->SessionReference ); + create_task(asyncOp).then([this, leavingUserXBLContext,&bError] (task t) + { + try + { + MXSM::MultiplayerSession^ currentSession = t.get(); + + if (currentSession != nullptr) + { + currentSession->Leave(); + HRESULT hr = S_OK; + WriteSessionHelper(leavingUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr); + } + else + { + // Specific error case where session is gone, this generally happens if all players have left (e.g. party of one and player signs out) + app.DebugPrintf("DQRNetworkManager::LeaveRoomThreadProc: Error removing a player from the session, session was null (user probably signed out)"); + } + } + catch ( Platform::COMException^ ex ) + { + bError = true; + } + }).wait(); + } + } + + if ( bError ) + { + app.DebugPrintf("DQRNetworkManager::LeaveRoomThreadProc: Error removing a player from the session"); + assert(true); + } + } + + ProfileManager.CompleteDeferredSignouts(); + app.DebugPrintf("DQRNetworkManager::LeaveRoomThreadProc: Completing deferred sign out"); + ProfileManager.SetDeferredSignoutEnabled(false); + + m_multiplayerSession = nullptr; + m_currentUserMask = 0; + m_joinSessionUserMask = 0; + UnflagInvitedToFullSession(); + + SetState(DNM_INT_STATE_ENDING); + + return 0; +} + +int DQRNetworkManager::_TidyUpJoinThreadProc( void* lpParameter ) +{ + + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->TidyUpJoinThreadProc(); +} + +// This method is called when joining the game fails in some situations, ie when we attempted to join with a +// set of players and not all managed to get into the session. Tidies things up by removing any players that +// Did get into the session, and removing any players we added to the party. +int DQRNetworkManager::TidyUpJoinThreadProc() +{ + WXS::User^ primaryUser = ProfileManager.GetUser(0); + + LogComment(L"TidyUpJoinThreadProc"); + + if( primaryUser != nullptr ) + { + // Remove any local users at all who are in the party - but first get a session reference from the party whilst we still have it + m_partyController->RefreshPartyView(); + MXSM::MultiplayerSessionReference ^sessionRef = m_partyController->GetGamePartySessionReference(); + m_partyController->RemoveLocalUsersFromParty(primaryUser); + + if( sessionRef != nullptr ) + { + // Now leave the game session. We need to do this for each player in turn, writing each time. Consider that any of the joining + // members *may* have a slot (reserved or active) depending on how far progressed the joining got. + bool bError = false; + + // We can fail to join at various points, and in at least one case (if it is down to RUDP unreliable packets timing out) then we don't have m_joinSessionUserMask bits set any more, + // but we Do have m_currentUserMask set. Any of these should be considered users we should be attempting to remove from the session. + int removeSessionMask = m_joinSessionUserMask | m_currentUserMask; + for( int i = 0; i < 4; i++ ) + { + if( removeSessionMask & ( 1 << i ) ) + { + LogCommentFormat(L"Attempting to remove player %d from session",i); + WXS::User^ leavingUser = ProfileManager.GetUser(i); + if( leavingUser == nullptr ) + { + bError = true; + continue; + } + MXS::XboxLiveContext^ leavingUserXBLContext = ref new MXS::XboxLiveContext(leavingUser); + if( leavingUserXBLContext == nullptr ) + { + bError = true; + continue; + } + + EnableDebugXBLContext(leavingUserXBLContext); + + auto asyncOp = leavingUserXBLContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(asyncOp).then([this, leavingUser, leavingUserXBLContext,&bError] (task t) + { + try + { + MXSM::MultiplayerSession^ currentSession = t.get(); + + if( currentSession ) + { + bool bFound = false; + for( int i = 0; i < currentSession->Members->Size; i++ ) + { + if( currentSession->Members->GetAt(i)->XboxUserId == leavingUser->XboxUserId ) + { + bFound = true; + break; + } + } + + if( bFound ) + { + currentSession->Leave(); + HRESULT hr = S_OK; + WriteSessionHelper(leavingUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr); + } + } + } + catch ( Platform::COMException^ ex ) + { + bError = true; + } + }).wait(); + } + } + + if ( bError ) + { + app.DebugPrintf("Error removing a player from the session"); + assert(true); + } + } + } + + m_multiplayerSession = nullptr; + m_currentUserMask = 0; + m_joinSessionUserMask = 0; + + // Fixing up things from joining needs to go straight from joining -> idle to be properly detected as a failed join by the higher-level networking systems + SetState(DNM_INT_STATE_IDLE); + + return 0; +} + +int DQRNetworkManager::_UpdateCustomSessionDataThreadProc( void* lpParameter ) +{ + + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->UpdateCustomSessionDataThreadProc(); +} + +// This method is called to updat the custom session data associated with the multiplayer session. This is done only on the host. The custom data is specified +// when we create the game session (when calling CreateAndJoinSession) as a region of memory so that this layer doesn't have to be concerned with what it +// represents, however we use it to synchronise a GameSessionData structure containing details of the current game session. This data is base 64 encoded +// and then put in the session document as a custom value as a json string name/value pair. +int DQRNetworkManager::UpdateCustomSessionDataThreadProc() +{ + LogComment(L"Starting thread to update custom data"); + WXS::User^ primaryUser = ProfileManager.GetUser(0); + + if( primaryUser == nullptr ) + { + return 0; + } + MXS::XboxLiveContext^ primaryUserXBLContext = ref new MXS::XboxLiveContext(primaryUser); + if( primaryUserXBLContext == nullptr ) + { + return 0; + } + + MXSM::MultiplayerSession^ session = nullptr; + + auto multiplayerSessionAsync = primaryUserXBLContext->MultiplayerService->GetCurrentSessionAsync( m_multiplayerSession->SessionReference ); + create_task(multiplayerSessionAsync).then([&session,this](task t) + { + try + { + session = t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"MultiplayerSession failed", ex->HResult ); + } + }) + .wait(); + + if( session != nullptr ) + { + // Set custom property with the game session data + session->SetSessionCustomPropertyJson( L"GameSessionData", Windows::Data::Json::JsonValue::CreateStringValue( base64_encode(m_customSessionData, m_customSessionDataSize ) )->Stringify() ); + + // Update XUIDs from room sync data which we also have as custom data + EnterCriticalSection(&m_csRoomSyncData); + Windows::Data::Json::JsonArray ^currentXuidArray = ref new Windows::Data::Json::JsonArray(); + for( int i = 0 ; i < m_roomSyncData.playerCount; i++ ) + { + currentXuidArray->Append( Windows::Data::Json::JsonValue::CreateStringValue( ref new Platform::String(m_roomSyncData.players[i].m_XUID ) ) ); + } + session->SetSessionCustomPropertyJson( L"RoomSyncData", currentXuidArray->Stringify() ); + LeaveCriticalSection(&m_csRoomSyncData); + + HRESULT hr = S_OK; + WriteSessionHelper( primaryUserXBLContext, session, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr ); + + // If we didn't succeed, then retry later + if( hr != S_OK ) + { + if( m_customDataDirtyUpdateTicks == 0 ) + { + LogCommentFormat(L"Error updating custom data 0x%x, will retry", hr); + m_customDataDirtyUpdateTicks = 20; + } + } + } + + LogComment(L"Ending thread to update custom data"); + + return 0; +} + +int DQRNetworkManager::_CheckInviteThreadProc(void* lpParameter) +{ + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->CheckInviteThreadProc(); +} + +int DQRNetworkManager::CheckInviteThreadProc() +{ + for( int i = 4; i < 12; i++ ) + { + WXS::User^ anyUser = InputManager.GetUserForGamepad(i); + if( anyUser ) + { + m_partyController->CheckPartySessionFull(anyUser); + return 0; + } + } + return 0; +} + +// This method is called by the the party controller if a new party is found for a local player. This will happen after the party system +// has called HandlePlayerRemovedFromParty, if the player is being removed from one party only to be placed in another. If Only +// HandlePlayerRemovedFromParty is called (with no following HandleNewPartyFoundForPlayer), then we must assume that the player was +// just removed from a party, and Isn't moving to another party. We try to differentiate between these two events by allowing a window of time +// to pass after a user is removed from a party before processing it. +void DQRNetworkManager::HandleNewPartyFoundForPlayer() +{ + LogCommentFormat(L"HandleNewPartyFoundForPlayer called after %d ms\n", System::currentTimeMillis() - m_playersLeftPartyTime); + m_playersLeftParty = 0; +} + +void DQRNetworkManager::HandlePlayerRemovedFromParty(int playerMask) +{ + if( m_state == DNM_INT_STATE_PLAYING ) + { + if( m_isHosting ) + { + // We should check that they're still here, they might already have left + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + if (!ProfileManager.IsSignedIn(i)) + { + // Player is already gone so remove them from the mask + playerMask &= ~(1 << i); + } + } + } + + LogCommentFormat(L"HandlePlayerRemovedFromParty called mask %d\n", playerMask); + m_playersLeftParty |= playerMask; + m_playersLeftPartyTime = System::currentTimeMillis(); + + // Check for forced sign out + CheckForcedSignOut(); + } + else + { + // As a client, we don't have any messy changing to offline game or saving etc. to do, so we can respond immediately to leaving the party + if( playerMask & 1 ) + { + DQRNetworkManager::LogComment(L"Primary player on this system has left the party - leaving game\n"); + app.SetDisconnectReason(DisconnectPacket::eDisconnect_ExitedGame); + LeaveRoom(); + } + else + { + // Secondary player(s) leaving, just remove as if they had chosen to exit themselves from the game + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + RemoveLocalPlayerByUserIndex(i); + } + } + } + } + } + else + { + LogComment(L"HandlePlayerRemovedFromParty called, ignoring as not in state DNM_INT_STATE_PLAYING\n"); + } +} + +// Method called by the DQRNetworkManager when we need to inform the chat system that a new player has been added to the game. We don't do anything +// directly here, as this ends up getting called whilst we are in a handler for receiving network data, and telling the chat system that a player +// has joined causes it to direct attempt to send data on the network, causing us to lock up. This is processed in the tick instead. +void DQRNetworkManager::ChatPlayerJoined(int idx) +{ + EnterCriticalSection(&m_csVecChatPlayers); + m_vecChatPlayersJoined.push_back(idx); + LeaveCriticalSection(&m_csVecChatPlayers); +} + +bool DQRNetworkManager::IsReadyToPlayOrIdle() +{ + return (( m_state == DNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || ( m_state == DNM_INT_STATE_PLAYING ) || ( m_state == DNM_INT_STATE_IDLE ) ); +} + +void DQRNetworkManager::StartGame() +{ + SetState( DNM_INT_STATE_STARTING); + SetState( DNM_INT_STATE_PLAYING); +} + +// Removes all local players from a network game (aysnchronously) and leaves the game +void DQRNetworkManager::LeaveRoom() +{ + m_playersLeftParty = 0; + if( ( m_state == DNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || + ( m_state == DNM_INT_STATE_STARTING ) || + ( m_state == DNM_INT_STATE_PLAYING ) ) + { + SetState(DNM_INT_STATE_LEAVING); + + // Empty out the room of players & notify the game (needs separate loops for each as HandlePlayerLeaving checks the players) + for( int i = 0; i < m_roomSyncData.playerCount; i++ ) + { + m_listener->HandlePlayerLeaving(m_players[i]); + } + + for( int i = 0; i < m_roomSyncData.playerCount; i++ ) + { + delete m_players[i]; + m_players[i] = NULL; + } + memset(&m_roomSyncData, 0, sizeof(m_roomSyncData)); + m_displayNames.clear(); + + m_LeaveRoomThread = new C4JThread(&DQRNetworkManager::_LeaveRoomThreadProc, this, "Leave room"); + m_LeaveRoomThread->Run(); + } + else + { + SetState(DNM_INT_STATE_IDLE); + } +} + +void DQRNetworkManager::TidyUpFailedJoin() +{ + m_TidyUpJoinThread = new C4JThread(&DQRNetworkManager::_TidyUpJoinThreadProc, this, "Tidying up failed join"); + m_TidyUpJoinThread->Run(); +} + +// This is called when we get notification that the user has accepted an invite toast - this is a good point to check the session that the party is associated with, +// and see if it is full of people that aren't us +void DQRNetworkManager::SetInviteReceivedFlag() +{ + m_inviteReceived = true; +} + +// The "Party process" is picked up in the tick and used to join games from invites etc. This method is static so it can be called from bits of the game that aren't really tied in with the network libs. +void DQRNetworkManager::SetPartyProcessJoinParty() +{ + m_partyProcess = DQRNetworkManager::DNM_PARTY_PROCESS_JOIN_PARTY; +} + +// The "Party process" is picked up in the tick and used to join games from invites etc. This method is static so it can be called from bits of the game that aren't really tied in with the network libs. +void DQRNetworkManager::SetPartyProcessJoinSession(int bootUserIndex, Platform::String^ bootSessionName, Platform::String^ bootServiceConfig, Platform::String^ bootSessionTemplate) +{ + if( s_pDQRManager ) + { + // Don't do anything if we are already in this session + if( s_pDQRManager->m_multiplayerSession ) + { + if( s_pDQRManager->m_multiplayerSession->SessionReference->SessionName == bootSessionName ) + { + return; + } + } + } + m_bootUserIndex = bootUserIndex; + m_bootSessionName = bootSessionName->Data(); + m_bootServiceConfig = bootServiceConfig->Data(); + m_bootSessionTemplate = bootSessionTemplate->Data(); + m_partyProcess = DQRNetworkManager::DNM_PARTY_PROCESS_JOIN_SPECIFIED; +} + +// Brings up the system GUI so that invites can be sent to invite friends to join the current game party +void DQRNetworkManager::SendInviteGUI(int quadrant) +{ + Windows::Xbox::UI::SystemUI::ShowSendInvitesAsync(ProfileManager.GetUser(quadrant)); +} + +bool DQRNetworkManager::IsAddingPlayer() +{ + return ( m_addLocalPlayerState != DNM_ADD_PLAYER_STATE_IDLE ); +} + +// Attempt to join a party that we have found, for a given set of local players. This adds the specified users to the +// party, and then we have to wait for the host to detect this change, and (hopefully) reserve slots for us in the game session. When our +// slots are reserved, we will receive a game session ready notification through the party manager & can take things from there. +bool DQRNetworkManager::JoinPartyFromSearchResult(SessionSearchResult *searchResult, int playerMask) +{ + // Assume that primary player is always player 0 + if( ( playerMask & 1 ) == 0 ) + { + return false; + } + + // Gather set of XUIDs for the players that we are joining with + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + WXS::User^ user = ProfileManager.GetUser(i); + if( user == nullptr ) + { + return false; + } + m_joinSessionXUIDs[i] = user->XboxUserId; + } + else + { + m_joinSessionXUIDs[i] = nullptr; + } + } + + // Set up primary user & context to be used generally by other things once we are in the game + m_primaryUser = ProfileManager.GetUser(0); + if( m_primaryUser == nullptr ) + { + return false; + } + + m_primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(m_primaryUser); + if( m_primaryUserXboxLiveContext == nullptr ) + { + return false; + } + + m_currentUserMask = 0; + m_joinSessionUserMask = playerMask; + m_isInSession = true; + m_isOfflineGame = false; + + m_startedWaitingForReservationsTime = System::currentTimeMillis(); + SetState(DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS); + + // There is a small window that we currently allow cancelling + m_cancelJoinFromSearchResult = false; + + // Before we join the party, check if any of the joining players are in the session already, and remove. Joining the game cleanly depends + // on the host detecting us joining the party and then adding us (as reserved) to the session so it can pull us in, which it can't do + // if we are already in the session + + MXSM::MultiplayerSessionReference ^sessionRef = ref new MXSM::MultiplayerSessionReference(SERVICE_CONFIG_ID, MATCH_SESSION_TEMPLATE_NAME, ref new Platform::String(searchResult->m_sessionName.c_str())); + + bool shownCancelScreen = false; + if( sessionRef != nullptr ) + { + // Allow 2 seconds before we let the player cancel + __int64 allowCancelTime = System::currentTimeMillis() + (1000 * 2); + + // Now leave the game session. We need to do this for each player in turn, writing each time. Consider that any of the joining + // members *may* have a slot (reserved or active) depending on how far progressed the joining got. + bool bError = false; + for( int i = 0; i < 4; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + LogCommentFormat(L"Attempting to remove player %d from session",i); + WXS::User^ leavingUser = ProfileManager.GetUser(i); + if( leavingUser == nullptr ) + { + bError = true; + continue; + } + MXS::XboxLiveContext^ leavingUserXBLContext = ref new MXS::XboxLiveContext(leavingUser); + if( leavingUserXBLContext == nullptr ) + { + bError = true; + continue; + } + + EnableDebugXBLContext(leavingUserXBLContext); + + auto asyncOp = leavingUserXBLContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + auto ccTask = create_task(asyncOp).then([this, leavingUser, leavingUserXBLContext,&bError] (task t) + { + try + { + MXSM::MultiplayerSession^ currentSession = t.get(); + + if( currentSession == nullptr ) + { + bError = true; + } + else + { + bool bFound = false; + for( int i = 0; i < currentSession->Members->Size; i++ ) + { + if( currentSession->Members->GetAt(i)->XboxUserId == leavingUser->XboxUserId ) + { + bFound = true; + break; + } + } + + if( bFound ) + { + currentSession->Leave(); + HRESULT hr = S_OK; + WriteSessionHelper(leavingUserXBLContext, currentSession, MXSM::MultiplayerSessionWriteMode::UpdateExisting, hr); + } + } + } + catch ( Platform::COMException^ ex ) + { + bError = true; + } + }); + + while(!ccTask.is_done()) + { + // Check for being disconnected from the network + if(!ProfileManager.IsSignedInLive(i) || m_cancelJoinFromSearchResult) + { + asyncOp->Cancel(); + bError = true; + break; + } + + __int64 currentTime = System::currentTimeMillis(); + if( currentTime > allowCancelTime) + { + shownCancelScreen = true; + + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = 0; + param->stringId = -1; + param->showTooltips = true; + param->cancelFunc = &g_NetworkManager.CancelJoinGame; + param->cancelFuncParam = &g_NetworkManager; + + // Show a progress spinner so that we can cancel the join. + ui.NavigateToScene(0, eUIScene_ConnectingProgress, param); + + allowCancelTime = LONGLONG_MAX; + } + + // Tick some simple things + ProfileManager.Tick(); + StorageManager.Tick(); + InputManager.Tick(); + RenderManager.Tick(); + ui.tick(); + ui.render(); + RenderManager.Present(); + } + // Check for being disconnected from the network + if(!ProfileManager.IsSignedInLive(i)) + { + bError = true; + break; + } + } + } + + if ( bError && !m_cancelJoinFromSearchResult ) + { + app.DebugPrintf("Error removing a player from the session"); + assert(true); + } + } + + if(shownCancelScreen) + { + ui.NavigateToScene(0,eUIScene_Timer); + } + + if(m_cancelJoinFromSearchResult) + { + SetState(DNM_INT_STATE_IDLE); + m_cancelJoinFromSearchResult= false; + + return false; + } + + m_cancelJoinFromSearchResult = false; + + + // Now we join the party with each of joining players, and then wait to be informed of our reserved slots + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + WXS::User^ user = ProfileManager.GetUser(i); + + auto joinPartyAsync = WXM::Party::JoinPartyByIdAsync(user, ref new Platform::String(searchResult->m_partyId.c_str())); + auto ccTask = create_task(joinPartyAsync).then([this](task t) + { + try + { + t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"JoinPartyByIdAsync failed", ex->HResult ); + SetState(DNM_INT_STATE_JOINING_FAILED); + } + }); + + + while(!ccTask.is_done()) + { + // Check for being disconnected from the network + if(!ProfileManager.IsSignedInLive(i)) + { + joinPartyAsync->Cancel(); + break; + } + + // Tick some simple things + ProfileManager.Tick(); + StorageManager.Tick(); + InputManager.Tick(); + RenderManager.Tick(); + ui.tick(); + ui.render(); + RenderManager.Present(); + } + // Check for being disconnected from the network + if(!ProfileManager.IsSignedInLive(i)) + { + break; + } + } + } + + return ( m_state == DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS ); +} + +void DQRNetworkManager::CancelJoinPartyFromSearchResult() +{ + m_cancelJoinFromSearchResult = true; +} + +// This method attempts to find the local player within a party that would be most appropriate to act as our primary player when joining the game. +bool DQRNetworkManager::GetBestPartyUserIndex() +{ + bool playerFound = false; + + // Use context from any user at all for this + WXS::User ^anyUser = nullptr; + if( WXS::User::Users->Size > 0 ) + { + anyUser = WXS::User::Users->GetAt(0); + } + if( anyUser == nullptr ) return 0; + MXS::XboxLiveContext^ xboxLiveContext = ref new MXS::XboxLiveContext(anyUser); + + m_partyController->RefreshPartyView(); + MXSM::MultiplayerSessionReference ^sessionRef = m_partyController->GetGamePartySessionReference(); + if( sessionRef != nullptr ) + { + auto asyncOp = xboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(asyncOp) + .then([this,&playerFound] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session = t.get(); + + int playerIdx = -1; + for( int i = 0; i < session->Members->Size; i++ ) // First pass through to see if we've a signed in user that is in the session we've been added to + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ ) + { + WXS::User ^user = ProfileManager.GetUser(j); + if( user != nullptr ) + { + if( user->XboxUserId == member->XboxUserId ) + { + DQRNetworkManager::LogCommentFormat(L"Found already signed in player %s to act as invite recipient",member->XboxUserId->Data()); + playerIdx = j; + playerFound = true; + break; + } + } + } + } + if( playerIdx == -1 ) // If nothing found, second pass through to attempt to find a controller that matches + { + for( int i = 0; i < session->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + for( int j = 4; j < 12; j++ ) + { + WXS::User ^user = InputManager.GetUserForGamepad(j); + if( user != nullptr ) + { + if( user->XboxUserId == member->XboxUserId ) + { + DQRNetworkManager::LogCommentFormat(L"Found controller %d for %s (session idx %d) to act as invite recipient (%s)",j,member->XboxUserId->Data(),i, user->XboxUserId->Data()); + playerIdx = ProfileManager.AddGamepadToGame(j); + if( playerIdx != -1 ) + { + playerFound = true; + DQRNetworkManager::LogCommentFormat(L"Assigned controller to user index %d",playerIdx); + break; + } + } + } + } + } + } + } + catch ( Platform::COMException^ ex ) + { + } + }).wait(); + } + return playerFound; +} + +// Request the GameDisplayName for this player asynchronously +void DQRNetworkManager::RequestDisplayName(DQRNetworkPlayer *player) +{ + if (player->IsLocal()) + { + // Player is local so we can just ask profile manager + SetDisplayName(player->GetUID(), ProfileManager.GetDisplayName(player->GetLocalPlayerIndex())); + } + else + { + // Player is remote so we need to do an async request + PlayerUID xuid = player->GetUID(); + ProfileManager.GetProfile(xuid, &GetProfileCallback, this); + } +} + +void DQRNetworkManager::GetProfileCallback(LPVOID pParam, Microsoft::Xbox::Services::Social::XboxUserProfile^ profile) +{ + DQRNetworkManager *dqnm = (DQRNetworkManager *)pParam; + dqnm->SetDisplayName(PlayerUID(profile->XboxUserId->Data()), profile->GameDisplayName->Data()); +} + +// Set player display name +void DQRNetworkManager::SetDisplayName(PlayerUID xuid, wstring displayName) +{ + EnterCriticalSection(&m_csRoomSyncData); + for (int i = 0; i < m_roomSyncData.playerCount; i++) + { + if( m_players[i] ) + { + if (m_players[i]->GetUID() == xuid) + { + // Set player display name + m_players[i]->SetDisplayName(displayName); + // Add display name to map + m_displayNames.insert(std::make_pair(m_players[i]->m_name, m_players[i]->m_displayName)); + } + } + } + LeaveCriticalSection(&m_csRoomSyncData); +} + +void DQRNetworkManager::CheckForcedSignOut() +{ + auto asyncOp = m_primaryUserXboxLiveContext->MultiplayerService->GetCurrentSessionAsync(m_multiplayerSession->SessionReference); + create_task(asyncOp) + .then([this] (task t) + { + try + { + t.get(); + } + catch (Platform::COMException^ ex) + { + if (ex->HResult == 0x8015DC16) + { + m_handleForcedSignOut = true; + } + } + }); +} + +void DQRNetworkManager::HandleForcedSignOut() +{ + // Bin the session (we can't use it anymore) + m_multiplayerSession = nullptr; + + // Bin the party + m_partyController->SetPartyView(nullptr); + + // Forced sign out destroyed the party so time to go + app.DebugPrintf("DQRNetworkManager::HandleForcedSignOut: Forced sign out destroyed the party, aborting game\n"); + + if (IsInSession() && !g_NetworkManager.IsLeavingGame()) + { + // Exit world + app.SetAction(0, eAppAction_ExitWorld); + } + else + { + app.DebugPrintf("DQRNetworkManager::HandleForcedSignOut: Already leaving the game, skipping abort\n"); + } +} diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager.h b/Minecraft.Client/Durango/Network/DQRNetworkManager.h new file mode 100644 index 0000000..5f7b8d9 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager.h @@ -0,0 +1,582 @@ +#pragma once + +#include "DQRNetworkPlayer.h" +#include "..\Minecraft.World\C4JThread.h" +#include + +class IDQRNetworkManagerListener; +class PartyController; + +class DQRNetworkManager; +class ChatIntegrationLayer; + +namespace MXS = Microsoft::Xbox::Services; +namespace MXSM = Microsoft::Xbox::Services::Multiplayer; +namespace MXSS = Microsoft::Xbox::Services::Social; +namespace WXM = Windows::Xbox::Multiplayer; +namespace WXN = Windows::Xbox::Networking; +namespace WXNRs = Windows::Xbox::Networking::RealtimeSession; +namespace WFC = Windows::Foundation::Collections; + +#define MATCH_SESSION_TEMPLATE_NAME L"PeerToHostTemplate" +#define MAX_PLAYERS_IN_TEMPLATE 8 + +using namespace std; + +ref class DQRNetworkManagerEventHandlers sealed +{ +internal: + DQRNetworkManagerEventHandlers(DQRNetworkManager *pDQRNet); +public: + void Setup(WXNRs::Session^ session); + void Pulldown(WXNRs::Session^ session); + + void DataReceivedHandler(Platform::Object^ session, WXNRs::DataReceivedEventArgs^ args); + void SessionAddressDataChangedHandler(Platform::Object^ session, WXNRs::SessionAddressDataChangedEventArgs^ args); + void SessionStatusUpdateHandler(Platform::Object^ session, WXNRs::SessionStatusUpdateEventArgs^ args); + void AddedSessionAddressHandler(Platform::Object^ session, WXNRs::AddedSessionAddressEventArgs^ args); + void RemovedSessionAddressHandler(Platform::Object^ session, WXNRs::RemovedSessionAddressEventArgs^ args); + void GlobalSessionDataChangedHandler(Platform::Object^ session, WXNRs::GlobalSessionDataChangedEventArgs^ args); + +private: + Windows::Foundation::EventRegistrationToken m_dataReceivedToken; + Windows::Foundation::EventRegistrationToken m_sessionStatusToken; + Windows::Foundation::EventRegistrationToken m_sessionAddressToken; + Windows::Foundation::EventRegistrationToken m_addedSessionToken; + Windows::Foundation::EventRegistrationToken m_removedSessionToken; + Windows::Foundation::EventRegistrationToken m_globalDataToken; + + DQRNetworkManager *m_pDQRNet; +}; + +typedef enum +{ + DQR_INTERNAL_ASSIGN_SMALL_IDS, + DQR_INTERNAL_UNASSIGN_SMALL_ID, + DQR_INTERNAL_PLAYER_TABLE, + DQR_INTERNAL_ADD_PLAYER_FAILED, +}; + +class DQRConnectionInfo +{ +public: + typedef enum + { + ConnectionState_HeaderByte0, + ConnectionState_HeaderByte1, + ConnectionState_ReadBytes + } eDQRConnectionState; + + typedef enum + { + ConnectionState_InternalHeaderByte, + ConnectionState_InternalAssignSmallIdMask, + ConnectionState_InternalAssignSmallId0, + ConnectionState_InternalAssignSmallId1, + ConnectionState_InternalAssignSmallId2, + ConnectionState_InternalAssignSmallId3, + ConnectionState_InternalRoomSyncData, + ConnectionState_InternalAddPlayerFailedData, + } eDQRInternalDataState; + + DQRConnectionInfo(); + void Reset(); + + eDQRConnectionState m_state; + eDQRInternalDataState m_internalDataState; + + int m_currentChannel; + bool m_internalFlag; + int m_bytesRemaining; + int m_roomSyncDataBytesRead; + int m_roomSyncDataBytesToRead; + unsigned char * m_pucRoomSyncData; + int m_addFailedPlayerDataBytesRead; + int m_addFailedPlayerDataBytesToRead; + unsigned char * m_pucAddFailedPlayerData; + unsigned char m_smallId[4]; + bool m_channelActive[4]; + unsigned char m_smallIdReadMask; + unsigned char *m_pucsmallIdReadMaskResolved; + bool m_initialPacketReceived; +}; + + +class DQRNetworkManager +{ + friend class PartyController; + friend ref class DQRNetworkManagerEventHandlers; + friend class DQRNetworkPlayer; + friend class ChatIntegrationLayer; +public: + static const int JOIN_PACKET_RESEND_DELAY_MS = 200; + static const int JOIN_PACKET_RESEND_TIMEOUT_MS = 20000; + static const int JOIN_RESERVATION_WAIT_TIME = 30000; + static const int JOIN_CREATE_SESSION_MAX_ATTEMPTS = 5; + + static const int PRIMARY_PLAYER_LEAVING_PARTY_WAIT_MS = 20000; // Time between primary player leaving and when we should respond to it, to allow time for us to receive a new party for them to be going to if they are just changing party rather than leaving altogether + + class SessionInfo + { + public: + SessionInfo(wstring& sessionName, wstring& serviceConfig, wstring& sessionTemplate); + SessionInfo(); + bool m_detailsValid; + wstring m_sessionName; + wstring m_serviceConfig; + wstring m_sessionTemplate; + }; + + static const int MAX_LOCAL_PLAYER_COUNT = 4; + static const int MAX_ONLINE_PLAYER_COUNT = 8; + static const int MAX_ONLINE_PLAYER_NAME_LENGTH = 21; + + // This class stores everything about a player that must be synchronised between machines. + class PlayerSyncData + { + public: + wchar_t *m_XUID; // XUID / XboxUserIds are passed round as decimal strings on Xbox 1 + uint32_t m_sessionAddress; // XRNS session address for this player, ie can identify which machine this player is on + uint8_t m_smallId; // Assigned by DQRNetworkManager, to attach a permanent id to this player (until we have to wrap round), to match a similar concept in qnet + uint8_t m_channel; // Local index / communication channel within session address, of player on this machine + wchar_t m_name[MAX_ONLINE_PLAYER_NAME_LENGTH]; + }; + + class RoomSyncData + { + public: + int playerCount; + PlayerSyncData players[MAX_ONLINE_PLAYER_COUNT]; + }; + + class HostGamertagResolveDetails + { + public:; + DQRNetworkPlayer* m_pPlayer; + wstring m_name; + unsigned int m_sessionAddress; + int m_channel; + bool m_sync; + }; + + DQRNetworkManager(IDQRNetworkManagerListener *listener); + void Initialise(); + void EnableDebugXBLContext(MXS::XboxLiveContext^ XBLContext); + + void CreateAndJoinSession(int userMask, unsigned char *customSessionData, unsigned int customSessionDataSize, bool offline); + void JoinSession(int playerMask); + void JoinSessionFromInviteInfo(int playerMask); + bool AddUsersToSession(int playerMask, MXSM::MultiplayerSessionReference^ sessionRef ); + void UpdateCustomSessionData(); + + bool AddLocalPlayerByUserIndex(int userIndex); + bool RemoveLocalPlayerByUserIndex(int userIndex); + + bool IsHost(); + bool IsInSession(); + + // Player retrieval + int GetPlayerCount(); + int GetOnlinePlayerCount(); + DQRNetworkPlayer *GetPlayerByIndex(int idx); + DQRNetworkPlayer *GetPlayerBySmallId(int idx); + DQRNetworkPlayer *GetPlayerByXuid(PlayerUID xuid); + wstring GetDisplayNameByGamertag(wstring gamertag); + DQRNetworkPlayer *GetLocalPlayerByUserIndex(int idx); + DQRNetworkPlayer *GetHostPlayer(); + int GetSessionIndex(DQRNetworkPlayer *player); + void Tick(); + void Tick_XRNS(); + void Tick_VoiceChat(); + void Tick_Party(); + void Tick_CustomSessionData(); + void Tick_AddAndRemoveLocalPlayers(); + void Tick_ResolveGamertags(); + void Tick_PartyProcess(); + void Tick_StateMachine(); + void Tick_CheckInviteParty(); + + bool ShouldMessageForFullSession(); + void FlagInvitedToFullSession(); + void UnflagInvitedToFullSession(); + // Externally exposed state. All internal states are mapped to one of these broader states. + typedef enum + { + DNM_STATE_INITIALISING, + DNM_STATE_INITIALISE_FAILED, + DNM_STATE_IDLE, + + DNM_STATE_HOSTING, + DNM_STATE_JOINING, + + DNM_STATE_STARTING, + DNM_STATE_PLAYING, + + DNM_STATE_LEAVING, + DNM_STATE_ENDING, + } eDQRNetworkManagerState; + + eDQRNetworkManagerState GetState(); + + class SessionSearchResult + { + public: + SessionSearchResult() { m_extData = NULL; } + ~SessionSearchResult() { free(m_extData); } + wstring m_partyId; + wstring m_sessionName; + + // These names/xuids reflect the server controlled list of who is actually in the game + wstring m_playerNames[MAX_ONLINE_PLAYER_COUNT]; + PlayerUID m_playerXuids[MAX_ONLINE_PLAYER_COUNT]; + int m_playerCount; + + // This count & set of xuids reflects the session document list of who is in the game + wstring m_sessionXuids[MAX_ONLINE_PLAYER_COUNT]; + int m_usedSlotCount; + + void *m_extData; + }; + +protected: + // NOTE: If anything changes in here, then the mapping from internal -> external state needs to be updated (m_INTtoEXTStateMappings, defined in the cpp file) + typedef enum + { + DNM_INT_STATE_INITIALISING, + DNM_INT_STATE_INITIALISE_FAILED, + DNM_INT_STATE_IDLE, + DNM_INT_STATE_HOSTING, + DNM_INT_STATE_HOSTING_WAITING_TO_PLAY, + DNM_INT_STATE_HOSTING_FAILED, + DNM_INT_STATE_JOINING, + DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS, + DNM_INT_STATE_JOINING_GET_SDA, + DNM_INT_STATE_JOINING_WAITING_FOR_SDA, + DNM_INT_STATE_JOINING_CREATE_SESSION, + DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION, + DNM_INT_STATE_JOINING_SENDING_UNRELIABLE, + DNM_INT_STATE_JOINING_FAILED_TIDY_UP, + DNM_INT_STATE_JOINING_FAILED, + DNM_INT_STATE_STARTING, + DNM_INT_STATE_PLAYING, + DNM_INT_STATE_LEAVING, + DNM_INT_STATE_LEAVING_FAILED, + DNM_INT_STATE_ENDING, + DNM_INT_STATE_COUNT + } eDQRNetworkManagerInternalState; + + typedef enum + { + DNM_ADD_PLAYER_STATE_IDLE, + DNM_ADD_PLAYER_STATE_PROCESSING, + DNM_ADD_PLAYER_STATE_COMPLETE_SUCCESS, + DNM_ADD_PLAYER_STATE_COMPLETE_FAIL, + DNM_ADD_PLAYER_STATE_COMPLETE_FAIL_FULL, + } eDQRAddLocalPlayerState; + + typedef enum + { + DNM_REMOVE_PLAYER_STATE_IDLE, + DNM_REMOVE_PLAYER_STATE_PROCESSING, + DNM_REMOVE_PLAYER_STATE_COMPLETE_SUCCESS, + DNM_REMOVE_PLAYER_STATE_COMPLETE_FAIL, + } eDQRRemoveLocalPlayerState; + + class StateChangeInfo + { + public: + eDQRNetworkManagerState m_oldState; + eDQRNetworkManagerState m_newState; + StateChangeInfo(eDQRNetworkManagerState oldState, eDQRNetworkManagerState newState) : m_oldState(oldState), m_newState(newState) {} + }; + + typedef enum + { + // Incoming messages + RTS_MESSAGE_DATA_RECEIVED, + RTS_MESSAGE_DATA_RECEIVED_CHAT, + RTS_MESSAGE_ADDED_SESSION_ADDRESS, + RTS_MESSAGE_REMOVED_SESSION_ADDRESS, + RTS_MESSAGE_STATUS_ACTIVE, + RTS_MESSAGE_STATUS_TERMINATED, + + // Outgoing messages + RTS_MESSAGE_START_CLIENT, + RTS_MESSAGE_START_HOST, + RTS_MESSAGE_TERMINATE, + RTS_MESSAGE_SEND_DATA, + } eRTSMessageType; + + typedef enum + { + RTS_MESSAGE_FLAG_BROADCAST_MODE = 1, + RTS_MESSAGE_FLAG_RELIABLE = 2, + RTS_MESSAGE_FLAG_SEQUENTIAL = 4, + RTS_MESSAGE_FLAG_COALESCE = 8, + RTS_MESSAGE_FLAG_GAME_CHANNEL = 16, + } eRTSFlags; + + class RTS_Message + { + public: + eRTSMessageType m_eType; + unsigned int m_sessionAddress; + unsigned char *m_pucData; + unsigned int m_dataSize; + unsigned int m_flags; + }; + + std::queue m_stateChangeQueue; + CRITICAL_SECTION m_csStateChangeQueue; + CRITICAL_SECTION m_csSendBytes; + CRITICAL_SECTION m_csPartyViewVector; + std::queue m_RTSMessageQueueIncoming; + CRITICAL_SECTION m_csRTSMessageQueueIncoming; + std::queue m_RTSMessageQueueOutgoing; + CRITICAL_SECTION m_csRTSMessageQueueOutgoing; +private: + void SetState(DQRNetworkManager::eDQRNetworkManagerInternalState state); + static const eDQRNetworkManagerState m_INTtoEXTStateMappings[DNM_INT_STATE_COUNT]; + eDQRNetworkManagerInternalState m_state; + eDQRNetworkManagerState m_stateExternal; + __int64 m_lastUnreliableSendTime; + __int64 m_firstUnreliableSendTime; + __int64 m_startedWaitingForReservationsTime; + unsigned char *m_customSessionData; + unsigned int m_customSessionDataSize; + int m_customDataDirtyUpdateTicks; + std::shared_ptr m_chat; + + eDQRAddLocalPlayerState m_addLocalPlayerState; + DQRNetworkPlayer *m_addLocalPlayerSuccessPlayer; + int m_addLocalPlayerSuccessIndex; + int m_addLocalPlayerFailedIndex; + + eDQRRemoveLocalPlayerState m_removeLocalPlayerState; + int m_removeLocalPlayerIndex; + + Windows::Xbox::System::User^ m_primaryUser; + MXS::XboxLiveContext^ m_primaryUserXboxLiveContext; + WXN::SecureDeviceAssociationTemplate^ m_associationTemplate; + + CRITICAL_SECTION m_csRoomSyncData; + RoomSyncData m_roomSyncData; + DQRNetworkPlayer *m_players[MAX_ONLINE_PLAYER_COUNT]; + + IDQRNetworkManagerListener *m_listener; + PartyController *m_partyController; + DQRNetworkManagerEventHandlers^ m_eventHandlers; + WXNRs::Session^ m_XRNS_Session; + unsigned int m_XRNS_LocalAddress; + unsigned int m_XRNS_OldestAddress; + MXSM::MultiplayerSession^ m_multiplayerSession; + WXN::SecureDeviceAssociation^ m_sda; + Platform::String^ m_secureDeviceAddressBase64; + unsigned char m_currentSmallId; + unsigned char m_hostSmallId; + bool m_isHosting; + bool m_isInSession; + bool m_isOfflineGame; +public: + int m_currentUserMask; +private: + int m_joinSessionUserMask; + Platform::Array^ m_joinSessionXUIDs; + int m_joinSmallIdMask; + + Platform::Array^ m_remoteSocketAddress; + Platform::Array^ m_localSocketAddress; + int m_joinCreateSessionAttempts; + + C4JThread *m_CreateSessionThread; + C4JThread *m_LeaveRoomThread; + C4JThread *m_TidyUpJoinThread; + C4JThread *m_UpdateCustomSessionDataThread; + C4JThread *m_RTS_DoWorkThread; + C4JThread *m_CheckPartyInviteThread; + + bool m_notifyForFullParty; + + unordered_map m_sessionAddressToConnectionInfoMapHost; // For host - there may be more than one remote session attached to this listening session + unsigned int m_sessionAddressFromSmallId[256]; // For host - for mapping back from small Id, to session address + unsigned char m_channelFromSmallId[256]; // For host and client, for mapping back from small Id, to channel + DQRConnectionInfo m_connectionInfoClient; // For client + unsigned int m_hostSessionAddress; // For client + + CRITICAL_SECTION m_csHostGamertagResolveResults; + queue m_hostGamertagResolveResults; + + void AddPlayerFailed(Platform::String ^xuid); + Platform::String^ RemoveBracesFromGuidString(__in Platform::String^ guid ); + void HandleSessionChange( MXSM::MultiplayerSession^ session ); + MXSM::MultiplayerSession^ WriteSessionHelper( MXS::XboxLiveContext^ xboxLiveContext, + MXSM::MultiplayerSession^ multiplayerSession, + MXSM::MultiplayerSessionWriteMode writeMode, + HRESULT& hr); + MXSM::MultiplayerSessionMember^ GetUserMemberInSession( Windows::Xbox::System::User^ user, MXSM::MultiplayerSession^ session); + bool IsPlayerInSession( Platform::String^ xboxUserId, MXSM::MultiplayerSession^ session, int *smallId ); + + WXM::MultiplayerSessionReference^ ConvertToWindowsXboxMultiplayerSessionReference( MXSM::MultiplayerSessionReference^ sessionRef); + MXSM::MultiplayerSessionReference^ ConvertToMicrosoftXboxServicesMultiplayerSessionReference( WXM::MultiplayerSessionReference^ sessionRef ); + + void BytesReceived(int smallId, BYTE *bytes, int byteCount); + void BytesReceivedInternal(DQRConnectionInfo *connectionInfo, unsigned int sessionAddress, BYTE *bytes, int byteCount); + void SendBytes(int smallId, BYTE *bytes, int byteCount); + int GetQueueSizeBytes(); + int GetQueueSizeMessages(); + void SendBytesRaw(int smallId, BYTE *bytes, int byteCount, bool reliableAndSequential); + void SendBytesChat(unsigned int address, BYTE *bytes, int byteCount, bool reliable, bool sequential, bool broadcast); + + bool AddRoomSyncPlayer(DQRNetworkPlayer *pPlayer, unsigned int sessionAddress, int channel); + void RemoveRoomSyncPlayersWithSessionAddress(unsigned int sessionAddress); + void RemoveRoomSyncPlayer(DQRNetworkPlayer *pPlayer); + void UpdateRoomSyncPlayers(RoomSyncData *pNewSyncData); + void SendRoomSyncInfo(); + void SendAddPlayerFailed(Platform::String^ xuid); + void SendSmallId(bool reliableAndSequential, int playerMask); + void SendUnassignSmallId(int playerIndex); + int GetSessionIndexForSmallId(unsigned char smallId); + int GetSessionIndexAndSmallIdForHost(unsigned char *smallId); + + static void LogComment( Platform::String^ strText ); + static void LogCommentFormat( LPCWSTR strMsg, ... ); + static void LogCommentWithError( Platform::String^ strTest, HRESULT hr ); + + static Platform::String^ GetErrorString( HRESULT hr ); + static Platform::String^ FormatString( LPCWSTR strMsg, ... ); + static Platform::String^ ConvertHResultToErrorName( HRESULT hr ); + + Platform::String^ GetNextSmallIdAsJsonString(); + static int _HostGameThreadProc( void* lpParameter ); + int HostGameThreadProc(); + static int _LeaveRoomThreadProc( void* lpParameter ); + int LeaveRoomThreadProc(); + static int _TidyUpJoinThreadProc( void* lpParameter ); + int TidyUpJoinThreadProc(); + static int _UpdateCustomSessionDataThreadProc( void* lpParameter ); + int UpdateCustomSessionDataThreadProc(); + static int _CheckInviteThreadProc(void *lpParameter); + int CheckInviteThreadProc(); + static int _RTSDoWorkThread(void* lpParameter); + int RTSDoWorkThread(); + + CRITICAL_SECTION m_csVecChatPlayers; + vector m_vecChatPlayersJoined; +public: + void HandleNewPartyFoundForPlayer(); + void HandlePlayerRemovedFromParty(int playerMask); + + void ChatPlayerJoined(int idx); + bool IsReadyToPlayOrIdle(); + void StartGame(); + void LeaveRoom(); + void TidyUpFailedJoin(); + + static void SetInviteReceivedFlag(); + static void SetPartyProcessJoinParty(); + static void SetPartyProcessJoinSession(int bootUserIndex, Platform::String^ bootSessionName, Platform::String^ bootServiceConfig, Platform::String^ bootSessionTemplate); + + void SendInviteGUI(int quadrant); + bool IsAddingPlayer(); + + bool FriendPartyManagerIsBusy(); + int FriendPartyManagerGetCount(); + bool FriendPartyManagerSearch(); + void FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult); + WFC::IVectorView^ FilterPartiesByPermission(MXS::XboxLiveContext ^context, WFC::IVectorView^ partyResults); + + bool JoinPartyFromSearchResult(SessionSearchResult *searchResult, int playerMask); + void CancelJoinPartyFromSearchResult(); + void RequestDisplayName(DQRNetworkPlayer *player); + void SetDisplayName(PlayerUID xuid, wstring displayName); + +private: + __int64 m_playersLeftPartyTime; + int m_playersLeftParty; + + bool GetBestPartyUserIndex(); + C4JThread *m_GetFriendPartyThread; + static int _GetFriendsThreadProc( void* lpParameter ); + int GetFriendsThreadProc(); + bool IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session); + bool GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData); + +public: + static Platform::Collections::Vector^ GetFriends(); + +private: + SessionSearchResult *m_sessionSearchResults; + int m_sessionResultCount; + bool m_cancelJoinFromSearchResult; + + map m_displayNames; // Player display names by gamertag + + + + typedef enum + { + DNM_PARTY_PROCESS_NONE, + DNM_PARTY_PROCESS_JOIN_PARTY, + DNM_PARTY_PROCESS_JOIN_SPECIFIED + } ePartyProcessType; + static int m_bootUserIndex; + static ePartyProcessType m_partyProcess; + static wstring m_bootSessionName; + static wstring m_bootServiceConfig; + static wstring m_bootSessionTemplate; + static bool m_inviteReceived; + + static DQRNetworkManager *s_pDQRManager; + + static void GetProfileCallback(LPVOID pParam, Microsoft::Xbox::Services::Social::XboxUserProfile^ profile); + + // Forced signout + bool m_handleForcedSignOut; + + void CheckForcedSignOut(); + void HandleForcedSignOut(); + + unsigned int m_RTS_Stat_totalBytes; + unsigned int m_RTS_Stat_totalSends; + + void UpdateRTSStats(); + + // Incoming messages - to be called from the main thread, to get incoming messages from the RTS work thread + void ProcessRTSMessagesIncoming(); + void Process_RTS_MESSAGE_DATA_RECEIVED(RTS_Message &message); + void Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(RTS_Message &message); + void Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(RTS_Message &message); + void Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(RTS_Message &message); + void Process_RTS_MESSAGE_STATUS_ACTIVE(RTS_Message &message); + void Process_RTS_MESSAGE_STATUS_TERMINATED(RTS_Message &message); + + // Outgoing messages - to be called from the RTS work thread, to process requests from the main thread + + void ProcessRTSMessagesOutgoing(); + void Process_RTS_MESSAGE_START_CLIENT(RTS_Message &message); + void Process_RTS_MESSAGE_START_HOST(RTS_Message &message); + void Process_RTS_MESSAGE_TERMINATE(RTS_Message &message); + void Process_RTS_MESSAGE_SEND_DATA(RTS_Message &message); + + // These methods are called from the main thread, to put an outgoing message onto the queue to be processed by these previous methods + void RTS_StartCient(); + void RTS_StartHost(); + void RTS_Terminate(); + void RTS_SendData(unsigned char *pucData, unsigned int dataSize, unsigned int sessionAddress, bool reliable, bool sequential, bool coalesce, bool includeMode, bool gameChannel ); + +}; + +// Class defining interface to be implemented for class that handles callbacks +class IDQRNetworkManagerListener +{ +public: + virtual void HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize) = 0; + virtual void HandlePlayerJoined(DQRNetworkPlayer *player) = 0; + virtual void HandlePlayerLeaving(DQRNetworkPlayer *player) = 0; + virtual void HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState) = 0; +// virtual void HandleResyncPlayerRequest(DQRNetworkPlayer **aPlayers) = 0; + virtual void HandleAddLocalPlayerFailed(int idx, bool serverFull) = 0; + virtual void HandleDisconnect(bool bLostRoomOnly) = 0; + virtual void HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo) = 0; + virtual bool IsSessionJoinable() = 0; +}; diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager_FriendSessions.cpp b/Minecraft.Client/Durango/Network/DQRNetworkManager_FriendSessions.cpp new file mode 100644 index 0000000..3a14a2d --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager_FriendSessions.cpp @@ -0,0 +1,591 @@ +#include "stdafx.h" + +#include "DQRNetworkManager.h" +#include "PartyController.h" +#include +#include +#include +#include "..\Minecraft.World\StringHelpers.h" +#include "base64.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +#include "ChatIntegrationLayer.h" + +using namespace Concurrency; +using namespace Windows::Foundation::Collections; + +// Returns true if we are already processing a request to find game parties of friends +bool DQRNetworkManager::FriendPartyManagerIsBusy() +{ + if( m_GetFriendPartyThread ) + { + if( m_GetFriendPartyThread->isRunning() ) + { + return true; + } + } + return false; +} + +// Returns the total count of game parties that we found for our friends +int DQRNetworkManager::FriendPartyManagerGetCount() +{ + return m_sessionResultCount; +} + +// Initiate the (asynchronous) search for game parties of our friends +bool DQRNetworkManager::FriendPartyManagerSearch() +{ + if( m_GetFriendPartyThread ) + { + if( m_GetFriendPartyThread->isRunning() ) + { + return false; + } + } + + m_sessionResultCount = 0; + delete [] m_sessionSearchResults; + m_sessionSearchResults = NULL; + + m_GetFriendPartyThread = new C4JThread(&_GetFriendsThreadProc,this,"GetFriendsThreadProc"); + m_GetFriendPartyThread->Run(); + + return true; +} + +// Get a particular search result for a game party that we have discovered. Index should be from 0 to the value returned by FriendPartyManagerGetCount. +void DQRNetworkManager::FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult) +{ + assert( idx < m_sessionResultCount ); + assert( ( m_GetFriendPartyThread == NULL ) || ( !m_GetFriendPartyThread->isRunning()) ); + + // Need to make sure that copied data has independently allocated m_extData, so both copies can be freed + *searchResult = m_sessionSearchResults[idx]; + searchResult->m_extData = malloc(sizeof(GameSessionData)); + memcpy(searchResult->m_extData, m_sessionSearchResults[idx].m_extData, sizeof(GameSessionData)); +} + +int DQRNetworkManager::_GetFriendsThreadProc(void* lpParameter) +{ + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->GetFriendsThreadProc(); +} + +// This is the main thread that is kicked off to find game sessions associated with our friends. We have to do this +// by finding parties associated with our friends, and from the parties get the assocated game session. +int DQRNetworkManager::GetFriendsThreadProc() +{ + LogComment(L"Starting GetFriendsThreadProc"); + WXS::User^ primaryUser = ProfileManager.GetUser(0); + + if( primaryUser == nullptr ) + { + return -1; + } + MXS::XboxLiveContext^ primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(primaryUser); + if( primaryUserXboxLiveContext == nullptr ) + { + return -1; + } + + MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr; + + // First get our friends list (people we follow who may or may not follow us back), note we're requesting all friends + auto getSocialRelationshipsAsync = primaryUserXboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); + create_task(getSocialRelationshipsAsync).then([this,&socialRelationshipResult](task t) + { + try + { + socialRelationshipResult = t.get(); + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"GetSocialRelationshipsAsync failed", ex->HResult ); + } + }) + .wait(); + if( socialRelationshipResult == nullptr ) + { + return -1; + } + + IVector^ friendXUIDs = ref new Platform::Collections::Vector; + + // Now construct a vector of these users, that follow us back - these are our "friends" + for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) + { + MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); + if(relationship->IsFollowingCaller) + { + friendXUIDs->Append(relationship->XboxUserId); + } + } + + // If we don't have any such friends, we're done + if( friendXUIDs->Size == 0 ) + { + return 0; + } + + // Now get party associations for these friends + auto getPartyAssociationsAsync = WXM::Party::GetUserPartyAssociationsAsync(primaryUser, friendXUIDs->GetView() ); + + IVectorView^ partyResults = nullptr; + + create_task(getPartyAssociationsAsync).then([this,&partyResults](task^> t) + { + try + { + partyResults = t.get(); + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"getPartyAssociationsAsync failed", ex->HResult ); + } + }) + .wait(); + + if( partyResults == nullptr ) + { + return -1; + } + + if( partyResults->Size == 0 ) + { + return 0; + } + + // Filter these parties by whether we have permission to see them online + partyResults = FilterPartiesByPermission(primaryUserXboxLiveContext, partyResults); + + + // At this point, we have Party Ids for our friends. Now we need to get Party Views for each of these Ids. + + LogComment("Parties found"); + + // Get party views for each of the user party associations that we have. These seem to be able to (individually) raise errors, so + // accumulate results into 2 matched vectors declared below so that we can ignore any broken UserPartyAssociations from now + vector partyViewVector; + vector partyResultsVector; + + vector> taskVector; + for each(WXM::UserPartyAssociation^ remoteParty in partyResults) + { + auto asyncOp = WXM::Party::GetPartyViewByPartyIdAsync( primaryUser, remoteParty->PartyId ); + task asyncTask = create_task(asyncOp); + + taskVector.push_back(asyncTask.then([this, &partyViewVector, &partyResultsVector, remoteParty] (task t) + { + try + { + WXM::PartyView^ partyView = t.get(); + + if( partyView != nullptr ) + { + app.DebugPrintf("Got party view\n"); + EnterCriticalSection(&m_csPartyViewVector); + partyViewVector.push_back(partyView); + partyResultsVector.push_back(remoteParty); + LeaveCriticalSection(&m_csPartyViewVector); + } + } + catch ( Platform::COMException^ ex ) + { + app.DebugPrintf("Getting party view error 0x%x\n",ex->HResult); + } + })); + } + for( auto it = taskVector.begin(); it != taskVector.end(); it++ ) + { + it->wait(); + } + + if( partyViewVector.size() == 0 ) + { + return 0; + } + + // Filter the party view, and party results vector (partyResultsVector) this is matched to, to remove any that don't have game sessions - or game sessions that aren't this game + vector partyViewVectorFiltered; + vector partyResultsFiltered; + + for( int i = 0; i < partyViewVector.size(); i++ ) + { + WXM::PartyView^ partyView = partyViewVector[i]; + + if( partyView->Joinability == WXM::SessionJoinability::JoinableByFriends ) + { + if( partyView->GameSession ) + { + if( partyView->GameSession->ServiceConfigurationId == SERVICE_CONFIG_ID ) + { + partyViewVectorFiltered.push_back( partyView ); + partyResultsFiltered.push_back( partyResultsVector[i] ); + } + } + } + } + + // We now have matched vectors: + // + // partyResultsFiltered + // partyViewVectorFiltered + // + // and, from the party views, we can now attempt to get game sessions + + vector sessionVector; + vector partyViewVectorValid; + vector partyResultsValid; + + for( int i = 0; i < partyViewVectorFiltered.size(); i++ ) + { + WXM::PartyView^ partyView = partyViewVectorFiltered[i]; + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession); + + LogComment(L"Party view vector " + sessionRef->SessionName + L" " + partyResultsFiltered[i]->QueriedXboxUserIds->GetAt(0)); + + MXSM::MultiplayerSession^ session = nullptr; + auto asyncOp = primaryUserXboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(asyncOp).then([&session] (task t) + { + try + { + session = t.get(); + } + catch (Platform::COMException^ ex) + { + } + }) + .wait(); + if( session ) + { + sessionVector.push_back(session); + partyViewVectorValid.push_back(partyView); + partyResultsValid.push_back(partyResultsFiltered[i]); + } + } + + if( sessionVector.size() == 0 ) + { + return 0; + } + + // We now have matched vectors: + // + // partyResultsValid + // partyViewVectorValid + // sessionVector + + // The next stage is to resolve the display names for the XUIDs of all the players in each of the sessions. It is possible that + // a session won't have any XUIDs to resolve, which would make GetUserProfilesAsync unhappy, so we'll only be creating a task + // when there are members. Creating new matching arrays for party results and sessions, to match the results (we don't care about the party view anymore) + + vector^>> nameResolveTaskVector; + vector^> nameResolveVector; + vector newSessionVector; + vector newPartyVector; + + for( int j = 0; j < sessionVector.size(); j++ ) + { + MXSM::MultiplayerSession^ session = sessionVector[j]; + IVector^ memberXUIDs = ref new Platform::Collections::Vector; + + Windows::Data::Json::JsonArray^ roomSyncArray = nullptr; + try + { + Windows::Data::Json::JsonObject^ customJson = Windows::Data::Json::JsonObject::Parse(session->SessionProperties->SessionCustomPropertiesJson); + Windows::Data::Json::JsonValue^ customValue = customJson->GetNamedValue(L"RoomSyncData"); + roomSyncArray = customValue->GetArray(); + LogComment("Attempting to parse RoomSyncData"); + for( int i = 0; i < roomSyncArray->Size; i++ ) + { + LogComment(roomSyncArray->GetAt(i)->GetString()); + } + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"Custom RoomSyncData Parse/GetNamedValue failed", ex->HResult ); + continue; + } + + if( roomSyncArray && ( roomSyncArray->Size > 0 ) ) + { + // For each session, we want to order these XUIDs so the display name of the first one is what we will name the session by. Prioritise doing this by: + // + // (1) If the host player (indicated by having a small id of 0) is our friend, use that + // (2) Otherwise use anyone who is our friend + + // Default to true + bool friendsOfFriends = true; + + int hostIndexFound = -1; + int friendIndexFound = -1; + + friendsOfFriends = IsSessionFriendsOfFriends(session); + + for( int i = 0; i < roomSyncArray->Size; i++ ) + { + Platform::String^ roomSyncXuid = roomSyncArray->GetAt(i)->GetString(); + + // Determine if this player is a friend + bool isFriend = false; + for each( Platform::String^ friendXUID in friendXUIDs ) + { + if( friendXUID == roomSyncXuid ) + { + isFriend = true; + break; + } + } + + bool isHost = i == 0; + + // Store that what we found at this index if it is a friend, or a friend who is a host + if( isFriend && ( friendsOfFriends || isHost ) ) + { + friendIndexFound = i; + if( isHost ) // Host is always in slot 0 + { + hostIndexFound = i; + } + } + } + + // Prefer to use index of host who is our friend + int bestIndex = friendIndexFound; + if( hostIndexFound != -1 ) + { + bestIndex = hostIndexFound; + } + + // Only consider if we have at least found one friend in the list of players + if( bestIndex != -1 ) + { + // Compile list of XUIDs to resolve with our specially chosen player as entry 0, then the rest + memberXUIDs->Append(roomSyncArray->GetAt(bestIndex)->GetString()); + for( int i = 0; i < roomSyncArray->Size; i++ ) + { + if( i != bestIndex ) + { + memberXUIDs->Append(roomSyncArray->GetAt(i)->GetString()); + } + } + nameResolveTaskVector.push_back( create_task( primaryUserXboxLiveContext->ProfileService->GetUserProfilesAsync( memberXUIDs->GetView() ) ) ); + newSessionVector.push_back(session); + newPartyVector.push_back(partyResultsValid[j]); + } + } + } + + try + { + auto joinTask = when_all(begin(nameResolveTaskVector), end(nameResolveTaskVector) ).then([this, &nameResolveVector](vector^> results) + { + nameResolveVector = results; + }) + .wait(); + } + catch(Platform::COMException^ ex) + { + return -1; + } + + // We now have matched vectors: + // + // newPartyVector - contains the party Ids that we'll need should we wish to join + // nameResolveVector - contains vectors views of the names of the members of the session each of these parties is in + // newSessionVector - contains the session information itself associated with each of the parties + + // Construct the final result vector + m_sessionResultCount = newSessionVector.size(); + m_sessionSearchResults = new SessionSearchResult[m_sessionResultCount]; + for( int i = 0; i < m_sessionResultCount; i++ ) + { + m_sessionSearchResults[i].m_partyId = newPartyVector[i]->PartyId->Data(); + m_sessionSearchResults[i].m_sessionName = newSessionVector[i]->SessionReference->SessionName->Data(); + for( int j = 0; j < nameResolveVector[i]->Size; j++ ) + { + m_sessionSearchResults[i].m_playerNames[j] = nameResolveVector[i]->GetAt(j)->GameDisplayName->Data(); + m_sessionSearchResults[i].m_playerXuids[j] = PlayerUID(nameResolveVector[i]->GetAt(j)->XboxUserId->Data()); + } + m_sessionSearchResults[i].m_playerCount = nameResolveVector[i]->Size; + m_sessionSearchResults[i].m_usedSlotCount = newSessionVector[i]->Members->Size; + if( m_sessionSearchResults[i].m_usedSlotCount > MAX_ONLINE_PLAYER_COUNT ) + { + // Don't think this could ever happen, but no harm in checking + m_sessionSearchResults[i].m_usedSlotCount = MAX_ONLINE_PLAYER_COUNT; + } + for( int j = 0; j < m_sessionSearchResults[i].m_usedSlotCount; j++ ) + { + m_sessionSearchResults[i].m_sessionXuids[j] = wstring( newSessionVector[i]->Members->GetAt(j)->XboxUserId->Data() ); + } + + m_sessionSearchResults[i].m_extData = malloc( sizeof(GameSessionData) ); + memset( m_sessionSearchResults[i].m_extData, 0, sizeof(GameSessionData) ); + + GetGameSessionData(newSessionVector[i], m_sessionSearchResults[i].m_extData); + } + + return 0; +} + +// Filters list of parties based on online presence permission (whether the friend is set to invisible or not) +IVectorView^ DQRNetworkManager::FilterPartiesByPermission(MXS::XboxLiveContext ^context, IVectorView^ partyResults) +{ + Platform::Collections::Vector^ filteredPartyResults = ref new Platform::Collections::Vector(); + + // List of permissions we want + auto permissionIds = ref new Platform::Collections::Vector(1, ref new Platform::String(L"ViewTargetPresence")); + + // List of target users + auto targetXboxUserIds = ref new Platform::Collections::Vector(); + for (int i = 0; i < partyResults->Size; i++) + { + assert(partyResults->GetAt(i)->QueriedXboxUserIds->Size > 0); + targetXboxUserIds->Append( partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) ); + } + + // Check + auto checkPermissionsAsync = context->PrivacyService->CheckMultiplePermissionsWithMultipleTargetUsersAsync(permissionIds->GetView(), targetXboxUserIds->GetView()); + create_task(checkPermissionsAsync).then([&partyResults, &filteredPartyResults](task^> t) + { + try + { + auto results = t.get(); + + // For each party, check to see if we have permission for the user + for (int i = 0; i < partyResults->Size; i++) + { + // For each permissions result + for (int j = 0; j < results->Size; j++) + { + auto result = results->GetAt(j); + + // If allowed to see this user AND it's the same user, add the party to the just + if ((result->Items->GetAt(0)->IsAllowed) && (partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) == result->XboxUserId)) + { + filteredPartyResults->Append(partyResults->GetAt(i)); + break; + } + } + } + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"CheckMultiplePermissionsWithMultipleTargetUsersAsync failed", ex->HResult ); + } + }) + .wait(); + + app.DebugPrintf("DQRNetworkManager::FilterPartiesByPermission: Removed %i parties because of online presence permissions\n", partyResults->Size - filteredPartyResults->Size); + + return filteredPartyResults->GetView(); +} + +// Get all friends (list of XUIDs) syncronously from the service (slow, may take 300ms+), returns empty list if something goes wrong +Platform::Collections::Vector^ DQRNetworkManager::GetFriends() +{ + auto friends = ref new Platform::Collections::Vector; + + auto primaryUser = ProfileManager.GetUser(0); + if (primaryUser == nullptr) + { + // Return empty + return friends; + } + + auto xboxLiveContext = ref new MXS::XboxLiveContext(primaryUser); + + // Request ALL friends because there's no other way to check friendships without using the REST API + auto getSocialRelationshipsAsync = xboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); + MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr; + + // First get our friends list (people we follow who may or may not follow us back) + Concurrency::create_task(getSocialRelationshipsAsync).then([&socialRelationshipResult](Concurrency::task t) + { + try + { + socialRelationshipResult = t.get(); + } + catch (Platform::COMException^ ex) + { + app.DebugPrintf("DQRNetworkManager::GetFriends: GetSocialRelationshipsAsync failed ()\n", ex->HResult); + } + }) + .wait(); + + if (socialRelationshipResult == nullptr) + { + // Return empty + return friends; + } + + app.DebugPrintf("DQRNetworkManager::GetFriends: Retrieved %i relationships\n", socialRelationshipResult->TotalCount); + + // Now construct a vector of these users, that follow us back - these are our "friends" + for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) + { + MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); + if(relationship->IsFollowingCaller) + { + app.DebugPrintf("DQRNetworkManager::GetFriends: Found friend \"%ls\"\n", relationship->XboxUserId->Data()); + friends->Append(relationship->XboxUserId); + } + } + + app.DebugPrintf("DQRNetworkManager::GetFriends: Found %i 2-way friendships\n", friends->Size); + + return friends; +} + +// If data for game settings exists returns FriendsOfFriends value, otherwise returns true +bool DQRNetworkManager::IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session) +{ + // Default to true, don't want to incorrectly prevent joining + bool friendsOfFriends = true; + + // We retrieve the game session data later too, shouldn't really duplicate this + void *gameSessionData = malloc( sizeof(GameSessionData)); + memset(gameSessionData, 0, sizeof(GameSessionData)); + + bool result = GetGameSessionData(session, gameSessionData); + + if (result) + { + friendsOfFriends = app.GetGameHostOption(((GameSessionData *)gameSessionData)->m_uiGameHostSettings, eGameHostOption_FriendsOfFriends); + } + + free(gameSessionData); + + return friendsOfFriends; +} + +// Parses custom json data from session and populates game session data param, return true if parse succeeded +bool DQRNetworkManager::GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData) +{ + Platform::String ^gameSessionDataJson = session->SessionProperties->SessionCustomPropertiesJson; + if( gameSessionDataJson ) + { + try + { + Windows::Data::Json::JsonObject^ customParam = Windows::Data::Json::JsonObject::Parse(gameSessionDataJson); + Windows::Data::Json::JsonValue^ customValue = customParam->GetNamedValue(L"GameSessionData"); + Platform::String ^customValueString = customValue->GetString(); + if( customValueString ) + { + base64_decode( customValueString, (unsigned char *)gameSessionData, sizeof(GameSessionData) ); + return true; + } + } + catch (Platform::COMException^ ex) + { + LogCommentWithError( L"Custom GameSessionData parameter Parse/GetNamedValue failed", ex->HResult ); + } + } + + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager_Log.cpp b/Minecraft.Client/Durango/Network/DQRNetworkManager_Log.cpp new file mode 100644 index 0000000..cf66ea6 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager_Log.cpp @@ -0,0 +1,303 @@ +#include "stdafx.h" + +#include "DQRNetworkManager.h" +#include "PartyController.h" +#include +#include +#include +#include "..\Minecraft.World\StringHelpers.h" +#include "base64.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +#include "ChatIntegrationLayer.h" + +using namespace Concurrency; +using namespace Windows::Foundation::Collections; + +void DQRNetworkManager::LogComment( Platform::String^ strText ) +{ +#ifndef _CONTENT_PACKAGE + static int64_t firstTime = 0; + wchar_t buf[64]; + + int64_t currentTime = System::currentTimeMillis(); + if( firstTime != 0 ) + { + _i64tow_s(currentTime - firstTime, buf, 64, 10); + OutputDebugString(buf); + OutputDebugString(L" ms: "); + } + else + { + firstTime = currentTime; + } + OutputDebugString(strText->Data()); + OutputDebugString(L"\n"); +#endif +} + +void DQRNetworkManager::LogCommentFormat( LPCWSTR strMsg, ... ) +{ + WCHAR strBuffer[2048]; + + va_list args; + va_start(args, strMsg); + _vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args ); + strBuffer[2047] = L'\0'; + + va_end(args); + + LogComment(ref new Platform::String(strBuffer)); +} + +void DQRNetworkManager::LogCommentWithError( Platform::String^ strTest, HRESULT hr ) +{ + Platform::String^ final = strTest + GetErrorString(hr); + LogComment(final); +} + +Platform::String^ DQRNetworkManager::GetErrorString( HRESULT hr ) +{ + Platform::String^ str = FormatString(L" %s [0x%0.8x]", ConvertHResultToErrorName(hr)->Data(), hr ); + return str; +} + +Platform::String^ DQRNetworkManager::FormatString( LPCWSTR strMsg, ... ) +{ + WCHAR strBuffer[2048]; + + va_list args; + va_start(args, strMsg); + _vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args ); + strBuffer[2047] = L'\0'; + + va_end(args); + + Platform::String^ str = ref new Platform::String(strBuffer); + return str; +} + +Platform::String^ DQRNetworkManager::ConvertHResultToErrorName( HRESULT hr ) +{ + switch( hr ) + { + // Generic errors + case S_OK: return L"S_OK"; + case S_FALSE: return L"S_FALSE"; + case E_OUTOFMEMORY: return L"E_OUTOFMEMORY"; + case E_ACCESSDENIED: return L"E_ACCESSDENIED"; + case E_INVALIDARG: return L"E_INVALIDARG"; + case E_UNEXPECTED: return L"E_UNEXPECTED"; + case E_ABORT: return L"E_ABORT"; + case E_FAIL: return L"E_FAIL"; + case E_NOTIMPL: return L"E_NOTIMPL"; + case E_ILLEGAL_METHOD_CALL: return L"E_ILLEGAL_METHOD_CALL"; + + // Authentication specific errors + case 0x87DD0003: return L"AM_E_XASD_UNEXPECTED"; + case 0x87DD0004: return L"AM_E_XASU_UNEXPECTED"; + case 0x87DD0005: return L"AM_E_XAST_UNEXPECTED"; + case 0x87DD0006: return L"AM_E_XSTS_UNEXPECTED"; + case 0x87DD0007: return L"AM_E_XDEVICE_UNEXPECTED"; + case 0x87DD0008: return L"AM_E_DEVMODE_NOT_AUTHORIZED"; + case 0x87DD0009: return L"AM_E_NOT_AUTHORIZED"; + case 0x87DD000A: return L"AM_E_FORBIDDEN"; + case 0x87DD000B: return L"AM_E_UNKNOWN_TARGET"; + case 0x87DD000C: return L"AM_E_INVALID_NSAL_DATA"; + case 0x87DD000D: return L"AM_E_TITLE_NOT_AUTHENTICATED"; + case 0x87DD000E: return L"AM_E_TITLE_NOT_AUTHORIZED"; + case 0x87DD000F: return L"AM_E_DEVICE_NOT_AUTHENTICATED"; + case 0x87DD0010: return L"AM_E_INVALID_USER_INDEX"; + case 0x87DD0011: return L"AM_E_USER_HASH_MISSING"; + case 0x87DD0012: return L"AM_E_ACTOR_NOT_SPECIFIED"; + case 0x87DD0013: return L"AM_E_USER_NOT_FOUND"; + case 0x87DD0014: return L"AM_E_INVALID_SUBTOKEN"; + case 0x87DD0015: return L"AM_E_INVALID_ENVIRONMENT"; + case 0x87DD0016: return L"AM_E_XASD_TIMEOUT"; + case 0x87DD0017: return L"AM_E_XASU_TIMEOUT"; + case 0x87DD0018: return L"AM_E_XAST_TIMEOUT"; + case 0x87DD0019: return L"AM_E_XSTS_TIMEOUT"; + case 0x8015DC00: return L"XO_E_DEVMODE_NOT_AUTHORIZED"; + case 0x8015DC01: return L"XO_E_SYSTEM_UPDATE_REQUIRED"; + case 0x8015DC02: return L"XO_E_CONTENT_UPDATE_REQUIRED"; + case 0x8015DC03: return L"XO_E_ENFORCEMENT_BAN"; + case 0x8015DC04: return L"XO_E_THIRD_PARTY_BAN"; + case 0x8015DC05: return L"XO_E_ACCOUNT_PARENTALLY_RESTRICTED"; + case 0x8015DC06: return L"XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED"; + case 0x8015DC08: return L"XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED"; + case 0x8015DC09: return L"XO_E_ACCOUNT_CREATION_REQUIRED"; + case 0x8015DC0A: return L"XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED"; + case 0x8015DC0B: return L"XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED"; + case 0x8015DC0C: return L"XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED"; + case 0x8015DC0D: return L"XO_E_ACCOUNT_CURFEW"; + case 0x8015DC0E: return L"XO_E_ACCOUNT_ZEST_MAINTENANCE_REQUIRED"; + case 0x8015DC0F: return L"XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED"; + case 0x8015DC10: return L"XO_E_ACCOUNT_MAINTENANCE_REQUIRED"; + case 0x8015DC11: return L"XO_E_ACCOUNT_TYPE_NOT_ALLOWED"; + case 0x8015DC12: return L"XO_E_CONTENT_ISOLATION"; + case 0x8015DC13: return L"XO_E_ACCOUNT_NAME_CHANGE_REQUIRED"; + case 0x8015DC14: return L"XO_E_DEVICE_CHALLENGE_REQUIRED"; + case 0x8015DC20: return L"XO_E_EXPIRED_DEVICE_TOKEN"; + case 0x8015DC21: return L"XO_E_EXPIRED_TITLE_TOKEN"; + case 0x8015DC22: return L"XO_E_EXPIRED_USER_TOKEN"; + case 0x8015DC23: return L"XO_E_INVALID_DEVICE_TOKEN"; + case 0x8015DC24: return L"XO_E_INVALID_TITLE_TOKEN"; + case 0x8015DC25: return L"XO_E_INVALID_USER_TOKEN"; + + // winsock errors + case MAKE_HRESULT(1,7,WSAEWOULDBLOCK) : return L"WSAEWOULDBLOCK"; + case MAKE_HRESULT(1,7,WSAEINPROGRESS) : return L"WSAEINPROGRESS"; + case MAKE_HRESULT(1,7,WSAEALREADY) : return L"WSAEALREADY"; + case MAKE_HRESULT(1,7,WSAENOTSOCK) : return L"WSAENOTSOCK"; + case MAKE_HRESULT(1,7,WSAEDESTADDRREQ) : return L"WSAEDESTADDRREQ"; + case MAKE_HRESULT(1,7,WSAEMSGSIZE) : return L"WSAEMSGSIZE"; + case MAKE_HRESULT(1,7,WSAEPROTOTYPE) : return L"WSAEPROTOTYPE"; + case MAKE_HRESULT(1,7,WSAENOPROTOOPT) : return L"WSAENOPROTOOPT"; + case MAKE_HRESULT(1,7,WSAEPROTONOSUPPORT) : return L"WSAEPROTONOSUPPORT"; + case MAKE_HRESULT(1,7,WSAESOCKTNOSUPPORT) : return L"WSAESOCKTNOSUPPORT"; + case MAKE_HRESULT(1,7,WSAEOPNOTSUPP) : return L"WSAEOPNOTSUPP"; + case MAKE_HRESULT(1,7,WSAEPFNOSUPPORT) : return L"WSAEPFNOSUPPORT"; + case MAKE_HRESULT(1,7,WSAEAFNOSUPPORT) : return L"WSAEAFNOSUPPORT"; + case MAKE_HRESULT(1,7,WSAEADDRINUSE) : return L"WSAEADDRINUSE"; + case MAKE_HRESULT(1,7,WSAEADDRNOTAVAIL) : return L"WSAEADDRNOTAVAIL"; + case MAKE_HRESULT(1,7,WSAENETDOWN) : return L"WSAENETDOWN"; + case MAKE_HRESULT(1,7,WSAENETUNREACH) : return L"WSAENETUNREACH"; + case MAKE_HRESULT(1,7,WSAENETRESET) : return L"WSAENETRESET"; + case MAKE_HRESULT(1,7,WSAECONNABORTED) : return L"WSAECONNABORTED"; + case MAKE_HRESULT(1,7,WSAECONNRESET) : return L"WSAECONNRESET"; + case MAKE_HRESULT(1,7,WSAENOBUFS) : return L"WSAENOBUFS"; + case MAKE_HRESULT(1,7,WSAEISCONN) : return L"WSAEISCONN"; + case MAKE_HRESULT(1,7,WSAENOTCONN) : return L"WSAENOTCONN"; + case MAKE_HRESULT(1,7,WSAESHUTDOWN) : return L"WSAESHUTDOWN"; + case MAKE_HRESULT(1,7,WSAETOOMANYREFS) : return L"WSAETOOMANYREFS"; + case MAKE_HRESULT(1,7,WSAETIMEDOUT) : return L"WSAETIMEDOUT"; + case MAKE_HRESULT(1,7,WSAECONNREFUSED) : return L"WSAECONNREFUSED"; + case MAKE_HRESULT(1,7,WSAELOOP) : return L"WSAELOOP"; + case MAKE_HRESULT(1,7,WSAENAMETOOLONG) : return L"WSAENAMETOOLONG"; + case MAKE_HRESULT(1,7,WSAEHOSTDOWN) : return L"WSAEHOSTDOWN"; + case MAKE_HRESULT(1,7,WSAEHOSTUNREACH) : return L"WSAEHOSTUNREACH"; + case MAKE_HRESULT(1,7,WSAENOTEMPTY) : return L"WSAENOTEMPTY"; + case MAKE_HRESULT(1,7,WSAEPROCLIM) : return L"WSAEPROCLIM"; + case MAKE_HRESULT(1,7,WSAEUSERS) : return L"WSAEUSERS"; + case MAKE_HRESULT(1,7,WSAEDQUOT) : return L"WSAEDQUOT"; + case MAKE_HRESULT(1,7,WSAESTALE) : return L"WSAESTALE"; + case MAKE_HRESULT(1,7,WSAEREMOTE) : return L"WSAEREMOTE"; + case MAKE_HRESULT(1,7,WSASYSNOTREADY) : return L"WSASYSNOTREADY"; + case MAKE_HRESULT(1,7,WSAVERNOTSUPPORTED) : return L"WSAVERNOTSUPPORTED"; + case MAKE_HRESULT(1,7,WSANOTINITIALISED) : return L"WSANOTINITIALISED"; + case MAKE_HRESULT(1,7,WSAEDISCON) : return L"WSAEDISCON"; + case MAKE_HRESULT(1,7,WSAENOMORE) : return L"WSAENOMORE"; + case MAKE_HRESULT(1,7,WSAECANCELLED) : return L"WSAECANCELLED"; + case MAKE_HRESULT(1,7,WSAEINVALIDPROCTABLE) : return L"WSAEINVALIDPROCTABLE"; + case MAKE_HRESULT(1,7,WSAEINVALIDPROVIDER) : return L"WSAEINVALIDPROVIDER"; + case MAKE_HRESULT(1,7,WSAEPROVIDERFAILEDINIT) : return L"WSAEPROVIDERFAILEDINIT"; + case MAKE_HRESULT(1,7,WSASYSCALLFAILURE) : return L"WSASYSCALLFAILURE"; + case MAKE_HRESULT(1,7,WSASERVICE_NOT_FOUND) : return L"WSASERVICE_NOT_FOUND"; + case MAKE_HRESULT(1,7,WSATYPE_NOT_FOUND) : return L"WSATYPE_NOT_FOUND"; + case MAKE_HRESULT(1,7,WSA_E_NO_MORE) : return L"WSA_E_NO_MORE"; + case MAKE_HRESULT(1,7,WSA_E_CANCELLED) : return L"WSA_E_CANCELLED"; + case MAKE_HRESULT(1,7,WSAEREFUSED) : return L"WSAEREFUSED"; + case MAKE_HRESULT(1,7,WSAHOST_NOT_FOUND) : return L"WSAHOST_NOT_FOUND"; + case MAKE_HRESULT(1,7,WSATRY_AGAIN) : return L"WSATRY_AGAIN"; + case MAKE_HRESULT(1,7,WSANO_RECOVERY) : return L"WSANO_RECOVERY"; + case MAKE_HRESULT(1,7,WSANO_DATA) : return L"WSANO_DATA"; + case MAKE_HRESULT(1,7,WSA_QOS_RECEIVERS) : return L"WSA_QOS_RECEIVERS"; + case MAKE_HRESULT(1,7,WSA_QOS_SENDERS) : return L"WSA_QOS_SENDERS"; + case MAKE_HRESULT(1,7,WSA_QOS_NO_SENDERS) : return L"WSA_QOS_NO_SENDERS"; + case MAKE_HRESULT(1,7,WSA_QOS_NO_RECEIVERS) : return L"WSA_QOS_NO_RECEIVERS"; + case MAKE_HRESULT(1,7,WSA_QOS_REQUEST_CONFIRMED) : return L"WSA_QOS_REQUEST_CONFIRMED"; + case MAKE_HRESULT(1,7,WSA_QOS_ADMISSION_FAILURE) : return L"WSA_QOS_ADMISSION_FAILURE"; + case MAKE_HRESULT(1,7,WSA_QOS_POLICY_FAILURE) : return L"WSA_QOS_POLICY_FAILURE"; + case MAKE_HRESULT(1,7,WSA_QOS_BAD_STYLE) : return L"WSA_QOS_BAD_STYLE"; + case MAKE_HRESULT(1,7,WSA_QOS_BAD_OBJECT) : return L"WSA_QOS_BAD_OBJECT"; + case MAKE_HRESULT(1,7,WSA_QOS_TRAFFIC_CTRL_ERROR) : return L"WSA_QOS_TRAFFIC_CTRL_ERROR"; + case MAKE_HRESULT(1,7,WSA_QOS_GENERIC_ERROR) : return L"WSA_QOS_GENERIC_ERROR"; + case MAKE_HRESULT(1,7,WSA_QOS_ESERVICETYPE) : return L"WSA_QOS_ESERVICETYPE"; + case MAKE_HRESULT(1,7,WSA_QOS_EFLOWSPEC) : return L"WSA_QOS_EFLOWSPEC"; + case MAKE_HRESULT(1,7,WSA_QOS_EPROVSPECBUF) : return L"WSA_QOS_EPROVSPECBUF"; + case MAKE_HRESULT(1,7,WSA_QOS_EFILTERSTYLE) : return L"WSA_QOS_EFILTERSTYLE"; + case MAKE_HRESULT(1,7,WSA_QOS_EFILTERTYPE) : return L"WSA_QOS_EFILTERTYPE"; + case MAKE_HRESULT(1,7,WSA_QOS_EFILTERCOUNT) : return L"WSA_QOS_EFILTERCOUNT"; + case MAKE_HRESULT(1,7,WSA_QOS_EOBJLENGTH) : return L"WSA_QOS_EOBJLENGTH"; + case MAKE_HRESULT(1,7,WSA_QOS_EFLOWCOUNT) : return L"WSA_QOS_EFLOWCOUNT"; + case MAKE_HRESULT(1,7,WSA_QOS_EUNKOWNPSOBJ) : return L"WSA_QOS_EUNKOWNPSOBJ"; + case MAKE_HRESULT(1,7,WSA_QOS_EPOLICYOBJ) : return L"WSA_QOS_EPOLICYOBJ"; + case MAKE_HRESULT(1,7,WSA_QOS_EFLOWDESC) : return L"WSA_QOS_EFLOWDESC"; + case MAKE_HRESULT(1,7,WSA_QOS_EPSFLOWSPEC) : return L"WSA_QOS_EPSFLOWSPEC"; + case MAKE_HRESULT(1,7,WSA_QOS_EPSFILTERSPEC) : return L"WSA_QOS_EPSFILTERSPEC"; + case MAKE_HRESULT(1,7,WSA_QOS_ESDMODEOBJ) : return L"WSA_QOS_ESDMODEOBJ"; + case MAKE_HRESULT(1,7,WSA_QOS_ESHAPERATEOBJ) : return L"WSA_QOS_ESHAPERATEOBJ"; + case MAKE_HRESULT(1,7,WSA_QOS_RESERVED_PETYPE) : return L"WSA_QOS_RESERVED_PETYPE"; + + // HTTP specific errors + case WEB_E_UNSUPPORTED_FORMAT: return L"WEB_E_UNSUPPORTED_FORMAT"; + case WEB_E_INVALID_XML: return L"WEB_E_INVALID_XML"; + case WEB_E_MISSING_REQUIRED_ELEMENT: return L"WEB_E_MISSING_REQUIRED_ELEMENT"; + case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return L"WEB_E_MISSING_REQUIRED_ATTRIBUTE"; + case WEB_E_UNEXPECTED_CONTENT: return L"WEB_E_UNEXPECTED_CONTENT"; + case WEB_E_RESOURCE_TOO_LARGE: return L"WEB_E_RESOURCE_TOO_LARGE"; + case WEB_E_INVALID_JSON_STRING: return L"WEB_E_INVALID_JSON_STRING"; + case WEB_E_INVALID_JSON_NUMBER: return L"WEB_E_INVALID_JSON_NUMBER"; + case WEB_E_JSON_VALUE_NOT_FOUND: return L"WEB_E_JSON_VALUE_NOT_FOUND"; + case HTTP_E_STATUS_UNEXPECTED: return L"HTTP_E_STATUS_UNEXPECTED"; + case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return L"HTTP_E_STATUS_UNEXPECTED_REDIRECTION"; + case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR"; + case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR"; + case HTTP_E_STATUS_AMBIGUOUS: return L"HTTP_E_STATUS_AMBIGUOUS"; + case HTTP_E_STATUS_MOVED: return L"HTTP_E_STATUS_MOVED"; + case HTTP_E_STATUS_REDIRECT: return L"HTTP_E_STATUS_REDIRECT"; + case HTTP_E_STATUS_REDIRECT_METHOD: return L"HTTP_E_STATUS_REDIRECT_METHOD"; + case HTTP_E_STATUS_NOT_MODIFIED: return L"HTTP_E_STATUS_NOT_MODIFIED"; + case HTTP_E_STATUS_USE_PROXY: return L"HTTP_E_STATUS_USE_PROXY"; + case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return L"HTTP_E_STATUS_REDIRECT_KEEP_VERB"; + case HTTP_E_STATUS_BAD_REQUEST: return L"HTTP_E_STATUS_BAD_REQUEST"; + case HTTP_E_STATUS_DENIED: return L"HTTP_E_STATUS_DENIED"; + case HTTP_E_STATUS_PAYMENT_REQ: return L"HTTP_E_STATUS_PAYMENT_REQ"; + case HTTP_E_STATUS_FORBIDDEN: return L"HTTP_E_STATUS_FORBIDDEN"; + case HTTP_E_STATUS_NOT_FOUND: return L"HTTP_E_STATUS_NOT_FOUND"; + case HTTP_E_STATUS_BAD_METHOD: return L"HTTP_E_STATUS_BAD_METHOD"; + case HTTP_E_STATUS_NONE_ACCEPTABLE: return L"HTTP_E_STATUS_NONE_ACCEPTABLE"; + case HTTP_E_STATUS_PROXY_AUTH_REQ: return L"HTTP_E_STATUS_PROXY_AUTH_REQ"; + case HTTP_E_STATUS_REQUEST_TIMEOUT: return L"HTTP_E_STATUS_REQUEST_TIMEOUT"; + case HTTP_E_STATUS_CONFLICT: return L"HTTP_E_STATUS_CONFLICT"; + case HTTP_E_STATUS_GONE: return L"HTTP_E_STATUS_GONE"; + case HTTP_E_STATUS_LENGTH_REQUIRED: return L"HTTP_E_STATUS_LENGTH_REQUIRED"; + case HTTP_E_STATUS_PRECOND_FAILED: return L"HTTP_E_STATUS_PRECOND_FAILED"; + case HTTP_E_STATUS_REQUEST_TOO_LARGE: return L"HTTP_E_STATUS_REQUEST_TOO_LARGE"; + case HTTP_E_STATUS_URI_TOO_LONG: return L"HTTP_E_STATUS_URI_TOO_LONG"; + case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return L"HTTP_E_STATUS_UNSUPPORTED_MEDIA"; + case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return L"HTTP_E_STATUS_RANGE_NOT_SATISFIABLE"; + case HTTP_E_STATUS_EXPECTATION_FAILED: return L"HTTP_E_STATUS_EXPECTATION_FAILED"; + case HTTP_E_STATUS_SERVER_ERROR: return L"HTTP_E_STATUS_SERVER_ERROR"; + case HTTP_E_STATUS_NOT_SUPPORTED: return L"HTTP_E_STATUS_NOT_SUPPORTED"; + case HTTP_E_STATUS_BAD_GATEWAY: return L"HTTP_E_STATUS_BAD_GATEWAY"; + case HTTP_E_STATUS_SERVICE_UNAVAIL: return L"HTTP_E_STATUS_SERVICE_UNAVAIL"; + case HTTP_E_STATUS_GATEWAY_TIMEOUT: return L"HTTP_E_STATUS_GATEWAY_TIMEOUT"; + case HTTP_E_STATUS_VERSION_NOT_SUP: return L"HTTP_E_STATUS_VERSION_NOT_SUP"; + + // WinINet specific errors + case INET_E_INVALID_URL: return L"INET_E_INVALID_URL"; + case INET_E_NO_SESSION: return L"INET_E_NO_SESSION"; + case INET_E_CANNOT_CONNECT: return L"INET_E_CANNOT_CONNECT"; + case INET_E_RESOURCE_NOT_FOUND: return L"INET_E_RESOURCE_NOT_FOUND"; + case INET_E_OBJECT_NOT_FOUND: return L"INET_E_OBJECT_NOT_FOUND"; + case INET_E_DATA_NOT_AVAILABLE: return L"INET_E_DATA_NOT_AVAILABLE"; + case INET_E_DOWNLOAD_FAILURE: return L"INET_E_DOWNLOAD_FAILURE"; + case INET_E_AUTHENTICATION_REQUIRED: return L"INET_E_AUTHENTICATION_REQUIRED"; + case INET_E_NO_VALID_MEDIA: return L"INET_E_NO_VALID_MEDIA"; + case INET_E_CONNECTION_TIMEOUT: return L"INET_E_CONNECTION_TIMEOUT"; + case INET_E_INVALID_REQUEST: return L"INET_E_INVALID_REQUEST"; + case INET_E_UNKNOWN_PROTOCOL: return L"INET_E_UNKNOWN_PROTOCOL"; + case INET_E_SECURITY_PROBLEM: return L"INET_E_SECURITY_PROBLEM"; + case INET_E_CANNOT_LOAD_DATA: return L"INET_E_CANNOT_LOAD_DATA"; + case INET_E_CANNOT_INSTANTIATE_OBJECT: return L"INET_E_CANNOT_INSTANTIATE_OBJECT"; + case INET_E_INVALID_CERTIFICATE: return L"INET_E_INVALID_CERTIFICATE"; + case INET_E_REDIRECT_FAILED: return L"INET_E_REDIRECT_FAILED"; + case INET_E_REDIRECT_TO_DIR: return L"INET_E_REDIRECT_TO_DIR"; + } + + return L""; +} diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager_SendReceive.cpp b/Minecraft.Client/Durango/Network/DQRNetworkManager_SendReceive.cpp new file mode 100644 index 0000000..9232d09 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager_SendReceive.cpp @@ -0,0 +1,409 @@ +#include "stdafx.h" + +#include "DQRNetworkManager.h" +#include "PartyController.h" +#include +#include +#include +#include "..\Minecraft.World\StringHelpers.h" +#include "base64.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +#include "ChatIntegrationLayer.h" + +using namespace Concurrency; +using namespace Windows::Foundation::Collections; + +// This method is called when bytes have been received that are to be passed on to the game itself. The data is associated with a small id so we can specify which network player +// that it was received for. +void DQRNetworkManager::BytesReceived(int smallId, BYTE *bytes, int byteCount) +{ + DQRNetworkPlayer *host = GetPlayerBySmallId(m_hostSmallId); + DQRNetworkPlayer *client = GetPlayerBySmallId(smallId); + + if( ( host == NULL ) || ( client == NULL ) ) + { + return; + } + + if( m_isHosting ) + { + m_listener->HandleDataReceived(client, host, bytes, byteCount ); + } + else + { + m_listener->HandleDataReceived(host, client, bytes, byteCount ); + } +// app.DebugPrintf("%d bytes received: %s\n", byteCount, bytes); +} + +// This method is called when network data is received, that is to be processed by the DQRNetworkManager itself. This is for handling internal +// updates such as assigning & unassigning of small Ids, transmission of the table of players currently in the session etc. +// Processing of these things is handled as a state machine so that we can receive a message split over more than one call to this method should +// the underlying communcation layer split data up somehow. +void DQRNetworkManager::BytesReceivedInternal(DQRConnectionInfo *connectionInfo, unsigned int sessionAddress, BYTE *bytes, int byteCount) +{ + BYTE *pNextByte = bytes; + BYTE *pEndByte = pNextByte + byteCount; + + do + { + BYTE byte = *pNextByte; + switch( connectionInfo->m_internalDataState ) + { + case DQRConnectionInfo::ConnectionState_InternalHeaderByte: + switch( byte ) + { + case DQR_INTERNAL_ASSIGN_SMALL_IDS: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask; + break; + case DQR_INTERNAL_UNASSIGN_SMALL_ID: + // Host only + if( connectionInfo->m_channelActive[connectionInfo->m_currentChannel] ) + { + int smallId = connectionInfo->m_smallId[connectionInfo->m_currentChannel]; + connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = false; + m_sessionAddressFromSmallId[smallId] = 0; + DQRNetworkPlayer *pPlayer = GetPlayerBySmallId(smallId); + if( pPlayer ) + { + RemoveRoomSyncPlayer(pPlayer); + SendRoomSyncInfo(); + } + } + break; + case DQR_INTERNAL_PLAYER_TABLE: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalRoomSyncData; + connectionInfo->m_pucRoomSyncData = new unsigned char[4]; + connectionInfo->m_roomSyncDataBytesToRead = 0; + connectionInfo->m_roomSyncDataBytesRead = 0; + break; + case DQR_INTERNAL_ADD_PLAYER_FAILED: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData; + connectionInfo->m_pucAddFailedPlayerData = new unsigned char[4]; + connectionInfo->m_addFailedPlayerDataBytesToRead = 0; + connectionInfo->m_addFailedPlayerDataBytesRead = 0; + break; + default: + break; + } + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask: + // Up to 4 smallIds are assigned at once, with the ones that are being assigned dictated by a mask byte which is passed in first. + // The small Ids themselves follow, always 4 bytes, and any that are masked as being assigned are processed, the other bytes ignored. + // In order work around a bug with the networking library, this particular packet (being the first this that is sent from a client) + // is at first sent unreliably, with retries, until a message is received back to the client, or it times out. We therefore have to be able + // to handle (and ignore) this being received more than once + DQRNetworkManager::LogCommentFormat(L"Small Ids being received"); + connectionInfo->m_smallIdReadMask = byte; + // Create a uniquely allocated byte to which names have been resolved, as another one of these packets could be received whilst that asyncronous process is going o n + connectionInfo->m_pucsmallIdReadMaskResolved = new unsigned char; + *connectionInfo->m_pucsmallIdReadMaskResolved = 0; + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId0; + connectionInfo->m_initialPacketReceived = true; + + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0: + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1: + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2: + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3: + { + int channel = ((int)connectionInfo->m_internalDataState) - DQRConnectionInfo::ConnectionState_InternalAssignSmallId0; + + if( ( connectionInfo->m_smallIdReadMask & ( 1 << channel ) ) && ( !connectionInfo->m_channelActive[channel] ) ) + { + // HOST ONLY + // Store the small Id that is associated with this send channel. In order work around a bug with the networking library, this particular packet + // (being the first this that is sent from a client) is sent unreliably, with retries, until a message is received back to the client, or it times out. + // We therefore have to be able to handle (and ignore) this being received more than once - hence the check of the bool above. + // At this point, the connection is considered properly active from the point of view of the host. + + int sessionIndex = GetSessionIndexForSmallId(byte); + if( sessionIndex != -1 ) + { + connectionInfo->m_channelActive[channel] = true; + connectionInfo->m_smallId[channel] = byte; + + m_sessionAddressFromSmallId[byte] = sessionAddress; + m_channelFromSmallId[byte] = channel; + + auto pAsyncOp = m_primaryUserXboxLiveContext->ProfileService->GetUserProfileAsync(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId); + DQRNetworkManager::LogCommentFormat(L"Session index of %d found for player with small id %d - attempting to resolve display name\n",sessionIndex,byte); + + DQRNetworkPlayer *pPlayer = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_REMOTE, true, 0, sessionAddress); + pPlayer->SetSmallId(byte); + pPlayer->SetUID(PlayerUID(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId->Data())); + + HostGamertagResolveDetails *resolveDetails = new HostGamertagResolveDetails(); + resolveDetails->m_pPlayer = pPlayer; + resolveDetails->m_sessionAddress = sessionAddress; + resolveDetails->m_channel = channel; + resolveDetails->m_sync = false; + + int mask = 1 << channel; + unsigned char *pucsmallIdReadMaskResolved = connectionInfo->m_pucsmallIdReadMaskResolved; + unsigned char ucsmallIdReadMask = connectionInfo->m_smallIdReadMask; + create_task( pAsyncOp ).then( [this,resolveDetails,mask,pucsmallIdReadMaskResolved,ucsmallIdReadMask] (task resultTask) + { + try + { + Microsoft::Xbox::Services::Social::XboxUserProfile^ result = resultTask.get(); + + resolveDetails->m_name.assign(result->Gamertag->Data()); // Use the gamertag for this data, as it is synchronised round all the machines and so we can't use a display name that could be a real name + + EnterCriticalSection(&m_csHostGamertagResolveResults); + // Update flags for which names have been resolved, and if this completes this set, then set the flag to say that we should synchronise these out to the clients + *pucsmallIdReadMaskResolved |= mask; + LogCommentFormat(L"<<>> Compare %d to %d",*pucsmallIdReadMaskResolved,ucsmallIdReadMask); + if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved) + { + resolveDetails->m_sync = true; + delete pucsmallIdReadMaskResolved; + } + m_hostGamertagResolveResults.push(resolveDetails); + LeaveCriticalSection(&m_csHostGamertagResolveResults); + } + + catch (Platform::Exception^ ex) + { + LogComment("Name resolve exception raised"); + // TODO - handle errors more usefully than just not setting the name... + EnterCriticalSection(&m_csHostGamertagResolveResults); + // Update flags for which names have been resolved, and if this completes this set, then set the flag to say that we should synchronise these out to the clients + *pucsmallIdReadMaskResolved |= mask; + if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved) + { + resolveDetails->m_sync = true; + delete pucsmallIdReadMaskResolved; + } + m_hostGamertagResolveResults.push(resolveDetails); + LeaveCriticalSection(&m_csHostGamertagResolveResults); + } + }); + } + } + } + + switch(connectionInfo->m_internalDataState) + { + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId1; + break; + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId2; + break; + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId3; + break; + case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3: + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; + break; + } + + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_InternalRoomSyncData: + connectionInfo->m_pucRoomSyncData[connectionInfo->m_roomSyncDataBytesRead++] = byte; + // The room sync info is sent as a 4 byte count of the length of XUID strings, then the RoomSyncData, then the XUID strings + if( connectionInfo->m_roomSyncDataBytesToRead == 0 ) + { + // At first stage of reading the 4 byte count + if( connectionInfo->m_roomSyncDataBytesRead == 4 ) + { + memcpy( &connectionInfo->m_roomSyncDataBytesToRead, connectionInfo->m_pucRoomSyncData, 4); + delete [] connectionInfo->m_pucRoomSyncData; + connectionInfo->m_roomSyncDataBytesToRead += sizeof(RoomSyncData); + connectionInfo->m_pucRoomSyncData = new unsigned char[ connectionInfo->m_roomSyncDataBytesToRead ]; + connectionInfo->m_roomSyncDataBytesRead = 0; + } + } + else if( connectionInfo->m_roomSyncDataBytesRead == connectionInfo->m_roomSyncDataBytesToRead ) + { + // Second stage of reading the variable length data - when we've read this all, we can created storage for the XUID strings and copy them all in + RoomSyncData *roomSyncData = (RoomSyncData *)connectionInfo->m_pucRoomSyncData; + wchar_t *pwcsData = (wchar_t *)((unsigned char *)connectionInfo->m_pucRoomSyncData + sizeof(RoomSyncData)); + for( int i = 0; i < roomSyncData->playerCount; i++ ) + { + unsigned int thisWchars = ( wcslen(pwcsData) + 1 ); + roomSyncData->players[i].m_XUID = new wchar_t[thisWchars]; + wcsncpy(roomSyncData->players[i].m_XUID, pwcsData, thisWchars); + pwcsData += thisWchars; + } + // Update the room sync data with this new data. This will handle notification of new and removed players + UpdateRoomSyncPlayers((RoomSyncData *)connectionInfo->m_pucRoomSyncData); + + delete connectionInfo->m_pucRoomSyncData; + connectionInfo->m_pucRoomSyncData = NULL; + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; + + // If we haven't actually established a connection yet for this channel, then this is the point where we can consider this active + if( !connectionInfo->m_channelActive[connectionInfo->m_currentChannel] ) + { + DQRNetworkManager::LogCommentFormat(L"Received data from host, channel %d considered active (%d bytes)\n",connectionInfo->m_currentChannel,connectionInfo->m_bytesRemaining); + connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = true; + + // This is also the time (as a client) to inform the chat integration layer of the host's session address, since we can now (reliably) send data to it + if(connectionInfo->m_currentChannel == 0) + { + if( m_chat ) + { + m_chat->OnNewSessionAddressAdded(m_hostSessionAddress); + } + } + } + + // Move to starting & playing states, if we are still joining rather than adding an additional player from this client, and we have all the local players here. + // We need to check that they are all here because we could have received a broadcast room sync data caused by another machine joining, and and so we can't assume + // that we're ready to go just yet. + if( m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_SENDING_UNRELIABLE ) + { + bool allLocalPlayersHere = true; + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( m_currentUserMask & ( 1 << i ) ) + { + if( GetLocalPlayerByUserIndex(i) == NULL ) + { + allLocalPlayersHere = false; + } + } + } + if( allLocalPlayersHere ) + { + DQRNetworkManager::LogComment(L"All local players present"); + SetState(DQRNetworkManager::DNM_INT_STATE_STARTING); + SetState(DQRNetworkManager::DNM_INT_STATE_PLAYING); + } + else + { + // Our players aren't all here yet. Going to keep on sending unreliable packets though until the connection is up and running + DQRNetworkManager::LogComment(L"All local players not yet present"); + } + } + } + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData: + connectionInfo->m_pucAddFailedPlayerData[connectionInfo->m_addFailedPlayerDataBytesRead++] = byte; + // The failed player info is sent as a 4 byte count of the length of XUID string, then the string itself + if( connectionInfo->m_addFailedPlayerDataBytesToRead == 0 ) + { + // At first stage of reading the 4 byte count + if( connectionInfo->m_addFailedPlayerDataBytesRead == 4 ) + { + memcpy( &connectionInfo->m_addFailedPlayerDataBytesToRead, connectionInfo->m_pucAddFailedPlayerData, 4); + delete [] connectionInfo->m_pucAddFailedPlayerData; + connectionInfo->m_pucAddFailedPlayerData = new unsigned char[ connectionInfo->m_addFailedPlayerDataBytesToRead ]; + connectionInfo->m_addFailedPlayerDataBytesRead = 0; + } + } + else if( connectionInfo->m_addFailedPlayerDataBytesRead == connectionInfo->m_addFailedPlayerDataBytesToRead ) + { + // XUID fully read, can now handle what to do with it + AddPlayerFailed(ref new Platform::String( (wchar_t *)connectionInfo->m_pucAddFailedPlayerData ) ); + delete [] connectionInfo->m_pucAddFailedPlayerData; + connectionInfo->m_pucAddFailedPlayerData = NULL; + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; + } + + pNextByte++; + break; + } + } while (pNextByte != pEndByte); +} + +// This method directly sends bytes via the network communication layer, used to send both game data & data internal to the DQRNetworkManager itself. +// This is used by higher level sending methods that wrap communications up with headers that can be processed at the receiving end. +void DQRNetworkManager::SendBytesRaw(int smallId, BYTE *bytes, int byteCount, bool reliableAndSequential) +{ + bool broadcast; + unsigned int sessionAddress; + +// app.DebugPrintf("{%d,%d - %d}\n",smallId,reliableAndSequential,byteCount); + + if( smallId == -1 ) + { + LogCommentFormat(L"Attempting broadcast, exception of address m_XRNS_Session->LocalSessionAddress %d %d %d", smallId, byteCount, reliableAndSequential); + // Broadcast, used from host only + broadcast = true; + sessionAddress = 0; + } + else + { + // Send to individual session address + broadcast = false; + if( m_isHosting ) + { + sessionAddress = m_sessionAddressFromSmallId[ smallId ]; + } + else + { + sessionAddress = m_hostSessionAddress; + } + } + RTS_SendData(bytes, byteCount, sessionAddress, reliableAndSequential, reliableAndSequential, reliableAndSequential, broadcast, true); +} + +// This method is called by the chat integration layer to be able to send data +void DQRNetworkManager::SendBytesChat(unsigned int address, BYTE *bytes, int byteCount, bool reliable, bool sequential, bool broadcast) +{ + unsigned int sessionAddress; + + if( broadcast ) + { + sessionAddress = 0; + } + else + { + // Send to individual session address + sessionAddress = address; + } + + RTS_SendData(bytes, byteCount, sessionAddress, reliable, sequential, false, broadcast, false); +} + +// This is the higher level sending method for sending game data - this prefixes the send with a header so that it will get routed to the correct player. +void DQRNetworkManager::SendBytes(int smallId, BYTE *bytes, int byteCount) +{ + EnterCriticalSection(&m_csSendBytes); + unsigned char *tempSendBuffer = (unsigned char *)malloc(8191 + 2); + + BYTE *data = bytes; + BYTE *dataEnd = bytes + byteCount; + + // Data to be sent has a header to say which of our own internal channels it is on (2 bits), and number of bytes that are in the message. + // The number of bytes has to be stored in 13 bits, and so a maximum of 8191 bytes can be send at a time. Split up longer messages into + // blocks of this size. + do + { + int bytesToSend = (int)(dataEnd - data); + if( bytesToSend > 8191 ) bytesToSend = 8191; + + // Send header with data sending mode - see full comment in DQRNetworkManagerEventHandlers::DataReceivedHandler + tempSendBuffer[0] = ( m_channelFromSmallId[smallId] << 5 ) | ( bytesToSend >> 8 ); + tempSendBuffer[1] = bytesToSend & 0xff; + memcpy(&tempSendBuffer[2], data, bytesToSend); + + SendBytesRaw(smallId, tempSendBuffer, bytesToSend + 2, true); + + data += bytesToSend; + } while (data != dataEnd); + + free(tempSendBuffer); + LeaveCriticalSection(&m_csSendBytes); +} + +int DQRNetworkManager::GetQueueSizeBytes() +{ + return m_RTS_Stat_totalBytes; +} + +int DQRNetworkManager::GetQueueSizeMessages() +{ + return m_RTS_Stat_totalSends; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/DQRNetworkManager_XRNSEvent.cpp b/Minecraft.Client/Durango/Network/DQRNetworkManager_XRNSEvent.cpp new file mode 100644 index 0000000..c985a19 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkManager_XRNSEvent.cpp @@ -0,0 +1,651 @@ +#include "stdafx.h" + +#include "DQRNetworkManager.h" +#include "PartyController.h" +#include +#include +#include +#include "..\Minecraft.World\StringHelpers.h" +#include "base64.h" + +#ifdef _DURANGO +#include "..\Minecraft.World\DurangoStats.h" +#endif + +#include "ChatIntegrationLayer.h" + +using namespace Concurrency; +using namespace Windows::Foundation::Collections; + +DQRNetworkManagerEventHandlers::DQRNetworkManagerEventHandlers(DQRNetworkManager *pDQRNet) +{ + m_pDQRNet = pDQRNet; +} + +void DQRNetworkManagerEventHandlers::Setup(WXNRs::Session^ session) +{ + try + { + m_dataReceivedToken = session->DataReceived += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::DataReceivedHandler); + m_sessionStatusToken = session->SessionStatusUpdate += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::SessionStatusUpdateHandler); + m_sessionAddressToken = session->SessionAddressDataChanged += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::SessionAddressDataChangedHandler); + m_addedSessionToken = session->AddedSessionAddress += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::AddedSessionAddressHandler); + m_removedSessionToken = session->RemovedSessionAddress += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::RemovedSessionAddressHandler); + m_globalDataToken = session->GlobalSessionDataChanged += ref new Windows::Foundation::EventHandler(this, &DQRNetworkManagerEventHandlers::GlobalSessionDataChangedHandler); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } +} + +void DQRNetworkManagerEventHandlers::Pulldown(WXNRs::Session^ session) +{ + try + { + session->DataReceived -= m_dataReceivedToken; + session->SessionStatusUpdate -= m_sessionStatusToken; + session->SessionAddressDataChanged -= m_sessionAddressToken; + session->AddedSessionAddress -= m_addedSessionToken; + session->RemovedSessionAddress -= m_removedSessionToken; + session->GlobalSessionDataChanged -= m_globalDataToken; + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } +} + +// This event handler is called directly by the realtime session layer, when data is received. We split this data into into data that is meant to be +// handled internally by the DQRNetworkManager, and that which is meant to be passed on to the game itself as communication between players. +void DQRNetworkManagerEventHandlers::DataReceivedHandler(Platform::Object^ session, WXNRs::DataReceivedEventArgs^ args) +{ +// DQRNetworkManager::LogCommentFormat(L"DataReceivedHandler session addr: 0x%x (%d bytes)",args->SessionAddress,args->Data->Length); + + if (session == m_pDQRNet->m_XRNS_Session) + { + EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + DQRNetworkManager::RTS_Message rtsMessage; + if( args->ChannelId == WXNRs::ChannelId::DefaultChatReceive ) + { + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED_CHAT; + } + else + { + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED; + } + rtsMessage.m_sessionAddress = args->SessionAddress; + rtsMessage.m_dataSize = args->Data->Length; + rtsMessage.m_pucData = (unsigned char *)malloc(rtsMessage.m_dataSize); + memcpy( rtsMessage.m_pucData, args->Data->Data, rtsMessage.m_dataSize ); + m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage); + LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + } +} + +// This event handler is called by the realtime session layer, when session address data is updated. We don't currently use session address data. +void DQRNetworkManagerEventHandlers::SessionAddressDataChangedHandler(Platform::Object^ session, WXNRs::SessionAddressDataChangedEventArgs^ args) +{ + DQRNetworkManager::LogComment(L"SessionAddressDataChangedHandler"); +} + +// This event handler is called by the realtime session layer when a session changes status. We use this to determine that a connection has been made made to the host, +// and the case when a connection has been terminated. +void DQRNetworkManagerEventHandlers::SessionStatusUpdateHandler(Platform::Object^ session, WXNRs::SessionStatusUpdateEventArgs^ args) +{ + DQRNetworkManager::LogComment(L"SessionStatusUpdateHandler"); + if (m_pDQRNet->m_XRNS_Session == session) + { + switch(args->NewStatus) + { + case WXNRs::SessionStatus::Active: + { + DQRNetworkManager::LogComment(L"Session active"); + m_pDQRNet->m_XRNS_LocalAddress = m_pDQRNet->m_XRNS_Session->LocalSessionAddress; + m_pDQRNet->m_XRNS_OldestAddress = m_pDQRNet->m_XRNS_Session->OldestSessionAddress; + EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + DQRNetworkManager::RTS_Message rtsMessage; + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_STATUS_ACTIVE; + rtsMessage.m_sessionAddress = 0; + rtsMessage.m_dataSize = 0; + rtsMessage.m_pucData = 0; + m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage); + LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + } + break; + case WXNRs::SessionStatus::Terminated: + { + DQRNetworkManager::LogComment(L"Session terminated"); + EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + DQRNetworkManager::RTS_Message rtsMessage; + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_STATUS_TERMINATED; + rtsMessage.m_sessionAddress = 0; + rtsMessage.m_dataSize = 0; + rtsMessage.m_pucData = 0; + m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage); + LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + } + break; + case WXNRs::SessionStatus::Activating: + DQRNetworkManager::LogComment(L"Session activating"); + break; + case WXNRs::SessionStatus::Terminating: + DQRNetworkManager::LogComment(L"Session terminating"); + break; + } + } +} + +// This event is called from the realtime session layer to notify any clients that a new endpoint has been connected into the network mesh. +void DQRNetworkManagerEventHandlers::AddedSessionAddressHandler(Platform::Object^ session, WXNRs::AddedSessionAddressEventArgs^ args) +{ + DQRNetworkManager::LogCommentFormat(L"AddedSessionAddressHandler session address 0x%x",args->SessionAddress); + EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + DQRNetworkManager::RTS_Message rtsMessage; + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_ADDED_SESSION_ADDRESS; + rtsMessage.m_sessionAddress = args->SessionAddress; + rtsMessage.m_dataSize = 0; + rtsMessage.m_pucData = 0; + m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage); + LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); +} + +// This event is called from the realtime session layer to notify any clients that an endpoint has been removed from the network mesh. +void DQRNetworkManagerEventHandlers::RemovedSessionAddressHandler(Platform::Object^ session, WXNRs::RemovedSessionAddressEventArgs^ args) +{ + DQRNetworkManager::LogCommentFormat(L"RemovedSessionAddressHandler session address 0x%x", args->SessionAddress); + EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); + DQRNetworkManager::RTS_Message rtsMessage; + rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_REMOVED_SESSION_ADDRESS; + rtsMessage.m_sessionAddress = args->SessionAddress; + rtsMessage.m_dataSize = 0; + rtsMessage.m_pucData = 0; + m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage); + LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming); +} + +// This event is called from the realtime session layer when session global data has been updated. We don't currently use global session data. +void DQRNetworkManagerEventHandlers::GlobalSessionDataChangedHandler(Platform::Object^ session, WXNRs::GlobalSessionDataChangedEventArgs^ args) +{ + DQRNetworkManager::LogComment(L"GlobalSessionDataChangedHandler"); +} + +void DQRNetworkManager::UpdateRTSStats() +{ + Platform::Array ^sessionAddresses = nullptr; + try + { + sessionAddresses = m_XRNS_Session->GetAllRemoteSessionAddresses(WXNRs::RemoteSessionAddressStateOptions::All, WXNRs::RemoteSessionAddressConnectivityOptions::All); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + + if( sessionAddresses ) + { + unsigned int totalBytes = 0; + unsigned int totalSends = 0; + for( unsigned int i = 0; i < sessionAddresses->Length; i++ ) + { + try + { + totalBytes += m_XRNS_Session->GetSendChannelOutstandingBytes(sessionAddresses->get(i), WXNRs::ChannelId::DefaultGameSend ); + totalSends += m_XRNS_Session->GetSendChannelOutstandingSends(sessionAddresses->get(i), WXNRs::ChannelId::DefaultGameSend ); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + } + m_RTS_Stat_totalBytes = totalBytes; + m_RTS_Stat_totalSends = totalSends; + } + else + { + m_RTS_Stat_totalBytes = 0; + m_RTS_Stat_totalSends = 0; + } +} + +void DQRNetworkManager::ProcessRTSMessagesIncoming() +{ + EnterCriticalSection(&m_csRTSMessageQueueIncoming); + while(m_RTSMessageQueueIncoming.size() > 0 ) + { + RTS_Message message = m_RTSMessageQueueIncoming.front(); + switch( message.m_eType ) + { + case eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED: + Process_RTS_MESSAGE_DATA_RECEIVED(message); + break; + case eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED_CHAT: + Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(message); + break; + case eRTSMessageType::RTS_MESSAGE_ADDED_SESSION_ADDRESS: + Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(message); + break; + case eRTSMessageType::RTS_MESSAGE_REMOVED_SESSION_ADDRESS: + Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(message); + break; + case eRTSMessageType::RTS_MESSAGE_STATUS_ACTIVE: + Process_RTS_MESSAGE_STATUS_ACTIVE(message); + break; + case eRTSMessageType::RTS_MESSAGE_STATUS_TERMINATED: + Process_RTS_MESSAGE_STATUS_TERMINATED(message); + break; + default: + break; + } + m_RTSMessageQueueIncoming.pop(); + } + LeaveCriticalSection(&m_csRTSMessageQueueIncoming); +}; + +void DQRNetworkManager::Process_RTS_MESSAGE_DATA_RECEIVED(RTS_Message &message) +{ + DQRConnectionInfo *connectionInfo; + if( m_isHosting ) + { + connectionInfo = m_sessionAddressToConnectionInfoMapHost[message.m_sessionAddress]; + } + else + { + connectionInfo = &m_connectionInfoClient; + } + + // Handle any header data, and actual data, in our stream. Data is as follows: + // Byte 0 Byte 1 + // fccsssss ssssssss + // + // Where: f is 0 if this is normal data send (to be passed up to the game), or is 1 if this is to be internally processed + // cc is the channel number that the data belongs to (0 to 3 representing actual player indices) + // sssssssssssss is the count of data bytes to follow (range 0 - 8191) + BYTE *pNextByte = message.m_pucData; + BYTE *pEndByte = pNextByte + message.m_dataSize; + do + { + BYTE byte = *pNextByte; + switch( connectionInfo->m_state ) + { + case DQRConnectionInfo::ConnectionState_HeaderByte0: + connectionInfo->m_currentChannel = ( byte >> 5 ) & 3; + connectionInfo->m_internalFlag = ( ( byte & 0x80 ) == 0x80 ); + + // Byte transfer mode. Bits 0-4 of this byte represent the upper 5 bits of our count of bytes to transfer... lower 8-bits will follow + connectionInfo->m_bytesRemaining = ((int)( byte & 0x1f )) << 8; + connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte1; + connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_HeaderByte1: + // Add in the lower 8 bits of our byte count, the upper 5 were obtained from the first header byte. + connectionInfo->m_bytesRemaining |= byte; + + // If there isn't any data following, then just go back to the initial state expecting another header byte. + if( connectionInfo->m_bytesRemaining == 0 ) + { + connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte0; + } + else + { + connectionInfo->m_state = DQRConnectionInfo::ConnectionState_ReadBytes; + } + pNextByte++; + break; + case DQRConnectionInfo::ConnectionState_ReadBytes: + // At this stage we can send up to connectionInfo->m_bytesRemaining bytes, or the number of bytes that we have remaining in the data received, whichever is lowest. + int bytesInBuffer = (int)(pEndByte - pNextByte); + int bytesToReceive = ( ( connectionInfo->m_bytesRemaining < bytesInBuffer ) ? connectionInfo->m_bytesRemaining : bytesInBuffer ); + + if( connectionInfo->m_internalFlag ) + { + BytesReceivedInternal(connectionInfo, message.m_sessionAddress, pNextByte, bytesToReceive ); + } + else + { + BytesReceived(connectionInfo->m_smallId[connectionInfo->m_currentChannel], pNextByte, bytesToReceive ); + } + + // Adjust counts and pointers + pNextByte += bytesToReceive; + connectionInfo->m_bytesRemaining -= bytesToReceive; + + // Set state back to expect a header if there is no more data bytes to receive + if( connectionInfo->m_bytesRemaining == 0 ) + { + connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte0; + } + break; + } + } while (pNextByte != pEndByte); + + free(message.m_pucData); +} + +void DQRNetworkManager::Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(RTS_Message &message) +{ + if( m_chat ) + { + m_chat->OnIncomingChatMessage(message.m_sessionAddress, Platform::ArrayReference(message.m_pucData, message.m_dataSize) ); + free(message.m_pucData); + } +} + +void DQRNetworkManager::Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(RTS_Message &message) +{ + if( m_chat ) + { + m_chat->OnNewSessionAddressAdded(message.m_sessionAddress); + } + + // New session address - add a mapping for it + if( m_isHosting ) + { + auto it = m_sessionAddressToConnectionInfoMapHost.find(message.m_sessionAddress); + DQRConnectionInfo *connectionInfo; + if( it == m_sessionAddressToConnectionInfoMapHost.end() ) + { + connectionInfo = new DQRConnectionInfo(); + + m_sessionAddressToConnectionInfoMapHost[message.m_sessionAddress] = connectionInfo; + } + else + { + // This shouldn't happen as we should be removing mappings as session addresses are removed. + connectionInfo = it->second; + connectionInfo->Reset(); + } + + } +} + +void DQRNetworkManager::Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(RTS_Message &message) +{ + if( m_chat ) + { + m_chat->RemoveRemoteConsole(message.m_sessionAddress); + } + + if( m_isHosting ) + { + auto it = m_sessionAddressToConnectionInfoMapHost.find(message.m_sessionAddress); + + if( it != m_sessionAddressToConnectionInfoMapHost.end() ) + { + delete it->second; + m_sessionAddressToConnectionInfoMapHost.erase(it); + RemoveRoomSyncPlayersWithSessionAddress(message.m_sessionAddress); + SendRoomSyncInfo(); + } + } + else + { + // As the client, if we are disonnected from the host, then it is all over. Proceed as if leaving the room. + if( message.m_sessionAddress == m_hostSessionAddress ) + { + LeaveRoom(); + } + } +} + +void DQRNetworkManager::Process_RTS_MESSAGE_STATUS_ACTIVE(RTS_Message &message) +{ + // When we detect that the session has become active, we start sending unreliable packets, until we get some data back. This is because there is an issue with the + // realtime session layer where it is telling us that the connection is active a bit to early, and it will disconnect if it receives a packet that must be reliable in this + // state. + if( !m_isHosting ) + { + m_firstUnreliableSendTime = 0; + m_hostSessionAddress = m_XRNS_OldestAddress; + // Also initialise the status of this connection + m_connectionInfoClient.Reset(); + SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_SENDING_UNRELIABLE); + } +} + +void DQRNetworkManager::Process_RTS_MESSAGE_STATUS_TERMINATED(RTS_Message &message) +{ + if( m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION ) + { + m_joinCreateSessionAttempts++; + if( m_joinCreateSessionAttempts > DQRNetworkManager::JOIN_CREATE_SESSION_MAX_ATTEMPTS ) + { + SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_FAILED); + } + else + { + SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_GET_SDA); + } + } +} + +int DQRNetworkManager::_RTSDoWorkThread(void* lpParameter) +{ + DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; + return pDQR->RTSDoWorkThread(); +} + +static const DWORD XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE = 0x00000001; +int DQRNetworkManager::RTSDoWorkThread() +{ + do + { + if( m_XRNS_Session ) + { + try + { + m_XRNS_Session->DoWork(20); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + UpdateRTSStats(); + } + else + { + Sleep(20); + } + ProcessRTSMessagesOutgoing(); + } while(true); +} + +void DQRNetworkManager::ProcessRTSMessagesOutgoing() +{ + EnterCriticalSection(&m_csRTSMessageQueueOutgoing); + while(m_RTSMessageQueueOutgoing.size() > 0 ) + { + RTS_Message message = m_RTSMessageQueueOutgoing.front(); + switch( message.m_eType ) + { + case eRTSMessageType::RTS_MESSAGE_START_CLIENT: + Process_RTS_MESSAGE_START_CLIENT(message); + break; + case eRTSMessageType::RTS_MESSAGE_START_HOST: + Process_RTS_MESSAGE_START_HOST(message); + break; + case eRTSMessageType::RTS_MESSAGE_TERMINATE: + Process_RTS_MESSAGE_TERMINATE(message); + break; + case eRTSMessageType::RTS_MESSAGE_SEND_DATA: + Process_RTS_MESSAGE_SEND_DATA(message); + break; + default: + break; + } + m_RTSMessageQueueOutgoing.pop(); + } + LeaveCriticalSection(&m_csRTSMessageQueueOutgoing); +}; + +void DQRNetworkManager::Process_RTS_MESSAGE_START_CLIENT(RTS_Message &message) +{ + if( m_XRNS_Session ) + { + m_eventHandlers->Pulldown(m_XRNS_Session); + // Close XRNS session + try + { + m_XRNS_Session->TerminateLocalSession(XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + + m_XRNS_Session = nullptr; + } + + m_XRNS_Session = ref new WXNRs::Session( m_localSocketAddress, m_remoteSocketAddress, MAX_PLAYERS_IN_TEMPLATE, 0); + m_XRNS_Session->MinSendRate = 512000; + + LogCommentFormat(L"connect retry period %d retries %d, data retry count %d, data retry timeout %d\n",m_XRNS_Session->ConnectRetryPeriod,m_XRNS_Session->MaxConnectRetries,m_XRNS_Session->MaxDataRetries,m_XRNS_Session->MinDataRetryTimeout); + + m_XRNS_Session->MaxConnectRetries = 50; // 50 at 100ms intervals = 5 seconds of attempting to connect + + m_eventHandlers->Setup(m_XRNS_Session); +} + +void DQRNetworkManager::Process_RTS_MESSAGE_START_HOST(RTS_Message &message) +{ + m_XRNS_Session = ref new WXNRs::Session( m_localSocketAddress, MAX_PLAYERS_IN_TEMPLATE, 0); + m_XRNS_Session->MinSendRate = 512000; + m_XRNS_Session->MaxConnectRetries = 50; // 50 at 100ms intervals = 5 seconds of attempting to connect + m_eventHandlers->Setup(m_XRNS_Session); +} + +void DQRNetworkManager::Process_RTS_MESSAGE_TERMINATE(RTS_Message &message) +{ + if( m_XRNS_Session ) + { + m_eventHandlers->Pulldown(m_XRNS_Session); + // Close XRNS session + try + { + m_XRNS_Session->TerminateLocalSession(XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + + m_XRNS_Session = nullptr; + } +} + +void DQRNetworkManager::Process_RTS_MESSAGE_SEND_DATA(RTS_Message &message) +{ + if( m_XRNS_Session ) + { + unsigned int sessionAddress = message.m_sessionAddress; + + try + { + if( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE ) + { + sessionAddress = m_XRNS_Session->LocalSessionAddress; + } + + m_XRNS_Session->Send( ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_GAME_CHANNEL ) ? WXNRs::ChannelId::DefaultGameSend : WXNRs::ChannelId::DefaultChatSend, + ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE ) ? WXNRs::SendExceptionType::ExcludedAddresses : WXNRs::SendExceptionType::IncludedAddresses, + Platform::ArrayReference(&message.m_sessionAddress, 1), + Platform::ArrayReference(message.m_pucData, message.m_dataSize), + 0, + ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_RELIABLE ) ? WXNRs::Send_Reliability::Reliable : WXNRs::Send_Reliability::NonReliable, + ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_SEQUENTIAL ) ? WXNRs::Send_Sequence::Sequential : WXNRs::Send_Sequence::NonSequential, + WXNRs::Send_Ack::AckNormal, + ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_COALESCE ) ? WXNRs::Send_Coalesce::CoalesceDelay : WXNRs::Send_Coalesce::CoalesceNever, + WXNRs::Send_MiscState::NoMiscState ); + } + catch(Platform::COMException^ ex) + { + // swallow exceptions + } + catch(...) + { + // swallow exceptions + } + } + free(message.m_pucData); +} + +void DQRNetworkManager::RTS_StartCient() +{ + EnterCriticalSection(&m_csRTSMessageQueueOutgoing); + RTS_Message message; + message.m_eType = eRTSMessageType::RTS_MESSAGE_START_CLIENT; + message.m_pucData = NULL; + message.m_dataSize = 0; + m_RTSMessageQueueOutgoing.push(message); + LeaveCriticalSection(&m_csRTSMessageQueueOutgoing); +} + +void DQRNetworkManager::RTS_StartHost() +{ + EnterCriticalSection(&m_csRTSMessageQueueOutgoing); + RTS_Message message; + message.m_eType = eRTSMessageType::RTS_MESSAGE_START_HOST; + message.m_pucData = NULL; + message.m_dataSize = 0; + m_RTSMessageQueueOutgoing.push(message); + LeaveCriticalSection(&m_csRTSMessageQueueOutgoing); +} + +void DQRNetworkManager::RTS_Terminate() +{ + EnterCriticalSection(&m_csRTSMessageQueueOutgoing); + RTS_Message message; + message.m_eType = eRTSMessageType::RTS_MESSAGE_TERMINATE; + message.m_pucData = NULL; + message.m_dataSize = 0; + m_RTSMessageQueueOutgoing.push(message); + LeaveCriticalSection(&m_csRTSMessageQueueOutgoing); +} + +void DQRNetworkManager::RTS_SendData(unsigned char *pucData, unsigned int dataSize, unsigned int sessionAddress, bool reliable, bool sequential, bool coalesce, bool broadcastMode, bool gameChannel ) +{ + EnterCriticalSection(&m_csRTSMessageQueueOutgoing); + RTS_Message message; + message.m_eType = eRTSMessageType::RTS_MESSAGE_SEND_DATA; + message.m_pucData = (unsigned char *)malloc(dataSize); + memcpy(message.m_pucData, pucData, dataSize); + message.m_dataSize = dataSize; + message.m_sessionAddress = sessionAddress; + message.m_flags = 0; + if( reliable ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_RELIABLE; + if( sequential ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_SEQUENTIAL; + if( coalesce ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_COALESCE; + if( broadcastMode ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE; + if( gameChannel ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_GAME_CHANNEL; + m_RTSMessageQueueOutgoing.push(message); + LeaveCriticalSection(&m_csRTSMessageQueueOutgoing); +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/DQRNetworkPlayer.cpp b/Minecraft.Client/Durango/Network/DQRNetworkPlayer.cpp new file mode 100644 index 0000000..e2d82a9 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkPlayer.cpp @@ -0,0 +1,204 @@ +#include "stdafx.h" +#include "DQRNetworkPlayer.h" +#include "ChatIntegrationLayer.h" + +DQRNetworkPlayer::DQRNetworkPlayer() +{ +} + +DQRNetworkPlayer::DQRNetworkPlayer(DQRNetworkManager *manager, eDQRNetworkPlayerType playerType, bool onHost, int localPlayerIdx, unsigned int sessionAddress) +{ + m_localPlayerIdx = localPlayerIdx; + m_type = playerType; + m_host = onHost; + m_manager = manager; + m_customData = 0; + m_sessionAddress = sessionAddress; +} + +DQRNetworkPlayer::~DQRNetworkPlayer() +{ +} + +PlayerUID DQRNetworkPlayer::GetUID() +{ + return m_UID; +} + +void DQRNetworkPlayer::SetUID(PlayerUID UID) +{ + m_UID = UID; +} + +int DQRNetworkPlayer::GetLocalPlayerIndex() +{ + return m_localPlayerIdx; +} + +uintptr_t DQRNetworkPlayer::GetCustomDataValue() +{ + return m_customData; +} + +void DQRNetworkPlayer::SetCustomDataValue(uintptr_t data) +{ + m_customData = data; +} + +bool DQRNetworkPlayer::IsRemote() +{ + return !IsLocal(); +} + +bool DQRNetworkPlayer::IsHost() +{ + return (m_type == DNP_TYPE_HOST); +} + +bool DQRNetworkPlayer::IsLocal() +{ + // m_host determines whether this *machine* is hosting the game, not this player (which is determined by m_type) + if( m_host ) + { + // If we are the hosting machine, then both the host & local players are local to this machine + return (m_type == DNP_TYPE_HOST) || (m_type == DNP_TYPE_LOCAL); + } + else + { + // Not hosting, just local players are actually physically local + return (m_type == DNP_TYPE_LOCAL) ; + } +} + +bool DQRNetworkPlayer::IsSameSystem(DQRNetworkPlayer *other) +{ + return ( m_sessionAddress == other->m_sessionAddress ); +} + +bool DQRNetworkPlayer::IsTalking() +{ + if(m_manager->m_chat == nullptr) return false; + Microsoft::Xbox::GameChat::ChatUser^ chatUser = m_manager->m_chat->GetChatUserByXboxUserId(ref new Platform::String(m_UID.toString().c_str())); + + if( chatUser == nullptr ) return false; + if( chatUser->TalkingMode == Microsoft::Xbox::GameChat::ChatUserTalkingMode::NotTalking ) + { + return false; + } + else + { + return true; + } +} + +bool DQRNetworkPlayer::HasVoice() +{ + if(m_manager->m_chat == nullptr) return false; + Microsoft::Xbox::GameChat::ChatUser^ chatUser = m_manager->m_chat->GetChatUserByXboxUserId(ref new Platform::String(m_UID.toString().c_str())); + + if( chatUser == nullptr ) return false; + if( ((int)chatUser->ParticipantType) & ((int)Windows::Xbox::Chat::ChatParticipantTypes::Talker) ) + { + return true; + } + else + { + return false; + } +} + +bool DQRNetworkPlayer::HasCamera() +{ + return false; +} + +LPCWSTR DQRNetworkPlayer::GetGamertag() +{ + return m_name; +} + +int DQRNetworkPlayer::GetSmallId() +{ + return (int)m_smallId; +} + +void DQRNetworkPlayer::SetSmallId(unsigned char smallId) +{ + m_smallId = smallId; +} + +int DQRNetworkPlayer::GetSessionIndex() +{ + return m_manager->GetSessionIndex(this); +} + +// Attempt to send data, of any size, from this player to that specified by pPlayerTarget. This may not be possible depending on the two players, due to +// our star shaped network connectivity. Data may be any size, and is copied so on returning from this method it does not need to be preserved. +void DQRNetworkPlayer::SendData( DQRNetworkPlayer *pPlayerTarget, const void *data, unsigned int dataSize ) +{ + // Our network is connected as a star. If we are the host, then we can send to any remote player. If we're a client, we can send only to the host. + // The host can also send to other local players, but this doesn't need to go through Rudp. + if( m_host ) + { + if( ( m_type == DNP_TYPE_HOST ) && ( pPlayerTarget->m_type == DNP_TYPE_REMOTE ) ) + { + // Rudp communication from host to remote player - handled by remote player instance + pPlayerTarget->SendInternal(data,dataSize); + } + else + { + // Can't do any other types of communications + assert(false); + } + } + else + { + if( ( m_type == DNP_TYPE_LOCAL ) && ( pPlayerTarget->m_type == DNP_TYPE_HOST ) ) + { + // Rudp communication from client to host - handled by this player instace + SendInternal(data, dataSize); + } + else + { + // Can't do any other types of communications + assert(false); + } + } +} + +void DQRNetworkPlayer::SendInternal(const void *data, unsigned int dataSize) +{ + m_manager->SendBytes(m_smallId, (BYTE *)data, dataSize); +} + +int DQRNetworkPlayer::GetSendQueueSizeBytes() +{ + return m_manager->GetQueueSizeBytes(); +} + +int DQRNetworkPlayer::GetSendQueueSizeMessages() +{ + return m_manager->GetQueueSizeMessages(); +} + +wchar_t *DQRNetworkPlayer::GetName() +{ + return m_name; +} + +void DQRNetworkPlayer::SetName(const wchar_t *name) +{ + wcscpy_s(m_name, name); +} + +// Return display name (if display name is not set, return name instead) +wstring DQRNetworkPlayer::GetDisplayName() +{ + return (m_displayName == L"") ? m_name : m_displayName; +} + +// Set display name +void DQRNetworkPlayer::SetDisplayName(wstring displayName) +{ + m_displayName = displayName; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/DQRNetworkPlayer.h b/Minecraft.Client/Durango/Network/DQRNetworkPlayer.h new file mode 100644 index 0000000..fd14072 --- /dev/null +++ b/Minecraft.Client/Durango/Network/DQRNetworkPlayer.h @@ -0,0 +1,65 @@ +#pragma once +#include "DQRNetworkManager.h" +#include + +// This is the lowest level class for handling the concept of a player on Durango. This is managed by DQRNetworkManager. The game shouldn't directly communicate +// with this class, as it is wrapped by NetworkPlayerDurango which is an implementation of a platform-independent interface INetworkPlayer. + +class DQRNetworkPlayer +{ +public: + friend class DQRNetworkManager; + + typedef enum + { + DNP_TYPE_HOST, // This player represents the host + DNP_TYPE_LOCAL, // On host - this player is a local player that needs communicated with specially not using rudp. On clients - this is a local player, where network communications can be used to communicate with the host + DNP_TYPE_REMOTE, // On host - this player can be used to communicate from between the host and this player. On clients - this is a remote player that cannot be communicated with + } eDQRNetworkPlayerType; + + DQRNetworkPlayer(); + DQRNetworkPlayer(DQRNetworkManager *manager, eDQRNetworkPlayerType playerType, bool onHost, int localPlayerIdx, unsigned int sessionAddress); + ~DQRNetworkPlayer(); + + PlayerUID GetUID(); + void SetUID(PlayerUID UID); + int GetLocalPlayerIndex(); + uintptr_t GetCustomDataValue(); + void SetCustomDataValue(uintptr_t data); + bool IsRemote(); + bool IsHost(); + bool IsLocal(); + bool IsSameSystem(DQRNetworkPlayer *other); + bool HasVoice(); + bool IsTalking(); + bool HasCamera(); + LPCWSTR GetGamertag(); + int GetSmallId(); + void SetSmallId(unsigned char smallId); + int GetSessionIndex(); + void SendData( DQRNetworkPlayer *pPlayerTarget, const void *data, unsigned int dataSize ); + + int GetSendQueueSizeBytes(); + int GetSendQueueSizeMessages(); + + wchar_t *GetName(); + void SetName(const wchar_t *name); + + std::wstring GetDisplayName(); + void SetDisplayName(std::wstring displayName); +private: + void SendInternal(const void *data, unsigned int dataSize); + + eDQRNetworkPlayerType m_type; // The player type + bool m_host; // Whether this actual player class is stored on a host (not whether it represents the host, or a player on the host machine) + int m_localPlayerIdx; // Index of this player on the machine to which it belongs + DQRNetworkManager *m_manager; // Pointer back to the manager that is managing this player + + PlayerUID m_UID; + uintptr_t m_customData; + unsigned char m_smallId; + unsigned int m_sessionAddress; + + wchar_t m_name[21]; + std::wstring m_displayName; +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/NetworkPlayerDurango.cpp b/Minecraft.Client/Durango/Network/NetworkPlayerDurango.cpp new file mode 100644 index 0000000..fd2181d --- /dev/null +++ b/Minecraft.Client/Durango/Network/NetworkPlayerDurango.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "NetworkPlayerDurango.h" + +NetworkPlayerDurango::NetworkPlayerDurango(DQRNetworkPlayer *qnetPlayer) +{ + m_dqrPlayer = qnetPlayer; + m_pSocket = NULL; +} + +unsigned char NetworkPlayerDurango::GetSmallId() +{ + return m_dqrPlayer->GetSmallId(); +} + +void NetworkPlayerDurango::SendData(INetworkPlayer *player, const void *pvData, int dataSize, bool lowPriority) +{ + m_dqrPlayer->SendData( ((NetworkPlayerDurango *)player)->m_dqrPlayer, pvData, dataSize ); +} + +bool NetworkPlayerDurango::IsSameSystem(INetworkPlayer *player) +{ + return m_dqrPlayer->IsSameSystem(((NetworkPlayerDurango *)player)->m_dqrPlayer); +} + +int NetworkPlayerDurango::GetSendQueueSizeBytes( INetworkPlayer *player, bool lowPriority ) +{ + return m_dqrPlayer->GetSendQueueSizeBytes(); +} + +int NetworkPlayerDurango::GetSendQueueSizeMessages( INetworkPlayer *player, bool lowPriority ) +{ + return m_dqrPlayer->GetSendQueueSizeMessages(); +} + +int NetworkPlayerDurango::GetCurrentRtt() +{ + return 0; // TODO +} + +bool NetworkPlayerDurango::IsHost() +{ + return m_dqrPlayer->IsHost(); +} + +bool NetworkPlayerDurango::IsGuest() +{ + return false; // TODO +} + +bool NetworkPlayerDurango::IsLocal() +{ + return m_dqrPlayer->IsLocal(); +} + +int NetworkPlayerDurango::GetSessionIndex() +{ + return m_dqrPlayer->GetSessionIndex(); +} + +bool NetworkPlayerDurango::IsTalking() +{ + return m_dqrPlayer->IsTalking(); +} + +bool NetworkPlayerDurango::IsMutedByLocalUser(int userIndex) +{ + return false; +} + +bool NetworkPlayerDurango::HasVoice() +{ + return m_dqrPlayer->HasVoice(); +} + +bool NetworkPlayerDurango::HasCamera() +{ + return false; // TODO +} + +int NetworkPlayerDurango::GetUserIndex() +{ + return m_dqrPlayer->GetLocalPlayerIndex(); +} + +void NetworkPlayerDurango::SetSocket(Socket *pSocket) +{ + m_pSocket = pSocket; +} + +Socket *NetworkPlayerDurango::GetSocket() +{ + return m_pSocket; +} + +const wchar_t *NetworkPlayerDurango::GetOnlineName() +{ + return m_dqrPlayer->GetName(); +} + +wstring NetworkPlayerDurango::GetDisplayName() +{ + return m_dqrPlayer->GetDisplayName(); +} + +PlayerUID NetworkPlayerDurango::GetUID() +{ + return m_dqrPlayer->GetUID(); +} + +void NetworkPlayerDurango::SetUID(PlayerUID UID) +{ + m_dqrPlayer->SetUID(UID); +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/NetworkPlayerDurango.h b/Minecraft.Client/Durango/Network/NetworkPlayerDurango.h new file mode 100644 index 0000000..c95e8ae --- /dev/null +++ b/Minecraft.Client/Durango/Network/NetworkPlayerDurango.h @@ -0,0 +1,39 @@ +#pragma once + +#include "..\..\Common\Network\NetworkPlayerInterface.h" +#include "DQRNetworkPlayer.h" + +// This is an implementation of the INetworkPlayer interface, for Durango. It effectively wraps the DQRNetworkPlayer class in a non-platform-specific way. + +class NetworkPlayerDurango : public INetworkPlayer +{ +public: + // Common player interface + NetworkPlayerDurango(DQRNetworkPlayer *sqrPlayer); + virtual unsigned char GetSmallId(); + virtual void SendData(INetworkPlayer *player, const void *pvData, int dataSize, bool lowPriority); + virtual bool IsSameSystem(INetworkPlayer *player); + virtual int GetSendQueueSizeBytes( INetworkPlayer *player, bool lowPriority ); + virtual int GetSendQueueSizeMessages( INetworkPlayer *player, bool lowPriority ); + virtual int GetCurrentRtt(); + virtual bool IsHost(); + virtual bool IsGuest(); + virtual bool IsLocal(); + virtual int GetSessionIndex(); + virtual bool IsTalking(); + virtual bool IsMutedByLocalUser(int userIndex); + virtual bool HasVoice(); + virtual bool HasCamera(); + virtual int GetUserIndex(); + virtual void SetSocket(Socket *pSocket); + virtual Socket *GetSocket(); + virtual const wchar_t *GetOnlineName(); + virtual wstring GetDisplayName(); + virtual PlayerUID GetUID(); + + void SetUID(PlayerUID UID); + +private: + DQRNetworkPlayer *m_dqrPlayer; + Socket *m_pSocket; +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/PartyController.cpp b/Minecraft.Client/Durango/Network/PartyController.cpp new file mode 100644 index 0000000..753a71a --- /dev/null +++ b/Minecraft.Client/Durango/Network/PartyController.cpp @@ -0,0 +1,1205 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved +#include "stdafx.h" +#include "PartyController.h" +#include "DQRNetworkManager.h" +#include + +/* +#include "time.h" +#include "../GameLogic/Game.h" +#include "../Utils/LogController.h" +#include "../Utils/Utils.h" +#include "UserController.h" +#include "SessionController.h" +*/ + +using namespace Concurrency; +using namespace Microsoft::Xbox::Services::Multiplayer; +using namespace Microsoft::Xbox::Services::Matchmaking; +using namespace Microsoft::Xbox::Services; +using namespace Windows::Foundation::Collections; +using namespace Windows::Foundation; +using namespace Windows::Storage::Streams; +using namespace Windows::Storage; +using namespace Windows::System; +using namespace Windows::UI::Core; +using namespace Windows::Xbox::Multiplayer; +using namespace Windows::Xbox::Networking; +using namespace Windows::Xbox::System; + + +PartyController::PartyController(DQRNetworkManager *pDQRNet) : + m_isGameSessionReadyEventTriggered(false), + m_isGamePlayerEventRegistered(false), + m_pDQRNet(pDQRNet) +{ +} + +void PartyController::DebugPrintPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView ) +{ + DQRNetworkManager::LogComment( L"PartyView:" ); + if( partyView == nullptr ) + { + DQRNetworkManager::LogComment( L" No party view" ); + return; + } + + DQRNetworkManager::LogComment( L" GameSession::SessionName: " + (partyView->GameSession ? partyView->GameSession->SessionName : L"NONE") ); + DQRNetworkManager::LogComment( L" GameSession::SessionTemplateName " + (partyView->GameSession ? partyView->GameSession->SessionTemplateName : L"NONE") ); + DQRNetworkManager::LogComment( L" GameSession::ServiceConfigurationId " + (partyView->GameSession ? partyView->GameSession->ServiceConfigurationId : L"NONE") ); + DQRNetworkManager::LogComment( L" MatchSession: " + (partyView->MatchSession ? partyView->MatchSession->SessionName : L"NONE") ); + DQRNetworkManager::LogComment( L" IsPartyInAnotherTitle: " + partyView->IsPartyInAnotherTitle.ToString() ); + + DQRNetworkManager::LogComment( L" Members: " + partyView->Members->Size.ToString() ); + for( PartyMember^ member : partyView->Members ) + { + DQRNetworkManager::LogComment( L" Member:" ); + DQRNetworkManager::LogComment( L" XboxUserID: " + member->XboxUserId ); + DQRNetworkManager::LogCommentFormat( L" IsLocalUser: %s", member->IsLocal ? L"TRUE" : L"FALSE" ); +// DQRNetworkManager::LogComment( L" JoinTime: " + Utils::DateTimeToString(member->JoinTime) ); + } + + DQRNetworkManager::LogComment( L" ReservedMembers: " + partyView->ReservedMembers->Size.ToString() ); + for( Platform::String^ memberXuid : partyView->ReservedMembers ) + { + DQRNetworkManager::LogComment( L" ReservedMember:" ); + DQRNetworkManager::LogComment( L" XboxUserID " + memberXuid ); + } + + DQRNetworkManager::LogComment( L" MembersGroupedByDevice: " + partyView->MembersGroupedByDevice->Size.ToString() ); + for( PartyMemberDeviceGroup^ partyMemberDeviceGroup : partyView->MembersGroupedByDevice ) + { + DQRNetworkManager::LogComment( L" Device:" ); + for( PartyMember^ member : partyMemberDeviceGroup->Members ) + { + DQRNetworkManager::LogComment( L" Member:" ); + DQRNetworkManager::LogComment( L" XboxUserID: " + member->XboxUserId ); + DQRNetworkManager::LogCommentFormat( L" IsLocalUser: %s", member->IsLocal ? L"TRUE" : L"FALSE" ); +// DQRNetworkManager::LogComment( L" JoinTime: " + Utils::DateTimeToString(member->JoinTime) ); + } + } +} + +void PartyController::RefreshPartyView() +{ +// LogComment( L"RefreshPartyView" ); + + PartyView^ partyView; + try + { + IAsyncOperation^ partyOperation = Party::GetPartyViewAsync(); + create_task(partyOperation) + .then([this, &partyView] (task t) + { + try + { + Concurrency::critical_section::scoped_lock lock(m_lock); + partyView = t.get(); + } + catch ( Platform::Exception^ ex ) + { + if( ex->HResult != (int)Windows::Xbox::Multiplayer::PartyErrorStatus::EmptyParty ) + { +// LogCommentWithError( L"GetPartyView failed", ex->HResult ); + } + + } + }).wait(); + } + catch ( Platform::Exception^ ex ) + { + partyView = nullptr; +// LogCommentWithError( L"GetPartyView failed", ex->HResult ); + } + + DebugPrintPartyView(partyView); + + SetPartyView(partyView); +} + +// Add any players specified in userMask to the party. This method returns a bool that is true if the a player was added, which wasn't already +// in the game session associated with the party. This is used to determine whether we should be waiting for a slot to be added for it by the host. +bool PartyController::AddLocalUsersToParty(int userMask, Windows::Xbox::System::User^ primaryUser) +{ + bool addedNewPlayerToPartyThatIsntInSession = false; + + PartyView^ partyView = GetPartyView(); + + // If there's already a party, then attempt to get a session document as we'll be needing this later + MultiplayerSession^ session; + if( partyView ) + { + MXS::XboxLiveContext^ xblContext = ref new MXS::XboxLiveContext(primaryUser); + if( xblContext ) + { + // Get a copy of the session document, for this user + auto multiplayerSessionAsync = xblContext->MultiplayerService->GetCurrentSessionAsync( m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession)); + create_task(multiplayerSessionAsync).then([&session,this](task t) + { + try + { + session = t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"GetCurrentSessionAsync failed", ex->HResult ); + } + }) + .wait(); + } + } + + // Adds all local players that are specified in userMask to the party belonging to the primary player + IVector^ localUsersToAddVector = ref new Platform::Collections::Vector; + for( int i = 0; i < 4; i++ ) + { + if( userMask & ( 1 << i ) ) + { + bool alreadyHere = false; + if( partyView ) + { + Windows::Xbox::System::User^ userToAdd = ProfileManager.GetUser(i, true); + for( int j = 0; j < partyView->Members->Size; j++ ) + { + if( partyView->Members->GetAt(j)->XboxUserId == userToAdd->XboxUserId ) + { + primaryUser = userToAdd; // If there is already a party, then the acting user passed to AddLocalUsersAsync must be a user that is in that party, so make sure this is the case. Doesn't matter Which user. + alreadyHere = true; + break; + } + } + } + + if( !alreadyHere ) + { + localUsersToAddVector->Append(ProfileManager.GetUser(i)); + bool alreadyInSession = false; + if( session ) + { + if( m_pDQRNet->IsPlayerInSession( ProfileManager.GetUser(i, true)->XboxUserId, session, NULL ) ) + { + alreadyInSession = true; + } + } + if( !alreadyInSession ) + { + addedNewPlayerToPartyThatIsntInSession = true; + } + } + } + } + + IVectorView^ localUsersToAddVecView = localUsersToAddVector->GetView(); + + try + { + IAsyncAction^ addMemberOperation = Party::AddLocalUsersAsync( primaryUser, localUsersToAddVecView ); + + create_task(addMemberOperation) + .then([this] (task t) + { + try + { + t.get(); + RefreshPartyView(); + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed", ex->HResult ); + } + catch (Platform::OperationCanceledException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed - operation cancelled", ex->HResult ); + } + }).wait(); + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed to create", ex->HResult ); + } + + return addedNewPlayerToPartyThatIsntInSession; +} + +void PartyController::CheckPartySessionFull(Windows::Xbox::System::User^ primaryUser) +{ + RefreshPartyView(); + + PartyView^ partyView = GetPartyView(); + + // If there's already a party, then attempt to get a session document + MultiplayerSession^ session; + if( partyView && partyView->GameSession) + { + MXS::XboxLiveContext^ xblContext = ref new MXS::XboxLiveContext(primaryUser); + if( xblContext ) + { + // Get a copy of the session document, for this user + auto multiplayerSessionAsync = xblContext->MultiplayerService->GetCurrentSessionAsync( m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession)); + create_task(multiplayerSessionAsync).then([&session,this](task t) + { + try + { + session = t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"GetCurrentSessionAsync failed", ex->HResult ); + } + }) + .wait(); + } + if( session ) + { + int nonLocalPlayerCount = 0; + for( int i = 0; i < session->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + + bool isLocalPlayer = false; + for( int j = 4; j < 12; j++ ) + { + WXS::User ^user = InputManager.GetUserForGamepad(j); + if( user != nullptr ) + { + if( user->XboxUserId == member->XboxUserId ) + { + isLocalPlayer = true; + } + } + } + if( !isLocalPlayer ) + { + nonLocalPlayerCount++; + } + } + app.DebugPrintf(">>>>> Invited to a game with a non-local player count of %d\n", nonLocalPlayerCount); + + if( nonLocalPlayerCount >= DQRNetworkManager::MAX_ONLINE_PLAYER_COUNT ) + { + m_pDQRNet->FlagInvitedToFullSession(); + } + } + } +} + +void PartyController::SetJoinability(bool isJoinable) +{ + Party::Joinability = isJoinable ? WXM::SessionJoinability::JoinableByFriends : WXM::SessionJoinability::InviteOnly; +} + +void PartyController::DisassociateSessionFromParty( Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference) +{ + RefreshPartyView(); + + PartyView^ partyView = GetPartyView(); + + if( partyView ) + { + // We need a user to be the acting user, when disassociating the party. Attempt to find a party member who is local. + for( int j = 0; j < partyView->Members->Size; j++ ) + { + Windows::Xbox::Multiplayer::PartyMember^ partyMember = partyView->Members->GetAt(j); + if( partyMember->IsLocal ) + { + IVectorView^ localUsers = Windows::Xbox::System::User::Users; + + for( int i = 0; i < localUsers->Size; i++ ) + { + Windows::Xbox::System::User^ localUser = localUsers->GetAt(i); + if( localUser->XboxUserId == partyMember->XboxUserId ) + { + // Convert the session reference (in Microsoft::Xbox::Services::Multiplayer namespace) to Windows::Xbox::Multiplayer names space so we can use in the party system + WXM::MultiplayerSessionReference^ winSessionRef = m_pDQRNet->ConvertToWindowsXboxMultiplayerSessionReference(sessionReference); + + auto disassociateSessionAsync = WXM::Party::DisassociateGameSessionAsync(localUser, winSessionRef); + create_task(disassociateSessionAsync).then([this](task t) + { + try + { + t.get(); // if t.get() didn't throw, it succeeded + } + catch (Platform::COMException^ ex) + { + m_pDQRNet->LogCommentWithError( L"DisassociateGameSessionAsync failed", ex->HResult ); + } + }); + //.wait(); // There are situations in which this never completes (?!) so waiting isn't a good idea + return; + } + } + } + } + } + + +} + +void PartyController::RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser) +{ + PartyView^ partyView = GetPartyView(); + + if( partyView == nullptr ) return; + + // Attempt to remove all local users that are currently in the party + IVectorView^ localUsers = Windows::Xbox::System::User::Users; + IVector^ localUsersToRemoveVector = ref new Platform::Collections::Vector; + for( int i = 0; i < localUsers->Size; i++ ) + { + Windows::Xbox::System::User^ userToRemove = localUsers->GetAt(i); + for( int j = 0; j < partyView->Members->Size; j++ ) + { + if( partyView->Members->GetAt(j)->XboxUserId == userToRemove->XboxUserId ) + { + localUsersToRemoveVector->Append(userToRemove); + break; + } + } + } + + IVectorView^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView(); + + IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView ); + + create_task(removeMemberOperation) + .then([this] (task t) + { + try + { + t.get(); + RefreshPartyView(); + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult ); + } + }).wait(); +} + +void PartyController::RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser, int playerMask, Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference) +{ + PartyView^ partyView = GetPartyView(); + + if( partyView == nullptr ) return; + + // Don't leave the party if it isn't actually the party belonging to the session we are in - this can happen when switching from one game session to another, after accepting an invite to a game whilst already playing + if( sessionReference != nullptr ) + { + if( partyView->GameSession != nullptr ) + { + if( partyView->GameSession->SessionName != sessionReference->SessionName ) return; + } + } + + // Attempt to remove all specified local users that are currently in the party. Check that each player is actually in + // the party, as we'll get an exception for trying to remove players that aren't + + IVector^ localUsersToRemoveVector = ref new Platform::Collections::Vector; + + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + Windows::Xbox::System::User^ userToRemove = ProfileManager.GetUser(i, true); + if(userToRemove!=nullptr) + { + for( int j = 0; j < partyView->Members->Size; j++ ) + { + if( partyView->Members->GetAt(j)->XboxUserId == userToRemove->XboxUserId ) + { + localUsersToRemoveVector->Append(userToRemove); + break; + } + } + } + } + } + + IVectorView^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView(); + + IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView ); + + create_task(removeMemberOperation) + .then([this] (task t) + { + try + { + t.get(); + RefreshPartyView(); + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult ); + } + }).wait(); +} + +void PartyController::RemoveLocalUserFromParty(Windows::Xbox::System::User^ userToRemove) +{ + // Attempt to a specific local user from the party + + RefreshPartyView(); + + // Don't need to do anything if there isn't currently a party + PartyView^ partyView = GetPartyView(); + + if( partyView == nullptr ) return; + + // Don't need to do anything if this player isn't in the current party - the remove will raise an exception + bool inParty = false; + for( int i = 0; i < partyView->Members->Size; i++ ) + { + if( partyView->Members->GetAt(i)->XboxUserId == userToRemove->XboxUserId ) + { + inParty = true; + break; + } + } + if( !inParty ) return; + + IVector^ localUsersToRemoveVector = ref new Platform::Collections::Vector; + localUsersToRemoveVector->Append(userToRemove); + IVectorView^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView(); + + IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView ); + + create_task(removeMemberOperation) + .then([this] (task t) + { + try + { + t.get(); + RefreshPartyView(); + } + catch (Platform::COMException^ ex) + { + DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult ); + } + }).wait(); +} + +void PartyController::RegisterGamePlayersChangedEventHandler() +{ + // Listen to Party::GamePlayersChanged + // Only the HOST should register for this event to detect if any party members were added that should be pulled into the game. + EventHandler^ partyGamePlayersChanged = ref new EventHandler( + [this] (Platform::Object^, GamePlayersChangedEventArgs^ eventArgs) + { + OnGamePlayersChanged( eventArgs ); + }); + m_partyGamePlayersChangedToken = Party::GamePlayersChanged += partyGamePlayersChanged; + m_isGamePlayerEventRegistered = true; +} + +void PartyController::RegisterEventHandlers() +{ + // Listen to Party Roster Changed + EventHandler^ partyRosterChangedEvent = ref new EventHandler( + [this] (Platform::Object^, PartyRosterChangedEventArgs^ eventArgs) + { + OnPartyRosterChanged( eventArgs ); + }); + m_partyRosterChangedToken = Party::PartyRosterChanged += partyRosterChangedEvent; + + // Listen to Party State Changed + EventHandler^ partyStateChangedEvent = ref new EventHandler( + [this] (Platform::Object^, PartyStateChangedEventArgs^ eventArgs) + { + OnPartyStateChanged( eventArgs ); + }); + m_partyStateChangedToken = Party::PartyStateChanged += partyStateChangedEvent; + // Listen to Game Session Ready + EventHandler^ gameSessionReadyEvent = ref new EventHandler( + [this] (Platform::Object^, GameSessionReadyEventArgs^ eventArgs) + { + OnGameSessionReady(eventArgs); + }); + m_partyGameSessionReadyToken = Party::GameSessionReady += gameSessionReadyEvent; +} + +void PartyController::UnregisterEventHandlers() +{ + Party::PartyRosterChanged -= m_partyRosterChangedToken; + Party::PartyStateChanged -= m_partyStateChangedToken; + Party::GameSessionReady -= m_partyGameSessionReadyToken; +} + +void PartyController::UnregisterGamePlayersEventHandler() +{ + if(m_isGamePlayerEventRegistered) + { + Party::GamePlayersChanged -= m_partyGamePlayersChangedToken; + } +} + +// This event will fire when : +// - A new Match Session registered to party, or +// - A new Game Session is registered to party (via RegisterGame call, or as a result of matchmaking), and +// - Party Title ID changes (which will trigger change in IsPartyInAnotherTItle bool flag). +void PartyController::OnPartyStateChanged( PartyStateChangedEventArgs^ eventArgs ) +{ + DQRNetworkManager::LogComment( L"OnPartyStateChanged"); + RefreshPartyView(); +} + +void PartyController::OnPartyRosterChanged( PartyRosterChangedEventArgs^ eventArgs ) +{ + if( m_pDQRNet->m_multiplayerSession == nullptr) return; + + RefreshPartyView(); + + XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext; + if( xboxLiveContext == nullptr ) return; + + DQRNetworkManager::LogComment( L"OnPartyRosterChanged"); + + if( eventArgs->RemovedMembers->Size ) + { + DQRNetworkManager::LogComment( L"Removed Members:"); + } + + // First, establish whether an active player local to this machine have been removed + bool activePlayerRemoved = false; + int playerLeavingMask = 0; + + // If there's no party anymore, then we're all leaving + if( m_partyView == nullptr ) + { + playerLeavingMask = m_pDQRNet->m_currentUserMask; + } + else + { + // Still a party, find out who left + for( int i = 0; i < eventArgs->RemovedMembers->Size; i++ ) + { + DQRNetworkPlayer *player = m_pDQRNet->GetPlayerByXuid(PlayerUID(eventArgs->RemovedMembers->GetAt(i)->Data())); + if( player ) + { + if( player->IsLocal() ) + { + playerLeavingMask |= ( 1 << player->GetLocalPlayerIndex() ); + } + } + DQRNetworkManager::LogComment(eventArgs->RemovedMembers->GetAt(i)); + } + } + + // If a local player is leaving the party, we want to handle it generally as if they had selected to exit from within the game, assuming that they have just deliberatly removed themselves from + // the party via the system interface. However... we may be being removed from this party because we have just accepted a request to join Another party via a "game session ready" sort of prompt. + // I don't think there's any way to distinguish these two things happening at this stage, so at this point we will signal to the DQR layer what has just happened, + // and it will only do something about it after some period of time has passed without a new game party becoming ready for the player to join + if( playerLeavingMask ) + { + m_pDQRNet->HandlePlayerRemovedFromParty(playerLeavingMask); + } +} + +#define LAST_INVITED_TIME_TIMEOUT 3 * 60 //secs +void PartyController::AddAvailableGamePlayers(IVectorView^ availablePlayers, int& remainingSlots, MultiplayerSession^ currentSession) +{ + bool bNewMembersAdded = false; + for each (Windows::Xbox::Multiplayer::GamePlayer^ player in availablePlayers) + { + if( remainingSlots <= 0 ) + { + DQRNetworkManager::LogCommentFormat( L"No more available slots - broadcasting failure of adding player %s",player->XboxUserId->Data()); + m_pDQRNet->SendAddPlayerFailed(player->XboxUserId); + continue; + } + + // Not sure what this condition is actually for - removing until I can see a use for it +#if 0 + if( GetTimeBetweenInSeconds(player->LastInvitedTime, GetCurrentTime()) < LAST_INVITED_TIME_TIMEOUT ) + { + DQRNetworkManager::LogComment( L"Possible user just exited; skipping join request" ); + continue; + } +#endif + + if( m_pDQRNet->IsPlayerInSession(player->XboxUserId, currentSession, NULL)) + { + DQRNetworkManager::LogComment( L"Player is already in session; skipping join request: " + player->XboxUserId->ToString() ); + continue; + } + + // Have a search through our local players, to see if we have a controller for this player. If so, then we'll want to add them directly to the game (if at all), rather than using + // the reservation system + bool bFoundLocal = false; + for( int i = 4; i < 12; i++ ) + { + WXS::User^ user = InputManager.GetUserForGamepad(i); + if( user != nullptr ) + { + if( user->XboxUserId == player->XboxUserId ) + { + bFoundLocal = true; + // Check that they aren't in the game already (don't see how this could be the case anyway) + if( m_pDQRNet->GetPlayerByXuid( PlayerUID(player->XboxUserId->Data()) ) == NULL ) + { + // And check whether they are signed in yet or not + int userIdx = -1; + for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ ) + { + WXS::User^ user2 = ProfileManager.GetUser(j); + if( user2 != nullptr ) + { + if(user2->XboxUserId == user->XboxUserId) + { + userIdx = j; + break; + } + } + } + + // If not found, then attempt to add them since we've found a controller + if( userIdx == -1 ) + { + // Found the appropriate controller. Attempt to add it - this will return -1 if unsuccessful + userIdx = ProfileManager.AddGamepadToGame(i); + } + + // Found a slot, so just need to add the user now + if( userIdx != -1 ) + { + m_pDQRNet->AddLocalPlayerByUserIndex(userIdx); + } + } + } + } + } + if(bFoundLocal) + { + continue; + } + + currentSession->AddMemberReservation( player->XboxUserId, m_pDQRNet->GetNextSmallIdAsJsonString(), true ); + DQRNetworkManager::LogComment( L"Member added: " + player->XboxUserId->ToString() ); + bNewMembersAdded = true; + remainingSlots--; + } + + if(bNewMembersAdded) + { + XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext; + DQRNetworkManager::LogComment( L"New members found and added from related parties" ); + HRESULT hr = S_OK; + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ outputSession = m_pDQRNet->WriteSessionHelper( + xboxLiveContext, + currentSession, + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionWriteMode::UpdateExisting, + hr + ); + + if(outputSession != nullptr) + { + Windows::Xbox::Multiplayer::MultiplayerSessionReference^ convertedSessionRef = + m_pDQRNet->ConvertToWindowsXboxMultiplayerSessionReference(currentSession->SessionReference); + Windows::Xbox::System::User^ actingUser = m_pDQRNet->m_primaryUser; + Party::PullReservedPlayersAsync(actingUser, convertedSessionRef); + + m_pDQRNet->HandleSessionChange( outputSession ); + } + } +} + +void PartyController::OnGamePlayersChanged( GamePlayersChangedEventArgs^ eventArgs ) +{ + DQRNetworkManager::LogComment( L"OnGamePlayersChanged"); + + RefreshPartyView(); + + if( m_pDQRNet->m_primaryUser == nullptr ) return; + + IVectorView^ availablePlayers = nullptr; + auto asyncGetAvailablePlayers = Party::GetAvailableGamePlayersAsync(m_pDQRNet->m_primaryUser); + create_task(asyncGetAvailablePlayers).then([&availablePlayers](task^> t) + { + try + { + availablePlayers = t.get(); + } + catch( Platform::COMException^ ex ) + { + } + }).wait(); + + if( availablePlayers == nullptr ) return; + + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = + m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(eventArgs->GameSession); + + if(sessionRef == nullptr) + { +// LogComment(L"OnGamePlayersChanged: invalid sessionRef."); + return; + } + + XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext; + IAsyncOperation^ asyncOp = xboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(asyncOp) + .then([this, availablePlayers, &xboxLiveContext] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession = t.get(); + + int remainingSlots = currentSession->SessionConstants->MaxMembersInSession - currentSession->Members->Size; + + DQRNetworkManager::LogCommentFormat( L"OnGamePlayersChanged - calling AddAvailableGamePlayers, with %d players available",availablePlayers->Size); + + // This should be called if we have available slots or not, because it also handles broadcasting failed joins to clients already in the game, which + // the clients need to know that adding a local player has failed + AddAvailableGamePlayers(availablePlayers, remainingSlots, currentSession); + } + catch ( Platform::COMException^ ex ) + { +// LogCommentWithError( L"OnGameSessionReady failed to retrieve current session", ex->HResult ); + } + }).wait(); +} + +void PartyController::OnGameSessionReady( GameSessionReadyEventArgs^ eventArgs ) +{ + DQRNetworkManager::LogComment( L"OnGameSessionReady"); + + // If we are already in this session, then we'll be trying to add a player to the session rather than start up a new game. Set a flag if this is the case. + bool bInSession = false; + if( m_pDQRNet->m_multiplayerSession ) + { + if( m_pDQRNet->m_multiplayerSession->SessionReference->SessionName == eventArgs->GameSession->SessionName ) + { + bInSession = true; + } + } + + // Get a current copy of the MPSD, and search for the players that we are trying to join in it + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = + m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(eventArgs->GameSession); + + vector localAdhocAdditions; + + // Use context from any user at all for this, since this might happen before we are in a game and won't have anything set up in the network manager itself. We are only + // using it to read the session so there shouldn't be any requirements to use a particular live context + WXS::User ^anyUser = nullptr; + if( WXS::User::Users->Size > 0 ) + { + anyUser = WXS::User::Users->GetAt(0); + } + if( anyUser == nullptr ) + { + app.DebugPrintf("Abandoning gamesessionready, no user found\n"); + return; + } + XboxLiveContext^ xboxLiveContext = ref new MXS::XboxLiveContext(anyUser); + + app.DebugPrintf("Gamesessionready user and xboxlivecontext found\n"); + + IAsyncOperation^ asyncOp = xboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); + create_task(asyncOp) + .then([this, eventArgs, bInSession, &localAdhocAdditions,sessionRef] (task t) + { + try + { + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session = t.get(); + + // Check if our joining users are in the session + int userFoundMask = 0; + m_pDQRNet->LogCommentFormat(L"Found session, size %d\n",session->Members->Size); + for( int i = 0; i < session->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + + Platform::String^ memberXUID = member->XboxUserId; + bool isAJoiningXuid = false; + for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ ) + { + if( m_pDQRNet->m_joinSessionUserMask & ( 1 << j ) ) + { + if( m_pDQRNet->m_joinSessionXUIDs[j] == memberXUID ) + { + userFoundMask |= ( 1 << j ); + isAJoiningXuid = true; + } + } + } + + // If: + // + // (1) this isn't a player we are actively trying to join + // (2) it isn't currently in the game (only applicable if we are in the same session we're considering going to) + // (3) we've got a controller assigned to a user with a matching xuid + // + // then we might still be interested in this as this could be someone joining via the system interface + if( !isAJoiningXuid ) // Isn't someone we are actively trying to join + { + if( (!bInSession) || // If not in a game session at all + ( ( m_pDQRNet->GetState() == DQRNetworkManager::DNM_INT_STATE_PLAYING ) && ( m_pDQRNet->GetPlayerByXuid( PlayerUID(memberXUID->Data()) ) == NULL ) ) // Or we're fully in, and this player isn't in it + ) + { + for( int j = 4; j < 12; j++ ) // Final check that we have a gamepad for this xuid so that we might possibly be able to pair someone up + { + WXS::User^ user = InputManager.GetUserForGamepad(j); + if( user != nullptr ) + { + m_pDQRNet->LogCommentFormat(L"%d %d %s vs %s\n",i,j,user->XboxUserId->Data(), memberXUID->Data()); + if( user->XboxUserId == memberXUID ) + { + localAdhocAdditions.push_back( memberXUID ); + } + } + } + } + } + } + + // If we are in the middle of a game-controlled join, then we only (currently) care about users involved in this turning up in the session + if( m_pDQRNet->m_joinSessionUserMask != 0 ) + { + m_pDQRNet->LogComment(L"In game controlled join\n"); + // If all the users we are expecting to join are here, then proceed to either start a new game or just add to the existing session{ + if( userFoundMask == m_pDQRNet->m_joinSessionUserMask ) + { + m_pDQRNet->m_joinSessionUserMask = 0; + m_pDQRNet->m_currentUserMask |= userFoundMask; + + if( m_pDQRNet->m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS ) + { + // Attempting to join a game via the discovered list of parties that our friends are in + m_pDQRNet->JoinSession(userFoundMask); + } + else + { + if( bInSession ) + { + // Already in a game, and adding a new local player - make them active + m_pDQRNet->AddUsersToSession( userFoundMask, m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference( eventArgs->GameSession )); + } + } + } + if( localAdhocAdditions.size() > 0 ) + { + // TODO - need to flag up the fact that there were adhoc additions here that we've just ignored, so we can come back and do something with them at a more appropriate time (ie when m_joinSessionUserMask transitions back to 0) + } + } + else if( localAdhocAdditions.size() > 0 ) + { + m_pDQRNet->LogComment(L"Not in game controlled join, but have other player(s) of possible interest\n"); + // Not trying to do a game controlled join at the moment, and we've got at least one user of interest that we've got a controller for and isn't currently playing + + // If we are in a session, then we might be able to add local players into the game + if( bInSession ) + { + m_pDQRNet->LogComment(L"In session, may be able to add local player to game\n"); + int adhocMask = 0; + for( int i = 0; i < localAdhocAdditions.size(); i++ ) + { + // First search to see if we already have the player signed in + int userIdx = -1; + for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ ) + { + WXS::User^ user = ProfileManager.GetUser(j); + if( user != nullptr ) + { + if(user->XboxUserId == localAdhocAdditions[i]) + { + userIdx = j; + break; + } + } + } + // If not found - see if we have a controller for that person so we can add them + if( userIdx == -1 ) + { + for( int j = 4; j < 12; j++ ) + { + WXS::User^ user = InputManager.GetUserForGamepad(j); + if( user != nullptr ) + { + if( user->XboxUserId == localAdhocAdditions[i] ) + { + // Found the appropriate controller. Attempt to add it - this will return -1 if unsuccessful + userIdx = ProfileManager.AddGamepadToGame(j); + break; + } + } + } + } + // If we found or were able to add a player to one of the 4 local player slots, accumulate in a mask + if( userIdx != -1 ) + { + m_pDQRNet->m_joinSessionXUIDs[userIdx] = localAdhocAdditions[i]; + adhocMask |= ( 1 << userIdx ); + } + } + // If we found anyone adhoc to add, join them into the game now + if( adhocMask ) + { + m_pDQRNet->AddUsersToSession( adhocMask, m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference( eventArgs->GameSession )); + } + } + else + { + // Not in a game, or not in The game that we've just been notified of. We need to try to do a best-fit to work out who + // of our signed in players or controllers we know about, should be being considered as being invited to this game + m_pDQRNet->LogComment(L"Not in a game, considering as possible invite scenario\n"); + int playerIdx = -1; + for( int i = 0; i < session->Members->Size; i++ ) // First pass through to see if we've a signed in user that is in the session we've been added to + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ ) + { + WXS::User ^user = ProfileManager.GetUser(j); + if( user != nullptr ) + { + if( user->XboxUserId == member->XboxUserId ) + { + DQRNetworkManager::LogCommentFormat(L"Found already signed in player %s to act as invite recipient",member->XboxUserId->Data()); + playerIdx = j; + break; + } + } + } + } + if( playerIdx == -1 ) // If nothing found, second pass through to attempt to find a controller that matches + { + for( int i = 0; i < session->Members->Size; i++ ) + { + MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i); + for( int j = 4; j < 12; j++ ) + { + WXS::User ^user = InputManager.GetUserForGamepad(j); + if( user != nullptr ) + { + if( user->XboxUserId == member->XboxUserId ) + { + DQRNetworkManager::LogCommentFormat(L"Found controller %d for %s (session idx %d) to act as invite recipient",j,member->XboxUserId->Data(),i); + playerIdx = ProfileManager.AddGamepadToGame(j); + if( playerIdx != -1 ) + { + DQRNetworkManager::LogCommentFormat(L"Assigned controller to user index %d",playerIdx); + break; + } + } + } + } + } + } + if( playerIdx != -1 ) + { + m_pDQRNet->LogComment(L"Player found, considered as invite scenario\n"); + m_pDQRNet->HandleNewPartyFoundForPlayer(); + // Note - must pass player 0 here as the player that the invite is for, or else the xbox 1 code generally breaks because it sets a non-zero primary player. We're going to + // be trying to join all current local users to the new game session though. no matter who the invite is for. + DQRNetworkManager::SetPartyProcessJoinSession(0, sessionRef->SessionName, sessionRef->ServiceConfigurationId, sessionRef->SessionTemplateName); + } + else + { + app.DebugPrintf("No player found to join party with\n"); + } + } + } + + } + catch ( Platform::COMException^ ex ) + { + m_pDQRNet->LogCommentWithError( L"OnGameSessionReady failed to retrieve current session", ex->HResult ); + } + }).wait(); + +} + +bool PartyController::CanJoinParty() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + if( m_partyView == nullptr ) + { + return false; + } + + return (m_partyView->GameSession != nullptr && !m_partyView->IsPartyInAnotherTitle); +} + +bool PartyController::CanInvitePartyToMyGame( + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ multiplayerSession + ) +{ + if( multiplayerSession == nullptr ) + { + // If my session doesn't exist then shouldn't invite party + return false; + } + + PartyView^ partyView = GetPartyView(); + if( partyView == nullptr ) + { + // If the party view doesn't have a session, then could invite party + return true; + } + + if( partyView->IsPartyInAnotherTitle ) + { + // If my session doesn't exist then shouldn't invite party + return true; + } + + if( partyView->GameSession != nullptr ) + { + // If my session and the party session differs, then could invite party + if ( _wcsicmp(partyView->GameSession->ServiceConfigurationId->Data(), multiplayerSession->SessionReference->ServiceConfigurationId->Data() ) != 0 ) + { + return true; + } + if ( _wcsicmp(partyView->GameSession->SessionName->Data(), multiplayerSession->SessionReference->SessionName->Data() ) != 0 ) + { + return true; + } + if ( _wcsicmp(partyView->GameSession->SessionTemplateName->Data(), multiplayerSession->SessionReference->SessionTemplateName->Data() ) != 0 ) + { + return true; + } + } + else + { + // If the party doesn't have a session, then I could invite party + return true; + } + + // If the party is in my session then return false + return false; +} + +int PartyController::GetActiveAndReservedMemberPartySize() +{ + int partySize = 0; + + PartyView^ partyView = GetPartyView(); + if ( partyView != nullptr ) + { + partySize = partyView->Members->Size + partyView->ReservedMembers->Size; + } + + return partySize; +} + +bool PartyController::IsPartyInAnotherTitle() +{ + PartyView^ partyView = GetPartyView(); + if( partyView == nullptr ) + { + return false; + } + + return partyView->IsPartyInAnotherTitle; +} + +bool PartyController::IsGameSessionReadyEventTriggered() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + return m_isGameSessionReadyEventTriggered; +} + +void PartyController::ClearGameSessionReadyEventTriggered() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + m_isGameSessionReadyEventTriggered = false; +} + +bool PartyController::DoesPartySessionExist() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + + if( m_partyView != nullptr && m_partyView->GameSession != nullptr) + { + return true; + } + return false; +} + +Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference ^ PartyController::GetGamePartySessionReference() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + + if( m_partyView != nullptr ) + { + if( m_partyView->GameSession != nullptr) + { + return m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(m_partyView->GameSession); + } + } + return nullptr; +} + +PartyView^ PartyController::GetPartyView() +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + return m_partyView; +} + +bool PartyController::DoesPartyAndSessionPlayersMatch( + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session + ) +{ + PartyView^ partyView = GetPartyView(); + + // Verify that session size and party size match + if ( session->Members->Size != partyView->Members->Size ) + { + return false; + } + + bool inParty; + + // Verify that session players match current party players + for ( unsigned int i = 0; i < session->Members->Size; i++ ) + { + inParty = false; + + MultiplayerSessionMember^ member = session->Members->GetAt( i ); + + for ( PartyMember^ partyMember : partyView->Members ) + { + if ( _wcsicmp(member->XboxUserId->Data(), partyMember->XboxUserId->Data() ) == 0 ) + { + inParty = inParty || true; + } + } + + if ( !inParty ) + { + return false; + } + } + + return true; +} + +void PartyController::SetPartyView( PartyView^ partyView ) +{ + Concurrency::critical_section::scoped_lock lock(m_lock); + m_partyView = partyView; +} + +Windows::Foundation::DateTime PartyController::GetCurrentTime() +{ + ULARGE_INTEGER uInt; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uInt.LowPart = ft.dwLowDateTime; + uInt.HighPart = ft.dwHighDateTime; + + Windows::Foundation::DateTime time; + time.UniversalTime = uInt.QuadPart; + return time; +} + +double PartyController::GetTimeBetweenInSeconds(Windows::Foundation::DateTime dt1, Windows::Foundation::DateTime dt2) +{ + const uint64 tickPerSecond = 10000000i64; + uint64 deltaTime = dt2.UniversalTime - dt1.UniversalTime; + return (double)deltaTime / (double)tickPerSecond; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/PartyController.h b/Minecraft.Client/Durango/Network/PartyController.h new file mode 100644 index 0000000..621162c --- /dev/null +++ b/Minecraft.Client/Durango/Network/PartyController.h @@ -0,0 +1,74 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved +#pragma once + +#include +class DQRNetworkManager; + +class PartyController +{ +public: + PartyController(DQRNetworkManager *pDQRNet); + + void SetPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView ); + Windows::Xbox::Multiplayer::PartyView^ GetPartyView(); + + void RefreshPartyView(); + bool AddLocalUsersToParty(int userMask, Windows::Xbox::System::User^ primaryUser); + void RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser); + void RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser, int playerMask, Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference); + void RemoveLocalUserFromParty(Windows::Xbox::System::User^ userToRemove); + void RegisterEventHandlers(); + void UnregisterEventHandlers(); + void UnregisterGamePlayersEventHandler(); + void RegisterGamePlayersChangedEventHandler(); + bool CanJoinParty(); + bool CanInvitePartyToMyGame( Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ multiplayerSession ); + bool IsPartyInAnotherTitle(); + bool IsGameSessionReadyEventTriggered(); + bool DoesPartySessionExist(); + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference ^ GetGamePartySessionReference(); + void ClearGameSessionReadyEventTriggered(); + int GetActiveAndReservedMemberPartySize(); + bool DoesPartyAndSessionPlayersMatch( + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session + ); + void CheckPartySessionFull(Windows::Xbox::System::User^ primaryUser); + void SetJoinability(bool isJoinable); + void DisassociateSessionFromParty( Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference); + +private: + Concurrency::critical_section m_lock; + bool m_isGameSessionReadyEventTriggered; + bool m_isGamePlayerEventRegistered; + DQRNetworkManager *m_pDQRNet; + + static void DebugPrintPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView ); + + void OnPartyStateChanged( Windows::Xbox::Multiplayer::PartyStateChangedEventArgs^ eventArgs ); + void OnPartyRosterChanged( Windows::Xbox::Multiplayer::PartyRosterChangedEventArgs^ eventArgs ); + void OnGamePlayersChanged( Windows::Xbox::Multiplayer::GamePlayersChangedEventArgs^ eventArgs ); + void OnGameSessionReady( Windows::Xbox::Multiplayer::GameSessionReadyEventArgs^ eventArgs ); + Windows::Xbox::Multiplayer::PartyView^ m_partyView; + Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ m_partyGameReadyRef; + void AddAvailableGamePlayers( + Windows::Foundation::Collections::IVectorView^ availablePlayers, + int& remainingSlots, + Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession + ); + + Windows::Foundation::DateTime GetCurrentTime(); + double GetTimeBetweenInSeconds(Windows::Foundation::DateTime dt1, Windows::Foundation::DateTime dt2); + + // Party/Session events. + Windows::Foundation::EventRegistrationToken m_partyRosterChangedToken; + Windows::Foundation::EventRegistrationToken m_partyStateChangedToken; + Windows::Foundation::EventRegistrationToken m_partyGamePlayersChangedToken; + Windows::Foundation::EventRegistrationToken m_partyGameSessionReadyToken; +}; + + diff --git a/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.cpp b/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.cpp new file mode 100644 index 0000000..3efba5e --- /dev/null +++ b/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.cpp @@ -0,0 +1,940 @@ +#include "stdafx.h" +#include "..\..\..\Minecraft.World\Socket.h" +#include "..\..\..\Minecraft.World\StringHelpers.h" +#include "PlatformNetworkManagerDurango.h" +#include "NetworkPlayerDurango.h" + +CPlatformNetworkManagerDurango *g_pPlatformNetworkManager; + +void CPlatformNetworkManagerDurango::HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState) +{ + static const char * c_apszStateNames[] = + { + "DNM_STATE_INITIALISING", + "DNM_STATE_INITIALISE_FAILED", + "DNM_STATE_IDLE", + "DNM_STATE_HOSTING", + "DNM_STATE_JOINING", + "DNM_STATE_STARTING", + "DNM_STATE_PLAYING", + "DNM_STATE_LEAVING", + "DNM_STATE_ENDING", + }; + + app.DebugPrintf( "Network State: %s ==> %s\n", + c_apszStateNames[ oldState ], + c_apszStateNames[ newState ] ); + + if( newState == DQRNetworkManager::DNM_STATE_HOSTING ) + { + m_bLeavingGame = false; + m_bLeaveGameOnTick = false; + m_bHostChanged = false; + g_NetworkManager.StateChange_AnyToHosting(); + } + else if( newState == DQRNetworkManager::DNM_STATE_JOINING ) + { + m_bLeavingGame = false; + m_bLeaveGameOnTick = false; + m_bHostChanged = false; + g_NetworkManager.StateChange_AnyToJoining(); + } + else if( newState == DQRNetworkManager::DNM_STATE_IDLE && oldState == DQRNetworkManager::DNM_STATE_JOINING ) + { + g_NetworkManager.StateChange_JoiningToIdle(JOIN_FAILED_NONSPECIFIC); + } + else if( newState == DQRNetworkManager::DNM_STATE_IDLE && oldState == DQRNetworkManager::DNM_STATE_HOSTING ) + { + m_bLeavingGame = true; + } + else if( newState == DQRNetworkManager::DNM_STATE_STARTING ) + { + m_lastPlayerEventTimeStart = app.getAppTime(); + + g_NetworkManager.StateChange_AnyToStarting(); + } + // Fix for #93148 - TCR 001: BAS Game Stability: Title will crash for the multiplayer client if host of the game will exit during the clients loading to created world. + // 4J Stu - If the client joins just as the host is exiting, then they can skip to leaving without passing through ending + else if( newState == DQRNetworkManager::DNM_STATE_ENDING ) + { + g_NetworkManager.StateChange_AnyToEnding( oldState == DQRNetworkManager::DNM_STATE_PLAYING ); + + if( m_pDQRNet->IsHost() ) + { + m_bLeavingGame = true; + } + } + + if( newState == DQRNetworkManager::DNM_STATE_IDLE ) + { + g_NetworkManager.StateChange_AnyToIdle(); + } +} + +void CPlatformNetworkManagerDurango::HandlePlayerJoined(DQRNetworkPlayer *pDQRPlayer) +{ + const char * pszDescription; + + // If this is a local player, we need to inform the chat system that it has joined + if( pDQRPlayer->IsLocal() ) + { + m_pDQRNet->ChatPlayerJoined(pDQRPlayer->GetLocalPlayerIndex()); + } + + // 4J Stu - We create a fake socket for every where that we need an INBOUND queue of game data. Outbound + // is all handled by QNet so we don't need that. Therefore each client player has one, and the host has one + // for each client player. + bool createFakeSocket = false; + bool localPlayer = false; + + NetworkPlayerDurango *networkPlayer = (NetworkPlayerDurango *)addNetworkPlayer(pDQRPlayer); + + // Request full display name for this player + m_pDQRNet->RequestDisplayName(pDQRPlayer); + + if( pDQRPlayer->IsLocal() ) + { + localPlayer = true; + if( pDQRPlayer->IsHost() ) + { + pszDescription = "local host"; + // 4J Stu - No socket for the localhost as it uses a special loopback queue + + m_machineDQRPrimaryPlayers.push_back( pDQRPlayer ); + } + else + { + pszDescription = "local"; + + // We need an inbound queue on all local players to receive data from the host + createFakeSocket = true; + } + } + else + { + if( pDQRPlayer->IsHost() ) + { + pszDescription = "remote host"; + } + else + { + pszDescription = "remote"; + + // If we are the host, then create a fake socket for every remote player + if( m_pDQRNet->IsHost() ) + { + createFakeSocket = true; + } + } + + if( m_pDQRNet->IsHost() && !m_bHostChanged ) + { + // Do we already have a primary player for this system? + bool systemHasPrimaryPlayer = false; + for(AUTO_VAR(it, m_machineDQRPrimaryPlayers.begin()); it < m_machineDQRPrimaryPlayers.end(); ++it) + { + DQRNetworkPlayer *pQNetPrimaryPlayer = *it; + if( pDQRPlayer->IsSameSystem(pQNetPrimaryPlayer) ) + { + systemHasPrimaryPlayer = true; + break; + } + } + if( !systemHasPrimaryPlayer ) + m_machineDQRPrimaryPlayers.push_back( pDQRPlayer ); + } + } + g_NetworkManager.PlayerJoining( networkPlayer ); + + if( createFakeSocket == true && !m_bHostChanged ) + { + g_NetworkManager.CreateSocket( networkPlayer, localPlayer ); + } + + app.DebugPrintf( "Player 0x%p \"%ls\" joined; %s; voice %i; camera %i.\n", + pDQRPlayer, + pDQRPlayer->GetGamertag(), + pszDescription, + (int) pDQRPlayer->HasVoice(), + (int) pDQRPlayer->HasCamera() ); + + + if( m_pDQRNet->IsHost() ) + { + g_NetworkManager.UpdateAndSetGameSessionData(); + SystemFlagAddPlayer( networkPlayer ); + } + + for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(playerChangedCallback[idx] != NULL) + playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, false ); + } + + if(m_pDQRNet->GetState() == QNET_STATE_GAME_PLAY) + { + int localPlayerCount = 0; + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if( m_pDQRNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount; + } + + float appTime = app.getAppTime(); + + // Only record stats for the primary player here + m_lastPlayerEventTimeStart = appTime; + } +} + +void CPlatformNetworkManagerDurango::HandlePlayerLeaving(DQRNetworkPlayer *pDQRPlayer) +{ + //__debugbreak(); + + app.DebugPrintf( "Player 0x%p leaving.\n", + pDQRPlayer ); + + INetworkPlayer *networkPlayer = getNetworkPlayer(pDQRPlayer); + + if( networkPlayer ) + { + // Get our wrapper object associated with this player. + Socket *socket = networkPlayer->GetSocket(); + if( socket != NULL ) + { + // If we are in game then remove this player from the game as well. + // We may get here either from the player requesting to exit the game, + // in which case we they will already have left the game server, or from a disconnection + // where we then have to remove them from the game server + if( m_pDQRNet->IsHost() && !m_bHostChanged ) + { + g_NetworkManager.CloseConnection(networkPlayer); + } + + // Free the wrapper object memory. + // TODO 4J Stu - We may still be using this at the point that the player leaves the session. + // We need this as long as the game server still needs to communicate with the player + //delete socket; + + networkPlayer->SetSocket( NULL ); + } + + if( m_pDQRNet->IsHost() && !m_bHostChanged ) + { + if( isSystemPrimaryPlayer(pDQRPlayer) ) + { + DQRNetworkPlayer *pNewDQRPrimaryPlayer = NULL; + for(unsigned int i = 0; i < m_pDQRNet->GetPlayerCount(); ++i ) + { + DQRNetworkPlayer *pDQRPlayer2 = m_pDQRNet->GetPlayerByIndex( i ); + + if( pDQRPlayer2 != pDQRPlayer && pDQRPlayer2->IsSameSystem( pDQRPlayer ) ) + { + pNewDQRPrimaryPlayer = pDQRPlayer2; + break; + } + } + AUTO_VAR(it, find( m_machineDQRPrimaryPlayers.begin(), m_machineDQRPrimaryPlayers.end(), pDQRPlayer)); + if( it != m_machineDQRPrimaryPlayers.end() ) + { + m_machineDQRPrimaryPlayers.erase( it ); + } + + if( pNewDQRPrimaryPlayer != NULL ) + m_machineDQRPrimaryPlayers.push_back( pNewDQRPrimaryPlayer ); + } + + g_NetworkManager.UpdateAndSetGameSessionData( networkPlayer ); + SystemFlagRemovePlayer( networkPlayer ); + + } + + g_NetworkManager.PlayerLeaving( networkPlayer ); + + for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(playerChangedCallback[idx] != NULL) + playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, true ); + } + + if(m_pDQRNet->GetState() == DQRNetworkManager::DNM_STATE_PLAYING) + { + int localPlayerCount = 0; + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if( m_pDQRNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount; + } + + float appTime = app.getAppTime(); + m_lastPlayerEventTimeStart = appTime; + } + + removeNetworkPlayer(pDQRPlayer); + } +} + +void CPlatformNetworkManagerDurango::HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize) +{ +#if 0 + // TODO - put this back in + if(m_pSQRNet->GetState() == SQRNetworkManager::SNM_STATE_ENDING) + { + return; + } +#endif + + if( playerTo->IsHost() ) + { + // If we are the host we care who this came from + //app.DebugPrintf( "Pushing data into host read queue for user \"%ls\"\n", pPlayerFrom->GetGamertag()); + // Push this data into the read queue for the player that sent it + INetworkPlayer *pPlayerFrom = getNetworkPlayer(playerFrom); + Socket *socket = pPlayerFrom->GetSocket(); + + if(socket != NULL) + socket->pushDataToQueue(data, dataSize, false); + } + else + { + // If we are not the host the message must have come from the host, so we care more about who it is addressed to + INetworkPlayer *pPlayerTo = getNetworkPlayer(playerTo); + Socket *socket = pPlayerTo->GetSocket(); + //app.DebugPrintf( "Pushing data into read queue for user \"%ls\"\n", apPlayersTo[dwPlayer]->GetGamertag()); + if(socket != NULL) + socket->pushDataToQueue(data, dataSize); + } +} + +void CPlatformNetworkManagerDurango::HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo) +{ + g_NetworkManager.GameInviteReceived(playerIndex, pInviteInfo); +} + +bool CPlatformNetworkManagerDurango::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize) +{ + m_pDQRNet = new DQRNetworkManager(this); + m_pDQRNet->Initialise(); + + m_hostGameSessionIsJoinable = false; + m_pGameNetworkManager = pGameNetworkManager; + m_flagIndexSize = flagIndexSize; + g_pPlatformNetworkManager = this; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + playerChangedCallback[ i ] = NULL; + } + + m_bLeavingGame = false; + m_bLeaveGameOnTick = false; + m_bHostChanged = false; + + m_bSearchResultsReady = false; + m_bSearchPending = false; + + m_bIsOfflineGame = false; + m_pSearchParam = NULL; + m_SessionsUpdatedCallback = NULL; + + m_searchResultsCount = 0; + m_lastSearchStartTime = 0; + + // The results that will be filled in with the current search + m_pSearchResults = NULL; + + Windows::Networking::Connectivity::NetworkInformation::NetworkStatusChanged += ref new Windows::Networking::Connectivity::NetworkStatusChangedEventHandler( []( Platform::Object^ pxObject ) + { + app.DebugPrintf("NetworkStatusChanged callback\n" ); + auto internetProfile = Windows::Networking::Connectivity::NetworkInformation::GetInternetConnectionProfile(); + if (internetProfile != nullptr) + { + auto connectionLevel = internetProfile->GetNetworkConnectivityLevel(); + app.DebugPrintf("Connection level has changed to (%d)\n", connectionLevel); + + //int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad(); + //bool bConnected = (connectionLevel == Windows::Networking::Connectivity::NetworkConnectivityLevel::XboxLiveAccess)?true:false; + //if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && bConnected == false && g_NetworkManager.IsInSession() ) + //{ + // app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected); + //} + } + }); + + // Success! + return true; +} + +void CPlatformNetworkManagerDurango::Terminate() +{ +} + +int CPlatformNetworkManagerDurango::GetJoiningReadyPercentage() +{ + return 100; +} + +int CPlatformNetworkManagerDurango::CorrectErrorIDS(int IDS) +{ + return IDS; +} + +bool CPlatformNetworkManagerDurango::isSystemPrimaryPlayer(DQRNetworkPlayer *pDQRPlayer) +{ + bool playerIsSystemPrimary = false; + for(auto it = m_machineDQRPrimaryPlayers.begin(); it < m_machineDQRPrimaryPlayers.end(); ++it) + { + DQRNetworkPlayer *pDQRPrimaryPlayer = *it; + if( pDQRPrimaryPlayer == pDQRPlayer ) + { + playerIsSystemPrimary = true; + break; + } + } + return playerIsSystemPrimary; +} + +// We call this twice a frame, either side of the render call so is a good place to "tick" things +void CPlatformNetworkManagerDurango::DoWork() +{ + m_pDQRNet->Tick(); + + TickSearch(); + + if( m_bLeaveGameOnTick ) + { + m_pDQRNet->LeaveRoom(); + m_bLeaveGameOnTick = false; + } +} + +int CPlatformNetworkManagerDurango::GetPlayerCount() +{ + return m_pDQRNet->GetPlayerCount(); +} + +bool CPlatformNetworkManagerDurango::ShouldMessageForFullSession() +{ + return m_pDQRNet->ShouldMessageForFullSession(); +} + +int CPlatformNetworkManagerDurango::GetOnlinePlayerCount() +{ + return m_pDQRNet->GetOnlinePlayerCount(); +} + +int CPlatformNetworkManagerDurango::GetLocalPlayerMask(int playerIndex) +{ + return 1 << playerIndex; +} + +bool CPlatformNetworkManagerDurango::AddLocalPlayerByUserIndex( int userIndex ) +{ + return m_pDQRNet->AddLocalPlayerByUserIndex( userIndex ); +} + +bool CPlatformNetworkManagerDurango::RemoveLocalPlayerByUserIndex( int userIndex ) +{ + return m_pDQRNet->RemoveLocalPlayerByUserIndex( userIndex ); +} + +bool CPlatformNetworkManagerDurango::IsInStatsEnabledSession() +{ + return true; +} + +bool CPlatformNetworkManagerDurango::SessionHasSpace(unsigned int spaceRequired /*= 1*/) +{ + return true; +} + +void CPlatformNetworkManagerDurango::SendInviteGUI(int quadrant) +{ + m_pDQRNet->SendInviteGUI(quadrant); +} + +bool CPlatformNetworkManagerDurango::IsAddingPlayer() +{ + return m_pDQRNet->IsAddingPlayer(); +} + +bool CPlatformNetworkManagerDurango::LeaveGame(bool bMigrateHost) +{ + if( m_bLeavingGame ) return true; + + m_bLeavingGame = true; + + // If we are the host wait for the game server to end + if(m_pDQRNet->IsHost() && g_NetworkManager.ServerStoppedValid()) + { +// m_pDQRNet->EndGame(); + g_NetworkManager.ServerStoppedWait(); + g_NetworkManager.ServerStoppedDestroy(); + } + return _LeaveGame(bMigrateHost, true);; +} + +bool CPlatformNetworkManagerDurango::_LeaveGame(bool bMigrateHost, bool bLeaveRoom) +{ + m_bLeavingGame = true; + m_bLeaveGameOnTick = true; + m_migrateHostOnLeave = bMigrateHost; + return true; +} + +void CPlatformNetworkManagerDurango::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) +{ +// #ifdef _XBOX + // 4J Stu - We probably did this earlier as well, but just to be sure! + SetLocalGame( !bOnlineGame ); + SetPrivateGame( bIsPrivate ); + SystemFlagReset(); + + // Make sure that the Primary Pad is in by default + localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() ); + + m_bLeavingGame = false; + + _HostGame( localUsersMask, publicSlots, privateSlots ); +//#endif +} + +void CPlatformNetworkManagerDurango::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) +{ + memset(&m_hostGameSessionData,0,sizeof(m_hostGameSessionData)); + m_hostGameSessionData.netVersion = MINECRAFT_NET_VERSION; + m_hostGameSessionData.isReadyToJoin = false; + m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All); + m_hostGameSessionIsJoinable = !IsPrivateGame(); + + m_pDQRNet->CreateAndJoinSession(usersMask, (unsigned char *)&m_hostGameSessionData, sizeof(m_hostGameSessionData), IsLocalGame() ); +} + +bool CPlatformNetworkManagerDurango::_StartGame() +{ + return true; +} + +int CPlatformNetworkManagerDurango::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex) +{ + app.DebugPrintf("Joining game party from search result\n"); + + int joinPlayerCount = 0; + for( int i = 0; i < DQRNetworkManager::MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( localUsersMask & ( 1 << i ) ) + { + // Check if this joining user is already in the session, in which case we don't need to count it + bool isJoiningUser = false; + for( int j = 0; j < searchResult->searchResult.m_usedSlotCount; j++ ) + { + Platform::String^ xuid = ref new Platform::String(searchResult->searchResult.m_sessionXuids[j].c_str()); + if( xuid == ProfileManager.GetUser(i)->XboxUserId ) + { + app.DebugPrintf("Joining user found to be already in session, so won't be counting to our total\n"); + isJoiningUser = true; + break; + } + } + if( !isJoiningUser ) + { + joinPlayerCount++; + } + } + } + app.DebugPrintf("Used slots: %d, fully playing players: %d, trying to join %d\n", searchResult->searchResult.m_usedSlotCount, searchResult->searchResult.m_playerCount, joinPlayerCount); + GameSessionData *gameSession = (GameSessionData *)(&searchResult->data); + if( ( searchResult->searchResult.m_usedSlotCount + joinPlayerCount ) > DQRNetworkManager::MAX_ONLINE_PLAYER_COUNT ) + { + return CGameNetworkManager::JOINGAME_FAIL_SERVER_FULL; + } + + if(m_pDQRNet->JoinPartyFromSearchResult(&searchResult->searchResult, localUsersMask)) + { + app.DebugPrintf("Join success\n"); + return CGameNetworkManager::JOINGAME_SUCCESS; + } + else + { + app.DebugPrintf("Join fail\n"); + return CGameNetworkManager::JOINGAME_FAIL_GENERAL; + } +} + +void CPlatformNetworkManagerDurango::CancelJoinGame() +{ + m_pDQRNet->CancelJoinPartyFromSearchResult(); +} + +bool CPlatformNetworkManagerDurango::SetLocalGame(bool isLocal) +{ + m_bIsOfflineGame = isLocal; + + return true; +} + +void CPlatformNetworkManagerDurango::SetPrivateGame(bool isPrivate) +{ + app.DebugPrintf("Setting as private game: %s\n", isPrivate ? "yes" : "no" ); + m_bIsPrivateGame = isPrivate; +} + +void CPlatformNetworkManagerDurango::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + playerChangedCallback[iPad] = callback; + playerChangedCallbackParam[iPad] = callbackParam; +} + +void CPlatformNetworkManagerDurango::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) +{ + if(playerChangedCallbackParam[iPad] == callbackParam) + { + playerChangedCallback[iPad] = NULL; + playerChangedCallbackParam[iPad] = NULL; + } +} + +void CPlatformNetworkManagerDurango::HandleSignInChange() +{ + return; +} + +void CPlatformNetworkManagerDurango::HandleAddLocalPlayerFailed(int idx, bool serverFull) +{ + g_NetworkManager.AddLocalPlayerFailed(idx, serverFull); +} + +void CPlatformNetworkManagerDurango::HandleDisconnect(bool bLostRoomOnly) +{ + g_NetworkManager.HandleDisconnect(bLostRoomOnly); +} + +bool CPlatformNetworkManagerDurango::_RunNetworkGame() +{ + m_pDQRNet->StartGame(); + m_hostGameSessionData.isReadyToJoin = true; + m_pDQRNet->UpdateCustomSessionData(); + return true; +} + +void CPlatformNetworkManagerDurango::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/) +{ + if( this->m_bLeavingGame ) + return; + + if( GetHostPlayer() == NULL ) + return; + + m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All); + + m_pDQRNet->UpdateCustomSessionData(); +} + +int CPlatformNetworkManagerDurango::RemovePlayerOnSocketClosedThreadProc( void* lpParam ) +{ + INetworkPlayer *pNetworkPlayer = (INetworkPlayer *)lpParam; + + Socket *socket = pNetworkPlayer->GetSocket(); + + if( socket != NULL ) + { + //printf("Waiting for socket closed event\n"); + socket->m_socketClosedEvent->WaitForSignal(INFINITE); + + //printf("Socket closed event has fired\n"); + // 4J Stu - Clear our reference to this socket + pNetworkPlayer->SetSocket( NULL ); + delete socket; + } + + return g_pPlatformNetworkManager->RemoveLocalPlayer( pNetworkPlayer ); +} + +bool CPlatformNetworkManagerDurango::RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ) +{ + return true; +} + +CPlatformNetworkManagerDurango::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count) +{ + // 4J Stu - Don't assert, just make it a multiple of 8! This count is calculated from a load of separate values, + // and makes tweaking world/render sizes a pain if we hit an assert here + count = (count + 8 - 1) & ~(8 - 1); + //assert( ( count % 8 ) == 0 ); + this->m_pNetworkPlayer = pNetworkPlayer; + this->flags = new unsigned char [ count / 8 ]; + memset( this->flags, 0, count / 8 ); + this->count = count; +} + +CPlatformNetworkManagerDurango::PlayerFlags::~PlayerFlags() +{ + delete [] flags; +} + +// Add a player to the per system flag storage - if we've already got a player from that system, copy its flags over +void CPlatformNetworkManagerDurango::SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer) +{ + PlayerFlags *newPlayerFlags = new PlayerFlags( pNetworkPlayer, m_flagIndexSize); + // If any of our existing players are on the same system, then copy over flags from that one + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) + { + memcpy( newPlayerFlags->flags, m_playerFlags[i]->flags, m_playerFlags[i]->count / 8 ); + break; + } + } + m_playerFlags.push_back(newPlayerFlags); +} + +// Remove a player from the per system flag storage - just maintains the m_playerFlags vector without any gaps in it +void CPlatformNetworkManagerDurango::SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer) +{ + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) + { + delete m_playerFlags[i]; + m_playerFlags[i] = m_playerFlags.back(); + m_playerFlags.pop_back(); + return; + } + } +} + +void CPlatformNetworkManagerDurango::SystemFlagReset() +{ + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + delete m_playerFlags[i]; + } + m_playerFlags.clear(); +} + +// Set a per system flag - this is done by setting the flag on every player that shares that system +void CPlatformNetworkManagerDurango::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index) +{ + if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return; + if( pNetworkPlayer == NULL ) return; + + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) + { + m_playerFlags[i]->flags[ index / 8 ] |= ( 128 >> ( index % 8 ) ); + } + } +} + +// Get value of a per system flag - can be read from the flags of the passed in player as anything else sent to that +// system should also have been duplicated here +bool CPlatformNetworkManagerDurango::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index) +{ + if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return false; + if( pNetworkPlayer == NULL ) + { + return false; + } + + for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) + { + if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) + { + return ( ( m_playerFlags[i]->flags[ index / 8 ] & ( 128 >> ( index % 8 ) ) ) != 0 ); + } + } + return false; +} + +wstring CPlatformNetworkManagerDurango::GatherStats() +{ + return L""; +} + +wstring CPlatformNetworkManagerDurango::GatherRTTStats() +{ + return L""; +} + +void CPlatformNetworkManagerDurango::TickSearch() +{ + if( m_bSearchPending ) + { + if( !m_pDQRNet->FriendPartyManagerIsBusy() ) + { + m_searchResultsCount = m_pDQRNet->FriendPartyManagerGetCount(); + delete [] m_pSearchResults; + m_pSearchResults = new DQRNetworkManager::SessionSearchResult[m_searchResultsCount]; + + for( int i = 0; i < m_searchResultsCount; i++ ) + { + m_pDQRNet->FriendPartyManagerGetSessionInfo(i, &m_pSearchResults[i] ); + } + m_bSearchPending = false; + + if( m_SessionsUpdatedCallback != NULL ) m_SessionsUpdatedCallback(m_pSearchParam); + } + } + else + { + if( !m_pDQRNet->FriendPartyManagerIsBusy() ) + { + // Don't start searches unless we have registered a callback + if( m_SessionsUpdatedCallback != NULL && (m_lastSearchStartTime + MINECRAFT_DURANGO_PARTY_SEARCH_DELAY_MILLISECONDS) < GetTickCount() ) + { + if( m_pDQRNet->FriendPartyManagerSearch() ); + { + m_bSearchPending = true; + m_lastSearchStartTime = GetTickCount(); + } + } + } + } +} + +vector *CPlatformNetworkManagerDurango::GetSessionList(int iPad, int localPlayers, bool partyOnly) +{ + vector *filteredList = new vector(); + for( int i = 0; i < m_searchResultsCount; i++ ) + { + GameSessionData *gameSessionData = (GameSessionData *)m_pSearchResults[i].m_extData; + if( ( gameSessionData->netVersion == MINECRAFT_NET_VERSION ) && + ( gameSessionData->isReadyToJoin ) ) + { + FriendSessionInfo *session = new FriendSessionInfo(); + session->searchResult = m_pSearchResults[i]; + session->searchResult.m_extData = NULL; // We have another copy of the GameSessionData, so don't need to make another copy of this here + session->displayLabelLength = session->searchResult.m_playerNames[0].size(); + session->displayLabel = new wchar_t[ session->displayLabelLength + 1 ]; + memcpy(&session->data, gameSessionData, sizeof(GameSessionData)); + wcscpy_s(session->displayLabel, session->displayLabelLength + 1, session->searchResult.m_playerNames[0].c_str() ); + filteredList->push_back(session); + } + } + return filteredList; +} + +bool CPlatformNetworkManagerDurango::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo) +{ + return false; +} + +void CPlatformNetworkManagerDurango::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) +{ + m_SessionsUpdatedCallback = SessionsUpdatedCallback; m_pSearchParam = pSearchParam; +} + +void CPlatformNetworkManagerDurango::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) +{ + FriendSessionUpdatedFn(true, pParam); +} + +void CPlatformNetworkManagerDurango::ForceFriendsSessionRefresh() +{ + app.DebugPrintf("Resetting friends session search data\n"); + m_lastSearchStartTime = 0; + m_searchResultsCount = 0; + delete [] m_pSearchResults; + m_pSearchResults = NULL; +} + +INetworkPlayer *CPlatformNetworkManagerDurango::addNetworkPlayer(DQRNetworkPlayer *pDQRPlayer) +{ + NetworkPlayerDurango *pNetworkPlayer = new NetworkPlayerDurango(pDQRPlayer); + pDQRPlayer->SetCustomDataValue((ULONG_PTR)pNetworkPlayer); + currentNetworkPlayers.push_back( pNetworkPlayer ); + return pNetworkPlayer; +} + +void CPlatformNetworkManagerDurango::removeNetworkPlayer(DQRNetworkPlayer *pDQRPlayer) +{ + INetworkPlayer *pNetworkPlayer = getNetworkPlayer(pDQRPlayer); + for( AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++ ) + { + if( *it == pNetworkPlayer ) + { + currentNetworkPlayers.erase(it); + return; + } + } +} + +INetworkPlayer *CPlatformNetworkManagerDurango::getNetworkPlayer(DQRNetworkPlayer *pDQRPlayer) +{ + return pDQRPlayer ? (INetworkPlayer *)(pDQRPlayer->GetCustomDataValue()) : NULL; +} + + +INetworkPlayer *CPlatformNetworkManagerDurango::GetLocalPlayerByUserIndex(int userIndex ) +{ + return getNetworkPlayer(m_pDQRNet->GetLocalPlayerByUserIndex(userIndex)); +} + +INetworkPlayer *CPlatformNetworkManagerDurango::GetPlayerByIndex(int playerIndex) +{ + return getNetworkPlayer(m_pDQRNet->GetPlayerByIndex(playerIndex)); +} + +INetworkPlayer * CPlatformNetworkManagerDurango::GetPlayerByXuid(PlayerUID xuid) +{ + return getNetworkPlayer(m_pDQRNet->GetPlayerByXuid(xuid)) ; +} + +INetworkPlayer * CPlatformNetworkManagerDurango::GetPlayerBySmallId(unsigned char smallId) +{ + return getNetworkPlayer(m_pDQRNet->GetPlayerBySmallId(smallId)); +} + +wstring CPlatformNetworkManagerDurango::GetDisplayNameByGamertag(wstring gamertag) +{ + return m_pDQRNet->GetDisplayNameByGamertag(gamertag); +} + +INetworkPlayer *CPlatformNetworkManagerDurango::GetHostPlayer() +{ + return getNetworkPlayer(m_pDQRNet->GetHostPlayer()); +} + +bool CPlatformNetworkManagerDurango::IsHost() +{ + return m_pDQRNet->IsHost() && !m_bHostChanged; +} + +bool CPlatformNetworkManagerDurango::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo) +{ + m_pDQRNet->m_currentUserMask = userMask; + m_pDQRNet->JoinSessionFromInviteInfo(userMask); + return true; +} + +void CPlatformNetworkManagerDurango::SetSessionTexturePackParentId( int id ) +{ + m_hostGameSessionData.texturePackParentId = id; +} + +void CPlatformNetworkManagerDurango::SetSessionSubTexturePackId( int id ) +{ + m_hostGameSessionData.subTexturePackId = id; +} + +void CPlatformNetworkManagerDurango::Notify(int ID, ULONG_PTR Param) +{ +} + +bool CPlatformNetworkManagerDurango::IsInSession() +{ + return m_pDQRNet->IsInSession(); +} + +bool CPlatformNetworkManagerDurango::IsInGameplay() +{ + return m_pDQRNet->GetState() == DQRNetworkManager::DNM_STATE_PLAYING; +} + +bool CPlatformNetworkManagerDurango::IsReadyToPlayOrIdle() +{ + return m_pDQRNet->IsReadyToPlayOrIdle(); +} + +bool CPlatformNetworkManagerDurango::IsSessionJoinable() +{ + return m_hostGameSessionIsJoinable; +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.h b/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.h new file mode 100644 index 0000000..5580722 --- /dev/null +++ b/Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.h @@ -0,0 +1,168 @@ +#pragma once +using namespace std; +#include +#include "..\..\..\Minecraft.World\C4JThread.h" +#include "..\..\Common\Network\NetworkPlayerInterface.h" +#include "..\..\Common\Network\PlatformNetworkManagerInterface.h" +#include "..\..\Common\Network\SessionInfo.h" +#include "DQRNetworkManager.h" + +#define MINECRAFT_DURANGO_PARTY_SEARCH_DELAY_MILLISECONDS 30000 + +class CPlatformNetworkManagerDurango : public CPlatformNetworkManager, IDQRNetworkManagerListener +{ + friend class CGameNetworkManager; +public: + virtual bool Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize); + virtual void Terminate(); + virtual int GetJoiningReadyPercentage(); + virtual int CorrectErrorIDS(int IDS); + + virtual void DoWork(); + virtual int GetPlayerCount(); + virtual int GetOnlinePlayerCount(); + virtual int GetLocalPlayerMask(int playerIndex); + virtual bool AddLocalPlayerByUserIndex( int userIndex ); + virtual bool RemoveLocalPlayerByUserIndex( int userIndex ); + virtual INetworkPlayer *GetLocalPlayerByUserIndex( int userIndex ); + virtual INetworkPlayer *GetPlayerByIndex(int playerIndex); + virtual INetworkPlayer * GetPlayerByXuid(PlayerUID xuid); + virtual INetworkPlayer * GetPlayerBySmallId(unsigned char smallId); + virtual wstring GetDisplayNameByGamertag(wstring gamertag); + virtual bool ShouldMessageForFullSession(); + + virtual INetworkPlayer *GetHostPlayer(); + virtual bool IsHost(); + virtual bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo); + virtual bool LeaveGame(bool bMigrateHost); + + virtual bool IsInSession(); + virtual bool IsInGameplay(); + virtual bool IsReadyToPlayOrIdle(); + virtual bool IsInStatsEnabledSession(); + virtual bool SessionHasSpace(unsigned int spaceRequired = 1); + virtual void SendInviteGUI(int quadrant); + virtual bool IsAddingPlayer(); + + virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0); + virtual int JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex ); + virtual void CancelJoinGame(); + virtual bool SetLocalGame(bool isLocal); + virtual bool IsLocalGame() { return m_bIsOfflineGame; } + virtual void SetPrivateGame(bool isPrivate); + virtual bool IsPrivateGame() { return m_bIsPrivateGame; } + virtual bool IsLeavingGame() { return m_bLeavingGame; } + virtual void ResetLeavingGame() { m_bLeavingGame = false; } + + virtual void RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + virtual void UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam); + + virtual void HandleSignInChange(); + + virtual bool _RunNetworkGame(); + +private: + bool isSystemPrimaryPlayer(DQRNetworkPlayer *pDQRPlayer); + virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom); + virtual void _HostGame(int dwUsersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0); + virtual bool _StartGame(); + + DQRNetworkManager * m_pDQRNet; // pointer to SQRNetworkManager interface + + HANDLE m_notificationListener; + + vector m_machineDQRPrimaryPlayers; // collection of players that we deem to be the main one for that system + + bool m_bLeavingGame; + bool m_bLeaveGameOnTick; + bool m_migrateHostOnLeave; + bool m_bHostChanged; + + bool m_bIsOfflineGame; + bool m_bIsPrivateGame; + int m_flagIndexSize; + + // This is only maintained by the host, and is not valid on client machines + GameSessionData m_hostGameSessionData; + bool m_hostGameSessionIsJoinable; + CGameNetworkManager *m_pGameNetworkManager; +public: + virtual void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = NULL); + +private: + // TODO 4J Stu - Do we need to be able to have more than one of these? + void (*playerChangedCallback[XUSER_MAX_COUNT])(void *callbackParam, INetworkPlayer *pPlayer, bool leaving); + void *playerChangedCallbackParam[XUSER_MAX_COUNT]; + + static int RemovePlayerOnSocketClosedThreadProc( void* lpParam ); + virtual bool RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ); + + // Things for handling per-system flags + class PlayerFlags + { + public: + INetworkPlayer *m_pNetworkPlayer; + unsigned char *flags; + unsigned int count; + PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count); + ~PlayerFlags(); + }; + vector m_playerFlags; + void SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer); + void SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer); + void SystemFlagReset(); +public: + virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index); + virtual bool SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index); + + // For telemetry +private: + float m_lastPlayerEventTimeStart; + +public: + wstring GatherStats(); + wstring GatherRTTStats(); + +private: + vector friendsSessions[XUSER_MAX_COUNT]; + int m_searchResultsCount; + int m_lastSearchStartTime; + + // The results that will be filled in with the current search + DQRNetworkManager::SessionSearchResult *m_pSearchResults; + + int m_lastSearchPad; + bool m_bSearchResultsReady; + bool m_bSearchPending; + LPVOID m_pSearchParam; + void (*m_SessionsUpdatedCallback)(LPVOID pParam); + + void TickSearch(); + + vectorcurrentNetworkPlayers; + INetworkPlayer *addNetworkPlayer(DQRNetworkPlayer *pDQRPlayer); + void removeNetworkPlayer(DQRNetworkPlayer *pDQRPlayer); + static INetworkPlayer *getNetworkPlayer(DQRNetworkPlayer *pDQRPlayer); + + virtual void SetSessionTexturePackParentId( int id ); + virtual void SetSessionSubTexturePackId( int id ); + virtual void Notify(int ID, ULONG_PTR Param); + +public: + virtual vector *GetSessionList(int iPad, int localPlayers, bool partyOnly); + virtual bool GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession); + virtual void SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ); + virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ); + virtual void ForceFriendsSessionRefresh(); + + // ... and the new ones that have been converted to IDQRNetworkManagerListener + virtual void HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize); + virtual void HandlePlayerJoined(DQRNetworkPlayer *player); + virtual void HandlePlayerLeaving(DQRNetworkPlayer *player); + virtual void HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState); +// virtual void HandleResyncPlayerRequest(DQRNetworkPlayer **aPlayers); + virtual void HandleAddLocalPlayerFailed(int idx, bool serverFull); + virtual void HandleDisconnect(bool bLostRoomOnly); + virtual void HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo); + virtual bool IsSessionJoinable(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/base64.cpp b/Minecraft.Client/Durango/Network/base64.cpp new file mode 100644 index 0000000..c5af745 --- /dev/null +++ b/Minecraft.Client/Durango/Network/base64.cpp @@ -0,0 +1,156 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 Ren Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "stdafx.h" + +#include "base64.h" +#include + +static const std::wstring base64_chars = + L"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + L"abcdefghijklmnopqrstuvwxyz" + L"0123456789+/"; + + +static inline bool is_base64(wchar_t c) +{ + return (isalnum(c) || (c == L'+') || (c == L'/')); +} + +// 4J changed to use Platform::String +Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len) +{ + std::wstring ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) + { + char_array_3[i++] = *(chars_to_encode++); + if (i == 3) + { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(int ii = 0; (ii <4) ; ii++) + { + ret += base64_chars[char_array_4[ii]]; + } + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + { + char_array_3[j] = '\0'; + } + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + { + ret += base64_chars[char_array_4[j]]; + } + + while((i++ < 3)) + { + ret += L'='; + } + + } + + return ref new Platform::String(ret.c_str()); + +} + +void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len) +{ + int in_len = encoded_string->Length(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4]; + unsigned char char_array_3[3]; + + unsigned char *pucOut = output; + + while (in_len-- && ( encoded_string->Data()[in_] != L'=') && is_base64(encoded_string->Data()[in_])) + { + char_array_4[i++] = (unsigned char)(encoded_string->Data()[in_]); + in_++; + if (i ==4) + { + for (i = 0; i <4; i++) + { + char_array_4[i] = (unsigned char )base64_chars.find(char_array_4[i]); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + { + *pucOut++ = char_array_3[i]; + if( ( pucOut - output ) >= out_len ) return; + } + i = 0; + } + } + + if(i) + { + for (j = i; j <4; j++) + { + char_array_4[j] = 0; + } + + for (j = 0; j <4; j++) + { + char_array_4[j] = (unsigned char )base64_chars.find(char_array_4[j]); + } + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) + { + *pucOut++ = char_array_3[j]; + if( ( pucOut - output ) >= out_len ) return; + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/Network/base64.h b/Minecraft.Client/Durango/Network/base64.h new file mode 100644 index 0000000..637a083 --- /dev/null +++ b/Minecraft.Client/Durango/Network/base64.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len); +void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len); \ No newline at end of file diff --git a/Minecraft.Client/Durango/PresenceIds.h b/Minecraft.Client/Durango/PresenceIds.h new file mode 100644 index 0000000..98eea02 --- /dev/null +++ b/Minecraft.Client/Durango/PresenceIds.h @@ -0,0 +1,27 @@ +#pragma once + +// Values for CONTEXT_GAME_STATE + +#define CONTEXT_GAME_STATE_BLANK 0 +#define CONTEXT_GAME_STATE_RIDING_PIG 1 +#define CONTEXT_GAME_STATE_RIDING_MINECART 2 +#define CONTEXT_GAME_STATE_BOATING 3 +#define CONTEXT_GAME_STATE_FISHING 4 +#define CONTEXT_GAME_STATE_CRAFTING 5 +#define CONTEXT_GAME_STATE_FORGING 6 +#define CONTEXT_GAME_STATE_NETHER 7 +#define CONTEXT_GAME_STATE_CD 8 +#define CONTEXT_GAME_STATE_MAP 9 +#define CONTEXT_GAME_STATE_ENCHANTING 10 +#define CONTEXT_GAME_STATE_BREWING 11 +#define CONTEXT_GAME_STATE_ANVIL 12 +#define CONTEXT_GAME_STATE_TRADING 13 + +// Values for X_CONTEXT_PRESENCE + +#define CONTEXT_PRESENCE_IDLE 0 +#define CONTEXT_PRESENCE_MENUS 1 +#define CONTEXT_PRESENCE_MULTIPLAYER 2 +#define CONTEXT_PRESENCE_MULTIPLAYEROFFLINE 3 +#define CONTEXT_PRESENCE_MULTIPLAYER_1P 4 +#define CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE 5 \ No newline at end of file diff --git a/Minecraft.Client/Durango/Resource.h b/Minecraft.Client/Durango/Resource.h new file mode 100644 index 0000000..50af4a4 --- /dev/null +++ b/Minecraft.Client/Durango/Resource.h @@ -0,0 +1,31 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by DurangoRenderTest.rc +// + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_DURANGORENDERTEST_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_MINECRAFTWINDOWS 107 +#define IDI_SMALL 108 +#define IDC_MINECRAFTWINDOWS 109 +#define IDC_MYICON 2 +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Minecraft.Client/Durango/ServiceConfig/Events-XBLA.8-149E11AEEvents.h b/Minecraft.Client/Durango/ServiceConfig/Events-XBLA.8-149E11AEEvents.h new file mode 100644 index 0000000..5fc8043 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/Events-XBLA.8-149E11AEEvents.h @@ -0,0 +1,1216 @@ + +//**********************************************************************` +//* This is an include file generated by EtwPlusTool. *` +//* *` +//* Copyright (c) Microsoft Corporation. All Rights Reserved. *` +//**********************************************************************` +#pragma once +#pragma pack(push, 16) + +#include "EtwPlus.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Field Descriptors, used in the ETX_EVENT_DESCRIPTOR array below +// +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_AchievementGet_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_AchievemntUnlocked_Fields[13] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_BanLevel_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_BlockBroken_Fields[7] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_BlockPlaced_Fields[7] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_ChestfulOfCobblestone_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_EnteredNewBiome_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_GameProgress_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Float,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_IncDistanceTravelled_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_IncTimePlayed_Fields[5] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_LeaderboardTotals_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_LevelExit_Fields[12] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_LevelResume_Fields[17] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_LevelSaveOrCheckpoint_Fields[13] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_LevelStart_Fields[16] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_McItemAcquired_Fields[14] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_McItemUsed_Fields[14] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Int32,0},{EtxFieldType_UInt64,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_MenuShown_Fields[13] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_MobInteract_Fields[5] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_MobKilled_Fields[18] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Float,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_MultiplayerRoundEnd_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Float,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_MultiplayerRoundStart_Fields[9] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_OnARail_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_Overkill_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PauseOrInactive_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayedMusicDisc_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayerDiedOrFailed_Fields[18] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayerSessionEnd_Fields[7] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayerSessionPause_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayerSessionResume_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_PlayerSessionStart_Fields[6] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_RecordMediaShareUpload_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_RichPresenceState_Fields[4] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_SkinChanged_Fields[12] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_TexturePackLoaded_Fields[13] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Boolean,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_UnbanLevel_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_UnpauseOrActive_Fields[11] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_UpsellPresented_Fields[13] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; +EXTERN_C __declspec(selectany) ETX_FIELD_DESCRIPTOR XBLA_149E11AE_UpsellResponded_Fields[14] = {{EtxFieldType_UnicodeString,0},{EtxFieldType_UnicodeString,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_GUID,0},{EtxFieldType_GUID,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0},{EtxFieldType_Int32,0}}; + +// Event name mapping +// +#define AchievementGet_value 1 +#define AchievemntUnlocked_value 2 +#define BanLevel_value 3 +#define BlockBroken_value 4 +#define BlockPlaced_value 5 +#define ChestfulOfCobblestone_value 6 +#define EnteredNewBiome_value 7 +#define GameProgress_value 8 +#define IncDistanceTravelled_value 9 +#define IncTimePlayed_value 10 +#define LeaderboardTotals_value 11 +#define LevelExit_value 12 +#define LevelResume_value 13 +#define LevelSaveOrCheckpoint_value 14 +#define LevelStart_value 15 +#define McItemAcquired_value 16 +#define McItemUsed_value 17 +#define MenuShown_value 18 +#define MobInteract_value 19 +#define MobKilled_value 20 +#define MultiplayerRoundEnd_value 21 +#define MultiplayerRoundStart_value 22 +#define OnARail_value 23 +#define Overkill_value 24 +#define PauseOrInactive_value 25 +#define PlayedMusicDisc_value 26 +#define PlayerDiedOrFailed_value 27 +#define PlayerSessionEnd_value 28 +#define PlayerSessionPause_value 29 +#define PlayerSessionResume_value 30 +#define PlayerSessionStart_value 31 +#define RecordMediaShareUpload_value 32 +#define RichPresenceState_value 33 +#define SkinChanged_value 34 +#define TexturePackLoaded_value 35 +#define UnbanLevel_value 36 +#define UnpauseOrActive_value 37 +#define UpsellPresented_value 38 +#define UpsellResponded_value 39 + +// Event Descriptor array +// +EXTERN_C __declspec(selectany) ETX_EVENT_DESCRIPTOR XBLA_149E11AEEvents[39] = { + {{ 1, 1, 0, 0, 0, 0, 0x0 }, "AchievementGet", "0.7.IGB-2.1", XBLA_149E11AE_AchievementGet_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 2, 1, 0, 0, 0, 0, 0x0 }, "AchievemntUnlocked", "0.7.IGB-2.1", XBLA_149E11AE_AchievemntUnlocked_Fields, 13, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 3, 1, 0, 0, 0, 0, 0x0 }, "BanLevel", "0.7.IGB-2.1", XBLA_149E11AE_BanLevel_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 4, 1, 0, 0, 0, 0, 0x0 }, "BlockBroken", "0.7.IGB-2.1", XBLA_149E11AE_BlockBroken_Fields, 7, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 5, 1, 0, 0, 0, 0, 0x0 }, "BlockPlaced", "0.7.IGB-2.1", XBLA_149E11AE_BlockPlaced_Fields, 7, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 6, 1, 0, 0, 0, 0, 0x0 }, "ChestfulOfCobblestone", "0.7.IGB-2.1", XBLA_149E11AE_ChestfulOfCobblestone_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 7, 2, 0, 0, 0, 0, 0x0 }, "EnteredNewBiome", "0.7.IGB-2.2", XBLA_149E11AE_EnteredNewBiome_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 8, 0, 0, 0, 0, 0, 0x0 }, "GameProgress", "0.7.IGGP-2.0", XBLA_149E11AE_GameProgress_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 9, 2, 0, 0, 0, 0, 0x0 }, "IncDistanceTravelled", "0.7.IGB-2.2", XBLA_149E11AE_IncDistanceTravelled_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 10, 1, 0, 0, 0, 0, 0x0 }, "IncTimePlayed", "0.7.IGB-2.1", XBLA_149E11AE_IncTimePlayed_Fields, 5, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 11, 1, 0, 0, 0, 0, 0x0 }, "LeaderboardTotals", "0.7.IGB-2.1", XBLA_149E11AE_LeaderboardTotals_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 12, 1, 0, 0, 0, 0, 0x0 }, "LevelExit", "0.7.IGB-2.1", XBLA_149E11AE_LevelExit_Fields, 12, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 13, 1, 0, 0, 0, 0, 0x0 }, "LevelResume", "0.7.IGB-2.1", XBLA_149E11AE_LevelResume_Fields, 17, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 14, 1, 0, 0, 0, 0, 0x0 }, "LevelSaveOrCheckpoint", "0.7.IGB-2.1", XBLA_149E11AE_LevelSaveOrCheckpoint_Fields, 13, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 15, 1, 0, 0, 0, 0, 0x0 }, "LevelStart", "0.7.IGB-2.1", XBLA_149E11AE_LevelStart_Fields, 16, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 16, 1, 0, 0, 0, 0, 0x0 }, "McItemAcquired", "0.7.IGIA-2.1", XBLA_149E11AE_McItemAcquired_Fields, 14, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 17, 2, 0, 0, 0, 0, 0x0 }, "McItemUsed", "0.7.IGIU-2.2", XBLA_149E11AE_McItemUsed_Fields, 14, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 18, 1, 0, 0, 0, 0, 0x0 }, "MenuShown", "0.7.IGB-2.1", XBLA_149E11AE_MenuShown_Fields, 13, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 19, 2, 0, 0, 0, 0, 0x0 }, "MobInteract", "0.7.IGB-2.2", XBLA_149E11AE_MobInteract_Fields, 5, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 20, 3, 0, 0, 0, 0, 0x0 }, "MobKilled", "0.7.IGED-2.3", XBLA_149E11AE_MobKilled_Fields, 18, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 21, 0, 0, 0, 0, 0, 0x0 }, "MultiplayerRoundEnd", "0.7.IGMRE-2.0", XBLA_149E11AE_MultiplayerRoundEnd_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 22, 0, 0, 0, 0, 0, 0x0 }, "MultiplayerRoundStart", "0.7.IGMRS-2.0", XBLA_149E11AE_MultiplayerRoundStart_Fields, 9, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 23, 1, 0, 0, 0, 0, 0x0 }, "OnARail", "0.7.IGB-2.1", XBLA_149E11AE_OnARail_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 24, 1, 0, 0, 0, 0, 0x0 }, "Overkill", "0.7.IGB-2.1", XBLA_149E11AE_Overkill_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 25, 1, 0, 0, 0, 0, 0x0 }, "PauseOrInactive", "0.7.IGB-2.1", XBLA_149E11AE_PauseOrInactive_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 26, 1, 0, 0, 0, 0, 0x0 }, "PlayedMusicDisc", "0.7.IGB-2.1", XBLA_149E11AE_PlayedMusicDisc_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 27, 1, 0, 0, 0, 0, 0x0 }, "PlayerDiedOrFailed", "0.7.IGB-2.1", XBLA_149E11AE_PlayerDiedOrFailed_Fields, 18, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 28, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionEnd", "0.7.IGPSE-2.0", XBLA_149E11AE_PlayerSessionEnd_Fields, 7, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 29, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionPause", "0.7.IGPSPA-2.0", XBLA_149E11AE_PlayerSessionPause_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 30, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionResume", "0.7.IGPSR-2.0", XBLA_149E11AE_PlayerSessionResume_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 31, 0, 0, 0, 0, 0, 0x0 }, "PlayerSessionStart", "0.7.IGPSS-2.0", XBLA_149E11AE_PlayerSessionStart_Fields, 6, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 32, 1, 0, 0, 0, 0, 0x0 }, "RecordMediaShareUpload", "0.7.IGB-2.1", XBLA_149E11AE_RecordMediaShareUpload_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 33, 1, 0, 0, 0, 0, 0x0 }, "RichPresenceState", "0.7.IGB-2.1", XBLA_149E11AE_RichPresenceState_Fields, 4, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 34, 1, 0, 0, 0, 0, 0x0 }, "SkinChanged", "0.7.IGB-2.1", XBLA_149E11AE_SkinChanged_Fields, 12, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 35, 1, 0, 0, 0, 0, 0x0 }, "TexturePackLoaded", "0.7.IGB-2.1", XBLA_149E11AE_TexturePackLoaded_Fields, 13, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 36, 1, 0, 0, 0, 0, 0x0 }, "UnbanLevel", "0.7.IGB-2.1", XBLA_149E11AE_UnbanLevel_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 37, 1, 0, 0, 0, 0, 0x0 }, "UnpauseOrActive", "0.7.IGB-2.1", XBLA_149E11AE_UnpauseOrActive_Fields, 11, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 38, 1, 0, 0, 0, 0, 0x0 }, "UpsellPresented", "0.7.IGB-2.1", XBLA_149E11AE_UpsellPresented_Fields, 13, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }, + {{ 39, 1, 0, 0, 0, 0, 0x0 }, "UpsellResponded", "0.7.IGB-2.1", XBLA_149E11AE_UpsellResponded_Fields, 14, 0, EtxEventEnabledState_Undefined, EtxEventEnabledState_ProviderDefault, EtxPopulationSample_Undefined, EtxPopulationSample_UseProviderPopulationSample, EtxEventLatency_Undefined, EtxEventLatency_ProviderDefault, EtxEventPriority_Undefined, EtxEventPriority_ProviderDefault }}; + +// Provider Descriptor for XBLA_149E11AE +// +EXTERN_C __declspec(selectany) ETX_PROVIDER_DESCRIPTOR XBLA_149E11AEProvider = {"XBLA_149E11AE", {0xee9ef54b,0xfe67,0x4a89,{0x80,0xa8,0x52,0xcc,0xa1,0x6b,0xe7,0x84}}, 39, (ETX_EVENT_DESCRIPTOR*)&XBLA_149E11AEEvents, 0, EtxProviderEnabledState_Undefined, EtxProviderEnabledState_OnByDefault, 0, 100, EtxProviderLatency_Undefined, EtxProviderLatency_RealTime, EtxProviderPriority_Undefined, EtxProviderPriority_Critical}; + +// ETW handle for XBLA_149E11AE +// +EXTERN_C __declspec(selectany) REGHANDLE XBLA_149E11AEHandle = (REGHANDLE)0; + +/*++ + +Routine Description: + + Register the provider with ETW+. + +Arguments: + + None + +Remarks: + + ERROR_SUCCESS if success or if the provider was already registered. + Otherwise, an error code. + +--*/ +#define EventRegisterXBLA_149E11AE() EtxRegister(&XBLA_149E11AEProvider, &XBLA_149E11AEHandle) + +/*++ + +Routine Description: + + Unregister the provider from ETW+. + +Arguments: + None +Remarks: + ERROR_SUCCESS if success or if the provider was not registered. + Otherwise, an error code. +--*/ +#define EventUnregisterXBLA_149E11AE() EtxUnregister(&XBLA_149E11AEProvider, &XBLA_149E11AEHandle) + +#define EventEnabledAchievementGet() (TRUE) + +// Entry point to log the event AchievementGet +// +__inline +ULONG +EventWriteAchievementGet(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int AchievementId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_AchievementGet 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_AchievementGet]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &AchievementId, sizeof(AchievementId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[0], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_AchievementGet, EventData); +} +#define EventEnabledAchievemntUnlocked() (TRUE) + +// Entry point to log the event AchievemntUnlocked +// +__inline +ULONG +EventWriteAchievemntUnlocked(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int AchievementId, __in const signed int AchievementGamerscore) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_AchievemntUnlocked 13 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_AchievemntUnlocked]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &AchievementId, sizeof(AchievementId)); + EventDataDescCreate(&EventData[12], &AchievementGamerscore, sizeof(AchievementGamerscore)); + + return EtxEventWrite(&XBLA_149E11AEEvents[1], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_AchievemntUnlocked, EventData); +} +#define EventEnabledBanLevel() (TRUE) + +// Entry point to log the event BanLevel +// +__inline +ULONG +EventWriteBanLevel(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_BanLevel 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_BanLevel]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[2], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_BanLevel, EventData); +} +#define EventEnabledBlockBroken() (TRUE) + +// Entry point to log the event BlockBroken +// +__inline +ULONG +EventWriteBlockBroken(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DifficultyLevelId, __in const signed int BlockId, __in const signed int BlockAux, __in const unsigned __int64 BlockCount) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_BlockBroken 7 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_BlockBroken]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[4], &BlockId, sizeof(BlockId)); + EventDataDescCreate(&EventData[5], &BlockAux, sizeof(BlockAux)); + EventDataDescCreate(&EventData[6], &BlockCount, sizeof(BlockCount)); + + return EtxEventWrite(&XBLA_149E11AEEvents[3], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_BlockBroken, EventData); +} +#define EventEnabledBlockPlaced() (TRUE) + +// Entry point to log the event BlockPlaced +// +__inline +ULONG +EventWriteBlockPlaced(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DifficultyLevelId, __in const signed int BlockId, __in const signed int BlockAux, __in const unsigned __int64 BlockCount) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_BlockPlaced 7 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_BlockPlaced]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[4], &BlockId, sizeof(BlockId)); + EventDataDescCreate(&EventData[5], &BlockAux, sizeof(BlockAux)); + EventDataDescCreate(&EventData[6], &BlockCount, sizeof(BlockCount)); + + return EtxEventWrite(&XBLA_149E11AEEvents[4], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_BlockPlaced, EventData); +} +#define EventEnabledChestfulOfCobblestone() (TRUE) + +// Entry point to log the event ChestfulOfCobblestone +// +__inline +ULONG +EventWriteChestfulOfCobblestone(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int Cobblecount) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_ChestfulOfCobblestone 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_ChestfulOfCobblestone]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &Cobblecount, sizeof(Cobblecount)); + + return EtxEventWrite(&XBLA_149E11AEEvents[5], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_ChestfulOfCobblestone, EventData); +} +#define EventEnabledEnteredNewBiome() (TRUE) + +// Entry point to log the event EnteredNewBiome +// +__inline +ULONG +EventWriteEnteredNewBiome(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int BiomeId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_EnteredNewBiome 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_EnteredNewBiome]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &BiomeId, sizeof(BiomeId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[6], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_EnteredNewBiome, EventData); +} +#define EventEnabledGameProgress() (TRUE) + +// Entry point to log the event GameProgress +// +__inline +ULONG +EventWriteGameProgress(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const float CompletionPercent) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_GameProgress 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_GameProgress]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &CompletionPercent, sizeof(CompletionPercent)); + + return EtxEventWrite(&XBLA_149E11AEEvents[7], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_GameProgress, EventData); +} +#define EventEnabledIncDistanceTravelled() (TRUE) + +// Entry point to log the event IncDistanceTravelled +// +__inline +ULONG +EventWriteIncDistanceTravelled(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DifficultyLevelId, __in const unsigned __int64 Distance, __in const signed int TravelMethodId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_IncDistanceTravelled 6 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_IncDistanceTravelled]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[4], &Distance, sizeof(Distance)); + EventDataDescCreate(&EventData[5], &TravelMethodId, sizeof(TravelMethodId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[8], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_IncDistanceTravelled, EventData); +} +#define EventEnabledIncTimePlayed() (TRUE) + +// Entry point to log the event IncTimePlayed +// +__inline +ULONG +EventWriteIncTimePlayed(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DifficultyLevelId, __in const unsigned __int64 TimePlayed) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_IncTimePlayed 5 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_IncTimePlayed]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[4], &TimePlayed, sizeof(TimePlayed)); + + return EtxEventWrite(&XBLA_149E11AEEvents[9], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_IncTimePlayed, EventData); +} +#define EventEnabledLeaderboardTotals() (TRUE) + +// Entry point to log the event LeaderboardTotals +// +__inline +ULONG +EventWriteLeaderboardTotals(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DifficultyLevelId, __in const signed int LeaderboardId, __in const signed int Count) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_LeaderboardTotals 6 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_LeaderboardTotals]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[4], &LeaderboardId, sizeof(LeaderboardId)); + EventDataDescCreate(&EventData[5], &Count, sizeof(Count)); + + return EtxEventWrite(&XBLA_149E11AEEvents[10], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_LeaderboardTotals, EventData); +} +#define EventEnabledLevelExit() (TRUE) + +// Entry point to log the event LevelExit +// +__inline +ULONG +EventWriteLevelExit(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID MultiplayerCorrelationId, __in const signed int LevelExitStatus, __in LPCGUID PlayerSession) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_LevelExit 12 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_LevelExit]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[10], &LevelExitStatus, sizeof(LevelExitStatus)); + EventDataDescCreate(&EventData[11], PlayerSession, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[11], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_LevelExit, EventData); +} +#define EventEnabledLevelResume() (TRUE) + +// Entry point to log the event LevelResume +// +__inline +ULONG +EventWriteLevelResume(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int FriendsOrMatch, __in const signed int CompeteOrCoop, __in const signed int DifficultyId, __in const signed int NumberOfLocalPlayers, __in const signed int NumberOfOnlinePlayers, __in const signed int SaveOrCheckpointId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_LevelResume 17 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_LevelResume]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &FriendsOrMatch, sizeof(FriendsOrMatch)); + EventDataDescCreate(&EventData[12], &CompeteOrCoop, sizeof(CompeteOrCoop)); + EventDataDescCreate(&EventData[13], &DifficultyId, sizeof(DifficultyId)); + EventDataDescCreate(&EventData[14], &NumberOfLocalPlayers, sizeof(NumberOfLocalPlayers)); + EventDataDescCreate(&EventData[15], &NumberOfOnlinePlayers, sizeof(NumberOfOnlinePlayers)); + EventDataDescCreate(&EventData[16], &SaveOrCheckpointId, sizeof(SaveOrCheckpointId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[12], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_LevelResume, EventData); +} +#define EventEnabledLevelSaveOrCheckpoint() (TRUE) + +// Entry point to log the event LevelSaveOrCheckpoint +// +__inline +ULONG +EventWriteLevelSaveOrCheckpoint(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int SaveOrCheckpointId, __in const signed int SaveSizeInBytes) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_LevelSaveOrCheckpoint 13 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_LevelSaveOrCheckpoint]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &SaveOrCheckpointId, sizeof(SaveOrCheckpointId)); + EventDataDescCreate(&EventData[12], &SaveSizeInBytes, sizeof(SaveSizeInBytes)); + + return EtxEventWrite(&XBLA_149E11AEEvents[13], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_LevelSaveOrCheckpoint, EventData); +} +#define EventEnabledLevelStart() (TRUE) + +// Entry point to log the event LevelStart +// +__inline +ULONG +EventWriteLevelStart(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID MultiplayerCorrelationId, __in const signed int FriendsOrMatch, __in const signed int CompeteOrCoop, __in const signed int DifficultyId, __in const signed int NumberOfLocalPlayers, __in const signed int NumberOfOnlinePlayers, __in LPCGUID PlayerSession) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_LevelStart 16 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_LevelStart]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[10], &FriendsOrMatch, sizeof(FriendsOrMatch)); + EventDataDescCreate(&EventData[11], &CompeteOrCoop, sizeof(CompeteOrCoop)); + EventDataDescCreate(&EventData[12], &DifficultyId, sizeof(DifficultyId)); + EventDataDescCreate(&EventData[13], &NumberOfLocalPlayers, sizeof(NumberOfLocalPlayers)); + EventDataDescCreate(&EventData[14], &NumberOfOnlinePlayers, sizeof(NumberOfOnlinePlayers)); + EventDataDescCreate(&EventData[15], PlayerSession, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[14], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_LevelStart, EventData); +} +#define EventEnabledMcItemAcquired() (TRUE) + +// Entry point to log the event McItemAcquired +// +__inline +ULONG +EventWriteMcItemAcquired(__in_opt PCWSTR UserId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId, __in const signed int ItemId, __in const signed int AcquisitionMethodId, __in const float LocationX, __in const float LocationY, __in const float LocationZ, __in const signed int ItemAux, __in const unsigned __int64 ItemCount) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_McItemAcquired 14 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_McItemAcquired]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], &SectionId, sizeof(SectionId)); + EventDataDescCreate(&EventData[3], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[4], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[5], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[6], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[7], &ItemId, sizeof(ItemId)); + EventDataDescCreate(&EventData[8], &AcquisitionMethodId, sizeof(AcquisitionMethodId)); + EventDataDescCreate(&EventData[9], &LocationX, sizeof(LocationX)); + EventDataDescCreate(&EventData[10], &LocationY, sizeof(LocationY)); + EventDataDescCreate(&EventData[11], &LocationZ, sizeof(LocationZ)); + EventDataDescCreate(&EventData[12], &ItemAux, sizeof(ItemAux)); + EventDataDescCreate(&EventData[13], &ItemCount, sizeof(ItemCount)); + + return EtxEventWrite(&XBLA_149E11AEEvents[15], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_McItemAcquired, EventData); +} +#define EventEnabledMcItemUsed() (TRUE) + +// Entry point to log the event McItemUsed +// +__inline +ULONG +EventWriteMcItemUsed(__in_opt PCWSTR UserId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId, __in const signed int ItemId, __in const float LocationX, __in const float LocationY, __in const float LocationZ, __in const signed int ItemAux, __in const unsigned __int64 ItemCount, __in const signed int Hunger) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_McItemUsed 14 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_McItemUsed]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], &SectionId, sizeof(SectionId)); + EventDataDescCreate(&EventData[3], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[4], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[5], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[6], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[7], &ItemId, sizeof(ItemId)); + EventDataDescCreate(&EventData[8], &LocationX, sizeof(LocationX)); + EventDataDescCreate(&EventData[9], &LocationY, sizeof(LocationY)); + EventDataDescCreate(&EventData[10], &LocationZ, sizeof(LocationZ)); + EventDataDescCreate(&EventData[11], &ItemAux, sizeof(ItemAux)); + EventDataDescCreate(&EventData[12], &ItemCount, sizeof(ItemCount)); + EventDataDescCreate(&EventData[13], &Hunger, sizeof(Hunger)); + + return EtxEventWrite(&XBLA_149E11AEEvents[16], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_McItemUsed, EventData); +} +#define EventEnabledMenuShown() (TRUE) + +// Entry point to log the event MenuShown +// +__inline +ULONG +EventWriteMenuShown(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int MenuId, __in const signed int SubMenuId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_MenuShown 13 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_MenuShown]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &MenuId, sizeof(MenuId)); + EventDataDescCreate(&EventData[12], &SubMenuId, sizeof(SubMenuId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[17], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_MenuShown, EventData); +} +#define EventEnabledMobInteract() (TRUE) + +// Entry point to log the event MobInteract +// +__inline +ULONG +EventWriteMobInteract(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int MobId, __in const signed int InteractionId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_MobInteract 5 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_MobInteract]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &MobId, sizeof(MobId)); + EventDataDescCreate(&EventData[4], &InteractionId, sizeof(InteractionId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[18], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_MobInteract, EventData); +} +#define EventEnabledMobKilled() (TRUE) + +// Entry point to log the event MobKilled +// +__inline +ULONG +EventWriteMobKilled(__in_opt PCWSTR UserId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId, __in LPCGUID RoundId, __in const signed int PlayerRoleId, __in const signed int PlayerWeaponId, __in const signed int EnemyRoleId, __in const signed int KillTypeId, __in const float LocationX, __in const float LocationY, __in const float LocationZ, __in const signed int EnemyWeaponId, __in const signed int Distance, __in const signed int MobId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_MobKilled 18 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_MobKilled]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], &SectionId, sizeof(SectionId)); + EventDataDescCreate(&EventData[3], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[4], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[5], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[6], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[7], RoundId, sizeof(GUID)); + EventDataDescCreate(&EventData[8], &PlayerRoleId, sizeof(PlayerRoleId)); + EventDataDescCreate(&EventData[9], &PlayerWeaponId, sizeof(PlayerWeaponId)); + EventDataDescCreate(&EventData[10], &EnemyRoleId, sizeof(EnemyRoleId)); + EventDataDescCreate(&EventData[11], &KillTypeId, sizeof(KillTypeId)); + EventDataDescCreate(&EventData[12], &LocationX, sizeof(LocationX)); + EventDataDescCreate(&EventData[13], &LocationY, sizeof(LocationY)); + EventDataDescCreate(&EventData[14], &LocationZ, sizeof(LocationZ)); + EventDataDescCreate(&EventData[15], &EnemyWeaponId, sizeof(EnemyWeaponId)); + EventDataDescCreate(&EventData[16], &Distance, sizeof(Distance)); + EventDataDescCreate(&EventData[17], &MobId, sizeof(MobId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[19], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_MobKilled, EventData); +} +#define EventEnabledMultiplayerRoundEnd() (TRUE) + +// Entry point to log the event MultiplayerRoundEnd +// +__inline +ULONG +EventWriteMultiplayerRoundEnd(__in_opt PCWSTR UserId, __in LPCGUID RoundId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int MatchTypeId, __in const signed int DifficultyLevelId, __in const float TimeInSeconds, __in const signed int ExitStatusId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundEnd 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundEnd]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], RoundId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SectionId, sizeof(SectionId)); + EventDataDescCreate(&EventData[4], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[5], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[6], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[7], &MatchTypeId, sizeof(MatchTypeId)); + EventDataDescCreate(&EventData[8], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[9], &TimeInSeconds, sizeof(TimeInSeconds)); + EventDataDescCreate(&EventData[10], &ExitStatusId, sizeof(ExitStatusId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[20], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundEnd, EventData); +} +#define EventEnabledMultiplayerRoundStart() (TRUE) + +// Entry point to log the event MultiplayerRoundStart +// +__inline +ULONG +EventWriteMultiplayerRoundStart(__in_opt PCWSTR UserId, __in LPCGUID RoundId, __in const signed int SectionId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int MatchTypeId, __in const signed int DifficultyLevelId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundStart 9 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundStart]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], RoundId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SectionId, sizeof(SectionId)); + EventDataDescCreate(&EventData[4], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[5], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[6], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[7], &MatchTypeId, sizeof(MatchTypeId)); + EventDataDescCreate(&EventData[8], &DifficultyLevelId, sizeof(DifficultyLevelId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[21], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_MultiplayerRoundStart, EventData); +} +#define EventEnabledOnARail() (TRUE) + +// Entry point to log the event OnARail +// +__inline +ULONG +EventWriteOnARail(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int Distance) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_OnARail 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_OnARail]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &Distance, sizeof(Distance)); + + return EtxEventWrite(&XBLA_149E11AEEvents[22], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_OnARail, EventData); +} +#define EventEnabledOverkill() (TRUE) + +// Entry point to log the event Overkill +// +__inline +ULONG +EventWriteOverkill(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int Damage) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_Overkill 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_Overkill]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &Damage, sizeof(Damage)); + + return EtxEventWrite(&XBLA_149E11AEEvents[23], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_Overkill, EventData); +} +#define EventEnabledPauseOrInactive() (TRUE) + +// Entry point to log the event PauseOrInactive +// +__inline +ULONG +EventWritePauseOrInactive(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PauseOrInactive 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PauseOrInactive]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[24], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PauseOrInactive, EventData); +} +#define EventEnabledPlayedMusicDisc() (TRUE) + +// Entry point to log the event PlayedMusicDisc +// +__inline +ULONG +EventWritePlayedMusicDisc(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int DiscId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayedMusicDisc 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayedMusicDisc]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &DiscId, sizeof(DiscId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[25], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayedMusicDisc, EventData); +} +#define EventEnabledPlayerDiedOrFailed() (TRUE) + +// Entry point to log the event PlayerDiedOrFailed +// +__inline +ULONG +EventWritePlayerDiedOrFailed(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int LowResMapX, __in const signed int LowResMapY, __in const signed int LowResMapZ, __in const signed int MapId, __in const signed int PlayerWeaponId, __in const signed int EnemyWeaponId, __in const signed int EnemyTypeId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayerDiedOrFailed 18 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayerDiedOrFailed]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &LowResMapX, sizeof(LowResMapX)); + EventDataDescCreate(&EventData[12], &LowResMapY, sizeof(LowResMapY)); + EventDataDescCreate(&EventData[13], &LowResMapZ, sizeof(LowResMapZ)); + EventDataDescCreate(&EventData[14], &MapId, sizeof(MapId)); + EventDataDescCreate(&EventData[15], &PlayerWeaponId, sizeof(PlayerWeaponId)); + EventDataDescCreate(&EventData[16], &EnemyWeaponId, sizeof(EnemyWeaponId)); + EventDataDescCreate(&EventData[17], &EnemyTypeId, sizeof(EnemyTypeId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[26], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayerDiedOrFailed, EventData); +} +#define EventEnabledPlayerSessionEnd() (TRUE) + +// Entry point to log the event PlayerSessionEnd +// +__inline +ULONG +EventWritePlayerSessionEnd(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId, __in const signed int ExitStatusId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionEnd 7 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionEnd]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId)); + EventDataDescCreate(&EventData[6], &ExitStatusId, sizeof(ExitStatusId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[27], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionEnd, EventData); +} +#define EventEnabledPlayerSessionPause() (TRUE) + +// Entry point to log the event PlayerSessionPause +// +__inline +ULONG +EventWritePlayerSessionPause(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionPause 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionPause]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + + return EtxEventWrite(&XBLA_149E11AEEvents[28], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionPause, EventData); +} +#define EventEnabledPlayerSessionResume() (TRUE) + +// Entry point to log the event PlayerSessionResume +// +__inline +ULONG +EventWritePlayerSessionResume(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionResume 6 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionResume]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[29], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionResume, EventData); +} +#define EventEnabledPlayerSessionStart() (TRUE) + +// Entry point to log the event PlayerSessionStart +// +__inline +ULONG +EventWritePlayerSessionStart(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in_opt PCWSTR MultiplayerCorrelationId, __in const signed int GameplayModeId, __in const signed int DifficultyLevelId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionStart 6 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionStart]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], (MultiplayerCorrelationId != NULL) ? MultiplayerCorrelationId : L"", (MultiplayerCorrelationId != NULL) ? (ULONG)((wcslen(MultiplayerCorrelationId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[4], &GameplayModeId, sizeof(GameplayModeId)); + EventDataDescCreate(&EventData[5], &DifficultyLevelId, sizeof(DifficultyLevelId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[30], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_PlayerSessionStart, EventData); +} +#define EventEnabledRecordMediaShareUpload() (TRUE) + +// Entry point to log the event RecordMediaShareUpload +// +__inline +ULONG +EventWriteRecordMediaShareUpload(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_RecordMediaShareUpload 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_RecordMediaShareUpload]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[31], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_RecordMediaShareUpload, EventData); +} +#define EventEnabledRichPresenceState() (TRUE) + +// Entry point to log the event RichPresenceState +// +__inline +ULONG +EventWriteRichPresenceState(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int ContextID) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_RichPresenceState 4 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_RichPresenceState]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &ContextID, sizeof(ContextID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[32], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_RichPresenceState, EventData); +} +#define EventEnabledSkinChanged() (TRUE) + +// Entry point to log the event SkinChanged +// +__inline +ULONG +EventWriteSkinChanged(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int SkinId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_SkinChanged 12 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_SkinChanged]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &SkinId, sizeof(SkinId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[33], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_SkinChanged, EventData); +} +#define EventEnabledTexturePackLoaded() (TRUE) + +// Entry point to log the event TexturePackLoaded +// +__inline +ULONG +EventWriteTexturePackLoaded(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int TexturePackId, __in const BOOL Purchased) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_TexturePackLoaded 13 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_TexturePackLoaded]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &TexturePackId, sizeof(TexturePackId)); + EventDataDescCreate(&EventData[12], &Purchased, sizeof(Purchased)); + + return EtxEventWrite(&XBLA_149E11AEEvents[34], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_TexturePackLoaded, EventData); +} +#define EventEnabledUnbanLevel() (TRUE) + +// Entry point to log the event UnbanLevel +// +__inline +ULONG +EventWriteUnbanLevel(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_UnbanLevel 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_UnbanLevel]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[35], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_UnbanLevel, EventData); +} +#define EventEnabledUnpauseOrActive() (TRUE) + +// Entry point to log the event UnpauseOrActive +// +__inline +ULONG +EventWriteUnpauseOrActive(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_UnpauseOrActive 11 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_UnpauseOrActive]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + + return EtxEventWrite(&XBLA_149E11AEEvents[36], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_UnpauseOrActive, EventData); +} +#define EventEnabledUpsellPresented() (TRUE) + +// Entry point to log the event UpsellPresented +// +__inline +ULONG +EventWriteUpsellPresented(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int UpsellId, __in const signed int MarketplaceOfferId) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_UpsellPresented 13 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_UpsellPresented]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &UpsellId, sizeof(UpsellId)); + EventDataDescCreate(&EventData[12], &MarketplaceOfferId, sizeof(MarketplaceOfferId)); + + return EtxEventWrite(&XBLA_149E11AEEvents[37], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_UpsellPresented, EventData); +} +#define EventEnabledUpsellResponded() (TRUE) + +// Entry point to log the event UpsellResponded +// +__inline +ULONG +EventWriteUpsellResponded(__in_opt PCWSTR UserId, __in LPCGUID PlayerSessionId, __in const signed int SecondsSinceInitialize, __in const signed int Mode, __in const signed int SubMode, __in const signed int LevelId, __in const signed int SubLevelId, __in const signed int LeveInstanceId, __in LPCGUID PlayerSession, __in LPCGUID MultiplayerCorrelationId, __in const signed int UpsellId, __in const signed int MarketplaceOfferId, __in const signed int UpsellOutcome) +{ +#define ARGUMENT_COUNT_XBLA_149E11AE_UpsellResponded 14 + + EVENT_DATA_DESCRIPTOR EventData[ARGUMENT_COUNT_XBLA_149E11AE_UpsellResponded]; + UINT8 scratch[64]; + + EtxFillCommonFields_v7(&EventData[0], scratch, 64); + + EventDataDescCreate(&EventData[1], (UserId != NULL) ? UserId : L"", (UserId != NULL) ? (ULONG)((wcslen(UserId) + 1) * sizeof(WCHAR)) : (ULONG)sizeof(L"")); + EventDataDescCreate(&EventData[2], PlayerSessionId, sizeof(GUID)); + EventDataDescCreate(&EventData[3], &SecondsSinceInitialize, sizeof(SecondsSinceInitialize)); + EventDataDescCreate(&EventData[4], &Mode, sizeof(Mode)); + EventDataDescCreate(&EventData[5], &SubMode, sizeof(SubMode)); + EventDataDescCreate(&EventData[6], &LevelId, sizeof(LevelId)); + EventDataDescCreate(&EventData[7], &SubLevelId, sizeof(SubLevelId)); + EventDataDescCreate(&EventData[8], &LeveInstanceId, sizeof(LeveInstanceId)); + EventDataDescCreate(&EventData[9], PlayerSession, sizeof(GUID)); + EventDataDescCreate(&EventData[10], MultiplayerCorrelationId, sizeof(GUID)); + EventDataDescCreate(&EventData[11], &UpsellId, sizeof(UpsellId)); + EventDataDescCreate(&EventData[12], &MarketplaceOfferId, sizeof(MarketplaceOfferId)); + EventDataDescCreate(&EventData[13], &UpsellOutcome, sizeof(UpsellOutcome)); + + return EtxEventWrite(&XBLA_149E11AEEvents[38], &XBLA_149E11AEProvider, XBLA_149E11AEHandle, ARGUMENT_COUNT_XBLA_149E11AE_UpsellResponded, EventData); +} +#if defined(__cplusplus) +}; +#endif + +#pragma pack(pop) diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/index.html b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/index.html new file mode 100644 index 0000000..3c12ef2 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/index.html @@ -0,0 +1,184 @@ + + + + Game Help + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft is a game about placing blocks to build anything you can imagine. At night monsters come out, make sure to build a shelter before that happens. +

+ The game contains a full tutorial, so we recommend that you give that go if you are new to the game. + The game also features in-game tooltips to guide you through your initial learning experience and if you need specific help, there is a comprehensive ‘How to Play’ section, + as well as many customisable settings, all of which you can find in the Help & Options menu. + +
+
+ + +
+ +
+ Xbox One Controller + +

Default layout:

+
+ + +
+
Jump
+
Drop
+
Crafting
+
Inventory
+
Cycle Held Item
+
Cycle Held Item
+
+
+
Use
+
Action
+
Move/Sprint
+
Change Camera Mode
+
Sneak/Look
+
Players/Invite
+
+
+
+ There are alternative controller layouts available as well as Invert Look and Southpaw settings. +
+
+ + +
+ + + +
+ +
+ + +

Xbox Customer Support

+

+ For help with Xbox Live or your Xbox One Console, visit us online at xbox.com/support. For community support visit forums.xbox.com. We are also available on Twitter @XboxSupport. + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/main.css b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/main.css new file mode 100644 index 0000000..6c23af9 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/main.css @@ -0,0 +1,279 @@ +/*standard plus html5 specific resets*/ +html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, hgroup, menu, nav, section, time, mark, audio, video +{ + margin: 0; + padding: 0; + border: 0; + outline: 0; + vertical-align: baseline; + background: transparent; +} + +article, aside, figure, footer, header, hgroup, nav, section +{ + display: block; +} + +img, object, embed +{ + max-width: 100%; +} + +/*end resets*/ + +/*setting default font size so that 1 em will approximately equal 10 pixels and so that we can use media queries to scale the fonts*/ +body +{ + height:100%; + font-size: 62.5%; + overflow: hidden; +} + +button +{ + /*button is a special case and gets a default font size from the browser so we reset it to the same font-size as it's parent container*/ + font-size: 1em; +} + +a +{ + text-decoration: none; +} + +article +{ + overflow: hidden; +} + +.articles article +{ + margin-top: 0; + display: none; +} + +.contentPanel li +{ + list-style-type: none; +} + +article +{ + position: relative; +} + +.menuLink {} /* marker class for links to menu pages */ +.contentLink {} /* marker class for content pages */ + +.contentPanel +{ + width: 388px; + height: 900px; + background-color: #222222; + color: #ebebeb; + float: left; + padding: 64px 28px 64px 64px; + vertical-align: top; + font-size:2.4em; +} + +.helpContent +{ + height: 794px; + overflow: hidden; + background-color: #282828; + padding: 136px 64px 64px 64px; + -moz-column-count: auto; /* Firefox - SmartGlass and console do not need these -moz-XXXX, but if help rendered on regular desktop it could be FF */ + -webkit-column-count: auto; /* Safari and Chrome */ + column-count: auto; + -moz-column-width: 344px; + -webkit-column-width: 344px; + column-width: 344px; + -moz-column-gap: 80px; /* Firefox */ + -webkit-column-gap: 80px; /* Safari and Chrome */ + column-gap: 80px; + /*without this property the columns will always try to have the same amount of content each. + Using auto if you set the height it will only fill as needed*/ + -webkit-column-fill: auto; + -moz-column-fill: auto; + column-fill: auto; + font-size:2.4em; + line-height:1.5; + position: relative; +} + +.contentPanel .articleTitle +{ + font-family: Segoe UI Light, Segoe UI Regular,HelveticaNeue, Droid Sans, Arial, Sans-Serif; + font-weight: 100; + font-size:1.5em; + margin-top:10px; + margin-bottom:32px; +} + +.contentPanel .articleTopic +{ + font-family: Segoe UI Regular,HelveticaNeue, Droid Sans, Arial, Sans-Serif; + font-weight: normal; + /*Because we reset font family here we have to also reset the size or it will get the default body font size. 1em here means the same as it's parent*/ + font-size:1em; + +} + +.panelRule +{ + color: #ebebeb; +} + +.panelButtons +{ + /*if there are additional buttons or other elements in the panel you'll need to adjust this value*/ + margin-top: 680px; + margin-left: 28px; +} + + +/**********button state formats***********/ +.navLinks a +{ + border-bottom:solid 4px #ebebeb; + background-color: rgba(235, 235, 235, 0); + width: 432px; + height: 108px; /*height 108px is the value that includes the focused border.*/ + outline: 4px solid rgba(235, 235, 235, 0); + line-height: 2.4em; + padding: 36px 0 0 18px; +} + +.navLinks a:focus, .pageButton:focus +{ + background-color: rgba(235, 235, 235, 0.1); + outline-color: rgba(235, 235, 235, 1); + border-color:rgba(235, 235, 235, 0); +} + +.navLinks a:hover:not(:focus) +{ + background-color: transparent; + outline: solid 4px rgb(107, 107, 107); + border-bottom:solid 4px transparent; +} + +.pageButton +{ + height: 90px; + color: #ebebeb; + text-align: left; + line-height: 140px; + width: 178px; + margin: 0 10px 0 0; + -ms-attraction: 0% 0% 0% 0%; + border: 1px solid #ebebeb; + background-color: rgba(235, 235, 235, 0); + outline: 4px solid rgba(235, 235, 235, 0); +} + + .pageButton:hover:not(:focus) + { + background-color: transparent; + outline: solid 4px rgb(107, 107, 107); + border:none; + } + +.pageButton.next +{ + line-height: 46px; + float: right; +} + +.panelButtons .pageCounter +{ + display:block; + height:2em; + overflow:hidden; +} + +.pageCounter +{ + display:none; +} + +.win-voice-activelistening .pageCounter +{ + display:none; +} + + .pageButton.previous + { + float: left; + } + +.contentPanel .backLink +{ + background: transparent url(left_arrow.png) no-repeat 28px 62px; + display: block; + height: 5.1em; + width: 5.1em; + margin: -60px 0 0 -24px; + background-size: 49%; + font-size:1em; +} + +.contentPanel .backLink:hover + { + background-color: transparent; + } + +.contentPanel .backLink:focus + { + background: transparent url(left_arrow_hoverFocus.png) no-repeat 28px 62px; + background-size: 49%; + outline:0; + } + + + +.contentPanel a +{ + display: inline; + line-height: 1; + color: #ebebeb; + -ms-attraction: 0% 0% 0% 0%; +} + +.helpMenu li a +{ + color: #ebebeb; + height:4em; + width: 100%; + display: block; + line-height: 4em; + margin:0 0 0 -59px; + padding:0 25px 0 56px; + -ms-attraction: 0% 0% 0% 0%; +} + +.endMark +{ + /*this empty element is given a width so that it will stretch to fill the last column as needed*/ + visibility: hidden; + display: block; + height: 1px; + width: 100%; + break-before: column; +} + +/*note that we use absolute postitioning here instead of static so we reset the margin to 0*/ +article .contentPanel .panelButtons +{ + position: absolute; + margin: 0; + bottom: 44px; /* safe region area */ + left: 64px; /* safe region area on the left: 64px */ + width: 386px; +} + +/* The following styles are only applied when voice commands are in use on the console */ +.win-voice-activelistening[data-win-voice] +{ + color: green; +} diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/mobile.css b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/mobile.css new file mode 100644 index 0000000..ae0014f --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/mobile.css @@ -0,0 +1,160 @@ +/*modify the base font size for mobile devices*/ + body +{ + /*bumping up the font-sizes for higher res displays*/ + font-size:54%; + +} + +@media (max-width: 336px) +{ + body + { + font-size: 44%; + } + + article.helpMenu li a + { + height:2.25em; + line-height:2.25em; + } + +} + +/*for odd Android aspect ratios*/ +@media (max-width: 385px) and (min-width:337px) and (max-height:520px) +{ + body + { + font-size: 44%; + } + + article.helpMenu li a + { + height:2em; + line-height:2em; + } + +} + + +/*gradient for smartglass UI*/ +body +{ + /*gradient for smartglass UI*/ + background: -webkit-linear-gradient(top, #000000 50%, #323232); /* Safari and Chrome*/ + background: -moz-linear-gradient(top, #000000 50%, #323232);/*Firefox*/ + background: -ms-linear-gradient(top, #000000 50%, #323232);/*IE*/ +} + +/*if the element is a main menu or a sub menu hide the placeholder element to the right*/ +.helpMenu .helpContent +{ + display: none; +} + +article .helpContent +{ + height: 75%; + background-color: transparent; + padding: 0; + margin: 0; + position: absolute; + top: 5em; + left: 5%; + width: 90%; + overflow: auto; + -webkit-overflow-scrolling: touch; + line-height:1; +} + +article .contentPanel +{ + width: 96%; + height: 100%; + padding: 0px 2% 1em 2%; + overflow:hidden; +} + +article .helpContent +{ + top:7em; + height: 65%; +} + +.contentPanel .backLink +{ + background-position-x: 1em; + background-position-y: 1em; + /*need to resize the back button due the change in font size for retina displays*/ + background-size:75%; + background-position:.15em .5em; + height:2.5em; + width:2.5em; + float: left; + margin: 0px; +} + +/*note you must re-initialize any background properties if you swap images*/ +.contentPanel .backLink:focus +{ + background-position-x: 1em; + background-position-y: 1em; + background-size:75%; + background-position:.15em .5em; +} + +.articles article:not(.helpMenu) .articleTitle +{ + margin-top: 1.9em; + margin-left: .35em; + height: 2.25em; + +} + +article .contentPanel .articleTitle +{ + margin-top:.25em; + left: 2em; + display: block; + width: 70%; + max-height: 3.8em; + overflow: hidden; +} + +article .contentPanel .navLinks +{ + clear: both; +} + +.helpMenu li a +{ + height: 2.75em; + line-height: 2.75em; +} + +/*remove controller button states*/ +.navLinks a, .navLinks a:focus, .navLinks a:hover:not(:focus) +{ + + border:none; + background-color: transparent; + outline:0; + box-shadow:none; +} + + +article .contentPanel .panelButtons +{ + display: none; +} + +.pageCounter +{ + display:block; + position:fixed; + bottom:.15em; + + right:.5em; +} + diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/skin.css b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/skin.css new file mode 100644 index 0000000..0793ee1 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/skin.css @@ -0,0 +1,51 @@ + +/* This file is intended to contain rules that will allow "skinning" of your manual by changeing the colors, fonts and background images. You should not place any rules here +that modify positioning or size or other layout properties*/ + +body +{ + /*these are the standard fallbacks that are design approved for the majority of the supported devices.*/ + font-family:Segoe UI Regular,HelveticaNeue, Droid Sans, Arial, Sans-Serif; + + /*add your skin background here if needed + background:#000 url(background.png) fixed no-repeat 0 20%; + */ + + color:#ebebeb; /*xboxwhite*/ + background-color:#222222; +} + +/*this would for example override the white background and text color on active links*/ +.pageButton:hover, .contentPanel a:hover +{ + /*background-color: #ee4036; + color:#222;*/ +} + +/*and this would modify the focus outline*/ +a:focus, a:hover, button:focus, button:hover +{ + /*outline:thick double #ee4036;*/ +} + + +article a.articleTitle, article a.articleTitle:visited +{ + color:#6b6b6b; +} + +/*Here's an example of using this file to skin based upon a media query, in this case it will work on smartglass tablet devices + Note: this is a duplicate of the media query used inline in the html to load tablet.css for the index page*/ +@media (orientation:landscape) and (max-height: 900px), +(orientation:landscape) and (min-height: 1081px), +(orientation:landscape) and (max-width: 1430px) and (min-height: 901px) and (max-height: 1080px), +(orientation:landscape) and (min-width: 1930px) and (min-height: 901px) and (max-height: 1080px) +{ + /*This would add a background image to the content panel in tablets only. Note: you need an additional selector to give this rule + enough weight to override the existing in tablet.css*/ + .articles article:not(.helpMenu) .contentPanel + { + /*background: url("Forza_Left.png") no-repeat transparent 24px 60px;*/ + } +} + diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/snapped.css b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/snapped.css new file mode 100644 index 0000000..8cf81af --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/snapped.css @@ -0,0 +1,53 @@ +body +{ + width: 480px; +} + +.contentPanel .articleTitle +{ + height: 3.75em; + width: 100%; + overflow: hidden; + margin-top:12px; +} + +/*if the element is a main menu or a sub menu hide the placeholder element to the right*/ +.helpMenu .helpContent +{ + display: none; +} + +nav.contentPanel +{ + padding: 64px 64px 64px 28px; +} + +/*if in snapped we need to modify the positioning scheme to allow us to insert the contents between existing nav elements*/ +article .helpContent +{ + height: 616px; + overflow: hidden; + background-color: transparent; + padding: 0 0 0 28px; + position: absolute; + left: 0px; + top: 10em; + column-width: 424px; +} + +article .contentPanel +{ + height: 100%; +} + +article .contentPanel .panelButtons +{ + left: 28px; /* safe region area on the right side: 480 - 28 - 386 ~ 64px */ +} + +/*in snapped the safe area is on the right so these need to be different than in main.css*/ +.helpMenu li a +{ + margin:16px -22px 0 -22px; + padding:0 56px 0 22px; +} diff --git a/Minecraft.Client/Durango/ServiceConfig/HelpDocument/tablet.css b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/tablet.css new file mode 100644 index 0000000..a98addc --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/HelpDocument/tablet.css @@ -0,0 +1,117 @@ +body +{ + font-size:50%; + /*gradient for smartglass UI*/ + background: -webkit-linear-gradient(top, #000000 50%, #323232); /* Safari and Chrome*/ + background: -moz-linear-gradient(top, #000000 50%, #323232);/*Firefox*/ + background: -ms-linear-gradient(top, #000000 50%, #323232);/*IE*/ +} + +/*for smaller android tablets the line height prevents showing all 6 items*/ +@media (max-height:595px) +{ + body + { + font-size:44%; + } + + .helpMenu li a + { + height:2.5em; + line-height: 2.5em; + } +} + +article .contentPanel +{ + padding: 0 0 0 48px; + position: relative; + top: 20%; + height: 80%; + width:300px; + background-color:transparent; +} + +article:not(.helpMenu) .contentPanel +{ + background:url("sg_logo_20.png") no-repeat transparent 56px 110px; +} + +article .contentPanel .panelButtons +{ + display: none; +} + +.pageCounter +{ + display: block; + position: fixed; + right: 2em; + bottom: .5em; + left: auto; +} + +article .helpContent +{ + position: relative; + height: 70%; + top: 20%; + padding: 2% 144px 2% 64px; /* right padding set to be higher than column-gap to allow "peek" (part of next column showing up next to full column). */ + background-color: transparent; + overflow: auto; + -webkit-overflow-scrolling: touch; + line-height:1; +} + +article .contentPanel .backLink +{ + position: fixed; + top: 1em; + left: 1em; + margin: 0; + background-position-y:2.25em; +} + +/*note you must re-initialize any background properties if you swap images*/ +article .contentPanel .backLink:focus +{ + background-position-y:2.25em; +} +.articles article:not(.helpMenu) .articleTitle +{ + top: 3.8em; + left: 1.5em; + height: 2em; +} +#TOC .articleTitle +{ + left:1.75em; +} +article .contentPanel .articleTitle +{ + position: fixed; + top: 1.85em; + left: 4em; + height: 3em; + display: block; + width: 70%; + overflow: hidden; +} + +.helpMenu li a +{ + width: 100%; + display: block; + padding-bottom:.25em; +} + +/*remove controller button states*/ +.navLinks a, .navLinks a:focus, .navLinks a:hover:not(:focus) +{ + + border:none; + background-color: transparent; + outline:0; + box-shadow:none; +} + diff --git a/Minecraft.Client/Durango/ServiceConfig/MakeZips.py b/Minecraft.Client/Durango/ServiceConfig/MakeZips.py new file mode 100644 index 0000000..9b91100 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/MakeZips.py @@ -0,0 +1,62 @@ + +import os +import shutil +import zipfile + +# 4J-JEV: Takes: +# - Localisation from 'ServiceConfig\loc\ex-EX\index.html' +# - Template help from 'ServiceConfig\HelpDocument\*' +# +# Then: +# - Constructs all zipped files to 'ServiceConfig\HelpDocument_ex-EX.zip'. +# +# NOTE: Make sure to check out 'ServiceConfig\HelpDocument\*' first. + +def formatLoc(str): + [lang,local] = str.split('-') + return ( lang.lower() + "-" + local.upper() ) + +def copyTemplate(dst): + for root, dirs, files in os.walk(".\\HelpDocument\\"): + if not os.path.exists(dst+"\\"+root): + os.makedirs(dst+"\\"+root) + + for f in filter(lambda x: x!="index.html", files): + if not os.path.isdir(root+"\\"+f): + print "Copying to '%s\\%s\\%s'" % (dst,root,f) + shutil.copyfile(root+"\\"+f, dst+"\\"+root+"\\"+f) + +def createZip(name): + os.chdir(".\\"+name) + + zipname = name+".zip" + print "Created "+zipname + + z = zipfile.ZipFile(zipname, 'w') + for root, dirs, files in os.walk(".\\HelpDocument\\"): + for file in files: + print "Adding '%s\\%s'." % (root,file) + z.write(os.path.join(root,file)) + + z.close() + + shutil.move(".\\"+zipname, "..\\"+zipname) + os.chdir("..") + + + + #== MAIN ==# + +if __name__=="__main__": + for loc in map(formatLoc,os.listdir(".\\loc\\")): + tardir = ".\\HelpDocument_"+loc + + if os.path.isdir(tardir): + shutil.rmtree(tardir) + copyTemplate(tardir+"\\") + + print ( "Making '%s'" % tardir ) + shutil.copy(".\\loc\\"+loc+"\\index.html",tardir+"\\HelpDocument\\index.html") + + createZip(tardir) + shutil.rmtree(tardir) \ No newline at end of file diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/de-DE/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/de-DE/index.html new file mode 100644 index 0000000..111efda --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/de-DE/index.html @@ -0,0 +1,170 @@ + + + + Spielhilfe + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft ist ein Spiel, bei dem du aus Blöcken alles bauen kannst, was du möchtest. Nachts treiben sich Monster herum – du solltest einen Unterschlupf bauen, bevor sie herauskommen. +

+ Im Spiel ist ein vollständiges Tutorial enthalten, das wir dir ans Herz legen möchten, falls du neu dabei bist. + Außerdem helfen dir Tooltips bei deinen ersten Schritten im Spiel. Falls du etwas Bestimmtes wissen möchtest, solltest du im umfassenden Abschnitt „So wird gespielt“ nachsehen; + darüber hinaus findest du im Menü „Hilfe und Optionen“ viele Einstellungen, die du anpassen kannst. + +
+
+ + +
+ +
+ Xbox One Controller + +

Standardlayout:

+
+ + +
+
Springen
+
Ablegen
+
Crafting
+
Inventar
+
Gegenstand wechseln
+
Gegenstand wechseln
+
+
+
Verwenden
+
Aktion
+
Bewegen/Sprinten
+
Kameramodus ändern
+
Schleichen/Schauen
+
Spieler/Einladen
+
+
+
+ Es stehen alternative Controller-Layouts sowie Einstellungen für Linkshänder und zum Umkehren der Sicht zur Verfügung. +
+
+ + +
+ + + +
+ +
+ Website: {*WEBSITE*} +

+ E-Mail-Adresse: {*EMAIL_ADDRESS*} +

+ Telefonnummern: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/en-GB/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/en-GB/index.html new file mode 100644 index 0000000..4aa7032 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/en-GB/index.html @@ -0,0 +1,170 @@ + + + + Game Help + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft is a game about placing blocks to build anything you can imagine. At night monsters come out. Make sure to build a shelter before that happens. +

+ The game contains a full tutorial, so we recommend that you give that go if you are new to the game. + The game also features in-game tooltips to guide you through your initial learning experience and if you need specific help, there is a comprehensive ‘How to Play’ section, + as well as many customisable settings, all of which you can find in the Help & Options menu. + +
+
+ + +
+ +
+ Xbox One Controller + +

Default layout:

+
+ + +
+
Jump
+
Drop
+
Crafting
+
Inventory
+
Cycle Held Item
+
Cycle Held Item
+
+
+
Use
+
Action
+
Move/Sprint
+
Change Camera Mode
+
Sneak/Look
+
Players/Invite
+
+
+
+ There are alternative controller layouts available as well as Invert Look and Southpaw settings. +
+
+ + +
+ + + +
+ +
+ Website: {*WEBSITE*} +

+ Email-address: {*EMAIL_ADDRESS*} +

+ Phone Numbers: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/es-ES/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/es-ES/index.html new file mode 100644 index 0000000..3672456 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/es-ES/index.html @@ -0,0 +1,170 @@ + + + + Ayuda del juego + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft es un juego de colocar bloques para construir todo lo que puedas imaginar. Por las noches salen los monstruos, así que asegúrate de construir un refugio antes de que esto suceda. +

+ El juego tiene un tutorial completo que te recomendamos seguir si es la primera vez que juegas. + También cuenta con mensajes emergentes que te ayudarán con la experiencia de aprendizaje inicial y, si necesitas ayuda con algo en concreto, existe una exhaustiva sección titulada "Cómo se juega", + además de muchos ajustes personalizables que podrás encontrar en el menú Ayuda y opciones. + +
+
+ + +
+ +
+ Xbox One Controller + +

Configuración predeterminada:

+
+ + +
+
Salto
+
Soltar
+
Crear
+
Inventario
+
Alternar objeto en mano
+
Alternar objeto en mano
+
+
+
Utilizar
+
Acción
+
Moverse/Esprintar
+
Cambiar modo de cámara
+
Acechar/Mirar
+
Jugadores/Invitar
+
+
+
+ También hay disponibles configuraciones de mando alternativas, además de para zurdos y para invertir la vista. +
+
+ + +
+ + + +
+ +
+ Sitio web: {*WEBSITE*} +

+ Correo electrónico: {*EMAIL_ADDRESS*} +

+ Números de teléfono: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/fr-FR/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/fr-FR/index.html new file mode 100644 index 0000000..55ce157 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/fr-FR/index.html @@ -0,0 +1,170 @@ + + + + Aide du jeu + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft est un jeu qui vous permet de placer des blocs pour construire absolument tout ce que vous voulez. Des monstres sortent de leur cachette pendant la nuit, alors n'oubliez pas de construire un abri pour vous protéger. +

+ Le jeu propose un didacticiel très complet, que nous vous recommandons d'effectuer si vous jouez pour la première fois. + Des info-bulles sont aussi disponibles en jeu pour vous guider lors de vos premiers pas. Si vous avez besoin d'aide sur un point spécifique, une section « Comment jouer » détaillée est à votre disposition, + ainsi que de nombreux paramètres personnalisables, dans le menu Aide et options. + +
+
+ + +
+ +
+ Xbox One Controller + +

Commandes par défaut :

+
+ + +
+
Sauter
+
Déposer
+
Artisanat
+
Inventaire
+
Changer d'objet en main
+
Changer d'objet en main
+
+
+
Utiliser
+
Action
+
Se déplacer/Courir
+
Changer de mode caméra
+
Se faufiler/Observer
+
Joueurs/Invitation
+
+
+
+ D'autres dispositions des commandes sur la manette sont disponibles, ainsi que des options Vue inversée et Gaucher. +
+
+ + +
+ + + +
+ +
+ Site Web : {*WEBSITE*} +

+ Adresse électronique : {*EMAIL_ADDRESS*} +

+ Numéros de téléphone : {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/it-IT/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/it-IT/index.html new file mode 100644 index 0000000..b65dac5 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/it-IT/index.html @@ -0,0 +1,170 @@ + + + + Guida di gioco + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft è un gioco che consente di collocare dei blocchi e di costruire praticamente qualsiasi cosa: l'unico limite è l'immaginazione! Costruisci un riparo per non farti catturare dai mostri che escono di notte. +

+ Nel gioco è presente un tutorial completo, che ti consigliamo di seguire se non hai familiarità con le caratteristiche di Minecraft; + sono inoltre incluse delle descrizioni che ti aiuteranno a muovere i primi passi; se invece vuoi saperne di più su un argomento specifico, puoi consultare la sezione 'Come giocare' + o modificare le numerose impostazioni personalizzabili nel menu Guida e opzioni. + +
+
+ + +
+ +
+ Xbox One Controller + +

Configurazione standard:

+
+ + +
+
Salto
+
Rilascia
+
Produzione
+
Inventario
+
Scorri oggetti attivo
+
Scorri oggetti attivo
+
+
+
Usa
+
Azione
+
Movimento/Scatto
+
Cambia inquadratura
+
Furtività/Osserva
+
Giocatori/Invita
+
+
+
+ Sono disponibili configurazioni dei comandi alternative, nonché una visuale invertita e impostazioni per giocatori mancini. +
+
+ + +
+ + + +
+ +
+ Sito Web: {*WEBSITE*} +

+ Indirizzo e-mail: {*EMAIL_ADDRESS*} +

+ Numeri di telefono: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/ja-JP/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/ja-JP/index.html new file mode 100644 index 0000000..9dba183 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/ja-JP/index.html @@ -0,0 +1,170 @@ + + + + ゲーム ヘルプ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft は自由な発想でブロックを積み上げて、いろいろな物を作っていくゲームです。夜になるとモンスターが現れるので、その前に安全な場所を作っておかなければなりません +

+ 本ゲームにはチュートリアルが含まれます。初めての方は、チュートリアルから始めることをお勧めします。 + また初めてのプレイの際には、「プレイ中のボタンガイド」の案内に沿ってプレイを進めていきます。特定のヘルプが必要な際は、「遊び方」セクションや、 + カスタマイズ可能な設定をご利用ください。これらはすべて「遊び方 & オプション」内にあります + +
+
+ + +
+ +
+ Xbox One Controller + +

既定のレイアウト:

+
+ + +
+
ジャンプ
+
落とす
+
工作
+
持ち物
+
手持ちアイテムの切り替え
+
手持ちアイテムの切り替え
+
+
+
使う
+
アクション
+
動く/ダッシュ
+
カメラ モードの変更
+
しのび足/見る
+
プレイヤー/招待
+
+
+
+ 上下反転や左利き設定、その他コントローラーのレイアウト設定にも変更できます +
+
+ + +
+ + + +
+ +
+ Web サイト: {*WEBSITE*} +

+ メール アドレス: {*EMAIL_ADDRESS*} +

+ 電話番号: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/ko-KR/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/ko-KR/index.html new file mode 100644 index 0000000..dee3112 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/ko-KR/index.html @@ -0,0 +1,170 @@ + + + + 게임 도움말 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft는 블록을 배치하여 무엇이든 상상한 대로 만들 수 있는 게임입니다. 밤에는 괴물이 출몰하므로, 그에 대비하여 피신처를 준비해둬야 합니다. +

+ 게임이 처음이라면, 게임 속에 포함된 튜토리얼을 먼저 플레이해 보십시오. + 게임에는 플레이 습득을 도와줄 게임 속 단추 설명 표시가 들어가 있으며, 도움말 및 옵션 메뉴에는 다양한 사용자 지정 설정과 더불어 + 특정한 질문에 대한 답을 확인할 수 있는 '게임 방법' 섹션이 포함되어 있습니다. + +
+
+ + +
+ +
+ Xbox One Controller + +

기본 배치:

+
+ + +
+
점프
+
버리기
+
제작
+
소지품
+
아이템 교체
+
아이템 교체
+
+
+
사용
+
행동
+
이동/질주
+
카메라 모드 변경
+
살금살금 걷기/보기
+
플레이어/초대
+
+
+
+ 시야 반전이나 왼손잡이 설정과 같은 대체 컨트롤러 배치를 사용할 수 있습니다. +
+
+ + +
+ + + +
+ +
+ 웹사이트: {*WEBSITE*} +

+ 이메일 주소: {*EMAIL_ADDRESS*} +

+ 전화번호: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/pt-BR/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/pt-BR/index.html new file mode 100644 index 0000000..51cfdb8 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/pt-BR/index.html @@ -0,0 +1,170 @@ + + + + Ajuda do Jogo + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft é um jogo sobre colocar blocos para montar qualquer coisa que você imaginar. À noite os monstros chegam, então construa um abrigo antes que isso aconteça. +

+ O jogo possui um tutorial completo, então recomendamos que você dê uma olhada nele se nunca jogou Minecraft antes. + O jogo também possui dicas para guiar o seu aprendizado inicial e, se você precisar de ajuda específica, há uma seção ‘Como Jogar’ fácil de entender, + assim como muitas configurações personalizáveis que você pode encontrar no menu Ajuda e Opções. + +
+
+ + +
+ +
+ Xbox One Controller + +

Layout padrão:

+
+ + +
+
Pular
+
Soltar
+
Criação
+
Inventário
+
Alternar item na mão
+
Alternar item na mão
+
+
+
Usar
+
Ação
+
Andar/Correr
+
Mudar modo da câmera
+
Olhar/Esgueirar
+
Jogadores/Convidar
+
+
+
+ Também existem layouts alternativos de controle, como visão invertida e layout para canhotos. +
+
+ + +
+ + + +
+ +
+ Website: {*WEBSITE*} +

+ Endereço de e-mail: {*EMAIL_ADDRESS*} +

+ Números de telefone: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/pt-PT/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/pt-PT/index.html new file mode 100644 index 0000000..3fcee50 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/pt-PT/index.html @@ -0,0 +1,170 @@ + + + + Ajuda do Jogo + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft é um jogo em que usas blocos para construir tudo o que conseguires imaginar. À noite, os monstros andam aí, por isso constrói um abrigo para te protegeres. +

+ O jogo contém um tutorial completo. Consulta-o se nunca tiveres jogado Minecraft. + O jogo também possui descrições do jogo para te orientar durante a experiência de aprendizagem inicial. Se precisares de ajuda específica, consulta a secção ‘Instruções de Jogo’, + bem como as muitas definições personalizáveis, que podes encontrar no menu Ajuda e Opções. + +
+
+ + +
+ +
+ Xbox One Controller + +

Esquema predefinido:

+
+ + +
+
Saltar
+
Largar
+
Criação
+
Inventário
+
Mudar de Objecto Seguro
+
Mudar de Objecto Seguro
+
+
+
Usar
+
Acção
+
Mover/Sprint
+
Alterar Modo Câmara
+
Agachar/Voar Para Baixo
+
Jogadores/Convidar
+
+
+
+ Há esquemas de controladores alternativos, bem como definições Inverter e Esquerdinos. +
+
+ + +
+ + + +
+ +
+ Website: {*WEBSITE*} +

+ E-mail: {*EMAIL_ADDRESS*} +

+ Telefones: {*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/ServiceConfig/loc/zh-CHT/index.html b/Minecraft.Client/Durango/ServiceConfig/loc/zh-CHT/index.html new file mode 100644 index 0000000..8962a20 --- /dev/null +++ b/Minecraft.Client/Durango/ServiceConfig/loc/zh-CHT/index.html @@ -0,0 +1,170 @@ + + + + 遊戲說明 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+ +
+ Minecraft 是一款透過配置方塊來創造想像世界的一款遊戲。在夜間會有怪物出沒,請確保在發生任何事情之前先把庇護所建造出來。 +

+ 此遊戲有完整的教學課程,如果您是第一次遊玩,我們建議您先進行教學課程。 + 遊戲裡還有工具提示來導引您初期的學習過程,如果您需要特定的幫助,請前往含括一切的“如何遊玩”選項, + 跟其它各種可自訂的設定一樣,您都可以在“幫助與選項”選單裡找到。 + +
+
+ + +
+ +
+ Xbox One Controller + +

預設配置:

+
+ + +
+
跳躍
+
丟棄
+
製作
+
物品欄
+
切換手持道具
+
切換所持道具
+
+
+
使用
+
行動
+
移動 / 衝刺
+
改變攝影機模式
+
偷瞄 / 觀看
+
玩家 / 邀請
+
+
+
+ 遊戲裡還有像是反轉視角和慣用左手等另類的控制器配置可用。 +
+
+ + +
+ + + +
+ +
+ 網站:{*WEBSITE*} +

+ 電子郵件地址:{*EMAIL_ADDRESS*} +

+ 電話號碼:{*LOCALE*} {*PHONE_NUMBER*} + + +
+ +
+
+ + + + + + + diff --git a/Minecraft.Client/Durango/Social/SocialManager.h b/Minecraft.Client/Durango/Social/SocialManager.h new file mode 100644 index 0000000..0b6f2b2 --- /dev/null +++ b/Minecraft.Client/Durango/Social/SocialManager.h @@ -0,0 +1,137 @@ +// +// Class to handle and manage integration with social networks. +// 4J Studios Ltd, 2011. +// Andy West +// + +#ifndef _SOCIAL_MANAGER_H +#define _SOCIAL_MANAGER_H + +#include + +#define MAX_SOCIALPOST_CAPTION 60 +#define MAX_SOCIALPOST_DESC 100 + +// XDK only provides for facebook so far. Others may follow!? +enum ESocialNetwork +{ + eFacebook = 0, + eNumSocialNetworks +}; + + +// Class follows singleton design pattern. +class CSocialManager +{ +private: + // Default constructor, copy constructor and assignment operator are all private. + CSocialManager(); + CSocialManager( const CSocialManager& ); + CSocialManager& operator= ( const CSocialManager& ); + + // Static private instance. + static CSocialManager* m_pInstance; + + // Bitset of title posting capability flags ( XSOCIAL_CAPABILITY_POSTIMAGE, XSOCIAL_CAPABILITY_POSTLINK ). + DWORD m_dwSocialPostingCapability; + + // Index of user who made current active request. + DWORD m_dwCurrRequestUser; + + // WESTY : Not sure if we even need to get social access key! +/* + // Size of the social network access key text buffer. + DWORD m_dwAccessKeyTextSize; + + // Pointer to the social network access key text buffer. + LPWSTR m_pAccessKeyText; + */ + + // The various states of the manager. + enum EState + { + eStateUnitialised = 0, + eStateReady, + eStateGetPostingCapability, + eStatePostingImage, + eStatePostingLink, + }; + + + // Current state that manager is in. + EState m_eCurrState; + + // For xsocial asyncronous operations. + XOVERLAPPED m_Overlapped; + DWORD m_dwOverlappedResultCode; + + // Social post preview image struct. + XSOCIAL_PREVIEWIMAGE m_PostPreviewImage; + +#ifdef _XBOX + // Social post image params. + XSOCIAL_IMAGEPOSTPARAMS m_PostImageParams; + + // Social post link params. + XSOCIAL_LINKPOSTPARAMS m_PostLinkParams; +#endif + + // Image details for posting an image to social network. + unsigned char* m_pMainImageBuffer; + DWORD m_dwMainImageBufferSize; + + void DestroyMainPostImage(); + void DestroyPreviewPostImage(); + + // WESTY : Not sure if we even need to get social access key! +/* + bool GetSocialNetworkAccessKey( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect, DWORD dwUserTrackingIndex, bool bShowNetworkSignin ); +*/ + +public: + // Retrieve singleton instance. + static CSocialManager* Instance(); + + // To be called once during game init. + void Initialise(); + + // Tick the social manager. Only does anything in async mode, polls for results of async actions. + void Tick(); + + // May need to be called if something changes (i.e. player signs in to live ). + bool RefreshPostingCapability(); + + // Returns true if any social newtork posting is allowed by us, false if not (if false, game must not display any social network UI). + bool IsTitleAllowedToPostAnything(); + + // Returns true if we are allowed to post images to social networks. + bool IsTitleAllowedToPostImages(); + + // Returns true if we are allowed to post links to social networks. + bool IsTitleAllowedToPostLinks(); + + // Returns false if any of the live signed in users have disabled XPRIVILEGE_SOCIAL_NETWORK_SHARING + bool AreAllUsersAllowedToPostImages(); + + // Post a test link to social network. + bool PostLinkToSocialNetwork( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect ); + + // Post a test image to social network. + bool PostImageToSocialNetwork( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect ); + + void SetSocialPostText(LPCWSTR Title, LPCWSTR Caption, LPCWSTR Desc); + + // WESTY : Not sure if we even need to get social access key! +/* + // We do not currently know what this is used for. We may not even need it? + bool ObtainSocialNetworkAccessKey( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect ); + */ + +private: + WCHAR m_wchTitleA[MAX_SOCIALPOST_CAPTION+1]; + WCHAR m_wchCaptionA[MAX_SOCIALPOST_CAPTION+1]; + WCHAR m_wchDescA[MAX_SOCIALPOST_DESC+1]; + +}; + +#endif //_SOCIAL_MANAGER_H diff --git a/Minecraft.Client/Durango/XML/ATGXmlParser.cpp b/Minecraft.Client/Durango/XML/ATGXmlParser.cpp new file mode 100644 index 0000000..fc5aed0 --- /dev/null +++ b/Minecraft.Client/Durango/XML/ATGXmlParser.cpp @@ -0,0 +1,968 @@ +// 4J-PB - +// The ATG Framework is a common set of C++ class libraries that is used by the samples in the XDK, and was developed by the Advanced Technology Group (ATG). +// The ATG Framework offers a clean and consistent format for the samples. These classes define functions used by all the samples. +// The ATG Framework together with the samples demonstrates best practices and innovative techniques for Xbox 360. There are many useful sections of code in the samples. +// You are encouraged to incorporate this code into your titles. + + +//------------------------------------------------------------------------------------- +// AtgXmlParser.cpp +// +// Simple callback non-validating XML parser implementation. +// +// Xbox Advanced Technology Group. +// Copyright (C) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "AtgXmlParser.h" + +namespace ATG +{ + +//------------------------------------------------------------------------------------- +// Name: XMLParser::XMLParser +//------------------------------------------------------------------------------------- +XMLParser::XMLParser() +{ + m_pWritePtr = m_pWriteBuf; + m_pReadPtr = m_pReadBuf; + m_pISAXCallback = NULL; + m_hFile = INVALID_HANDLE_VALUE; +} + +//------------------------------------------------------------------------------------- +// Name: XMLParser::~XMLParser +//------------------------------------------------------------------------------------- +XMLParser::~XMLParser() +{ +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::FillBuffer +// Desc: Reads a block from the current open file +//------------------------------------------------------------------------------------- +VOID XMLParser::FillBuffer() +{ + DWORD NChars; + + m_pReadPtr = m_pReadBuf; + + if( m_hFile == NULL ) + { + if( m_uInXMLBufferCharsLeft > XML_READ_BUFFER_SIZE ) + NChars = XML_READ_BUFFER_SIZE; + else + NChars = m_uInXMLBufferCharsLeft; + + CopyMemory( m_pReadBuf, m_pInXMLBuffer, NChars ); + m_uInXMLBufferCharsLeft -= NChars; + m_pInXMLBuffer += NChars; + } + else + { + if( !ReadFile( m_hFile, m_pReadBuf, XML_READ_BUFFER_SIZE, &NChars, NULL )) + { + return; + } + } + + m_dwCharsConsumed += NChars; + __int64 iProgress = m_dwCharsTotal ? (( (__int64)m_dwCharsConsumed * 1000 ) / (__int64)m_dwCharsTotal) : 0; + m_pISAXCallback->SetParseProgress( (DWORD)iProgress ); + + m_pReadBuf[ NChars ] = '\0'; + m_pReadBuf[ NChars + 1] = '\0'; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::SkipNextAdvance +// Desc: Puts the last character read back on the input stream +//------------------------------------------------------------------------------------- +VOID XMLParser::SkipNextAdvance() +{ + m_bSkipNextAdvance = TRUE; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::ConsumeSpace +// Desc: Skips spaces in the current stream +//------------------------------------------------------------------------------------- +HRESULT XMLParser::ConsumeSpace() +{ + HRESULT hr; + + // Skip spaces + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + while ( ( m_Ch == ' ' ) || ( m_Ch == '\t' ) || + ( m_Ch == '\n' ) || ( m_Ch == '\r' ) ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + } + SkipNextAdvance(); + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::ConvertEscape +// Desc: Copies and converts an escape sequence into m_pWriteBuf +//------------------------------------------------------------------------------------- +HRESULT XMLParser::ConvertEscape() +{ + HRESULT hr; + WCHAR wVal = 0; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + // all escape sequences start with &, so ignore the first character + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if ( m_Ch == '#' ) // character as hex or decimal + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + if ( m_Ch == 'x' ) // hex number + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + while ( m_Ch != ';' ) + { + wVal *= 16; + + if ( ( m_Ch >= '0' ) && ( m_Ch <= '9' ) ) + { + wVal += m_Ch - '0'; + } + else if ( ( m_Ch >= 'a' ) && ( m_Ch <= 'f' ) ) + { + wVal += m_Ch - 'a' + 10; + } + else if ( ( m_Ch >= 'A' ) && ( m_Ch <= 'F' ) ) + { + wVal += m_Ch - 'A' + 10; + } + else + { + Error( E_INVALID_XML_SYNTAX, "Expected hex digit as part of &#x escape sequence" ); + return E_INVALID_XML_SYNTAX; + } + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + } + } + else // decimal number + { + while ( m_Ch != ';' ) + { + wVal *= 10; + + if ( ( m_Ch >= '0' ) && ( m_Ch <= '9' ) ) + { + wVal += m_Ch - '0'; + } + else + { + Error( E_INVALID_XML_SYNTAX, "Expected decimal digit as part of &# escape sequence" ); + return E_INVALID_XML_SYNTAX; + } + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + } + } + + // copy character into the buffer + m_Ch = wVal; + + return S_OK; + } + + // must be an entity reference + + WCHAR *pEntityRefVal = m_pWritePtr; + UINT EntityRefLen; + + SkipNextAdvance(); + if( FAILED( hr = AdvanceName() ) ) + return hr; + + EntityRefLen = (UINT)( m_pWritePtr - pEntityRefVal ); + m_pWritePtr = pEntityRefVal; + + if ( EntityRefLen == 0 ) + { + Error( E_INVALID_XML_SYNTAX, "Expecting entity name after &" ); + return E_INVALID_XML_SYNTAX; + } + + if( !wcsncmp( pEntityRefVal, L"lt", EntityRefLen ) ) + wVal = '<'; + else if( !wcsncmp( pEntityRefVal, L"gt", EntityRefLen ) ) + wVal = '>'; + else if( !wcsncmp( pEntityRefVal, L"amp", EntityRefLen ) ) + wVal = '&'; + else if( !wcsncmp( pEntityRefVal, L"apos", EntityRefLen ) ) + wVal = '\''; + else if( !wcsncmp( pEntityRefVal, L"quot", EntityRefLen ) ) + wVal = '"'; + else + { + Error( E_INVALID_XML_SYNTAX, "Unrecognized entity name after & - (should be lt, gt, amp, apos, or quot)" ); + return E_INVALID_XML_SYNTAX; // return false if unrecognized token sequence + } + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( m_Ch != ';' ) + { + Error( E_INVALID_XML_SYNTAX, "Expected terminating ; for entity reference" ); + return E_INVALID_XML_SYNTAX; // malformed reference - needs terminating ; + } + + m_Ch = wVal; + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceAttrVal +// Desc: Copies an attribute value into m_pWrite buf, skipping surrounding quotes +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceAttrVal() +{ + HRESULT hr; + WCHAR wQuoteChar; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( ( m_Ch != '"' ) && ( m_Ch != '\'' ) ) + { + Error( E_INVALID_XML_SYNTAX, "Attribute values must be enclosed in quotes" ); + return E_INVALID_XML_SYNTAX; + } + + wQuoteChar = m_Ch; + + for( ;; ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + else if( m_Ch == wQuoteChar ) + break; + else if( m_Ch == '&' ) + { + SkipNextAdvance(); + if( FAILED( hr = ConvertEscape() ) ) + return hr; + } + else if( m_Ch == '<' ) + { + Error( E_INVALID_XML_SYNTAX, "Illegal character '<' in element tag" ); + return E_INVALID_XML_SYNTAX; + } + + // copy character into the buffer + + if( m_pWritePtr - m_pWriteBuf >= XML_WRITE_BUFFER_SIZE ) + { + Error( E_INVALID_XML_SYNTAX, "Total element tag size may not be more than %d characters", XML_WRITE_BUFFER_SIZE ); + return E_INVALID_XML_SYNTAX; + } + + *m_pWritePtr = m_Ch; + m_pWritePtr++; + } + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceName +// Desc: Copies a name into the m_pWriteBuf - returns TRUE on success, FALSE on failure +// Ignores leading whitespace. Currently does not support unicode names +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceName() +{ + HRESULT hr; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( ( ( m_Ch < 'A' ) || ( m_Ch > 'Z' ) ) && + ( ( m_Ch < 'a' ) || ( m_Ch > 'z' ) ) && + ( m_Ch != '_' ) && ( m_Ch != ':' ) ) + { + Error( E_INVALID_XML_SYNTAX, "Names must start with an alphabetic character or _ or :" ); + return E_INVALID_XML_SYNTAX; + } + + while( ( ( m_Ch >= 'A' ) && ( m_Ch <= 'Z' ) ) || + ( ( m_Ch >= 'a' ) && ( m_Ch <= 'z' ) ) || + ( ( m_Ch >= '0' ) && ( m_Ch <= '9' ) ) || + ( m_Ch == '_' ) || ( m_Ch == ':' ) || + ( m_Ch == '-' ) || ( m_Ch == '.' ) ) + { + + if( m_pWritePtr - m_pWriteBuf >= XML_WRITE_BUFFER_SIZE ) + { + Error( E_INVALID_XML_SYNTAX, "Total element tag size may not be more than %d characters", XML_WRITE_BUFFER_SIZE ); + return E_INVALID_XML_SYNTAX; + } + + *m_pWritePtr = m_Ch; + m_pWritePtr++; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + } + + SkipNextAdvance(); + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceCharacter +// Desc: Copies the character at *m_pReadPtr to m_Ch +// handling difference in UTF16 / UTF8, and big/little endian +// and getting another chunk of the file if needed +// Returns S_OK if there are more characters, E_ABORT for no characters to read +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceCharacter( BOOL bOkToFail ) +{ + if( m_bSkipNextAdvance ) + { + m_bSkipNextAdvance = FALSE; + return S_OK; + } + + // If we hit EOF in the middle of a character, + // it's ok-- we'll just have a corrupt last character + // (the buffer is padded with double NULLs ) + + if ( ( m_pReadPtr[0] == '\0' ) && ( m_pReadPtr[1] == '\0' ) ) + { + // Read more from the file + FillBuffer(); + + // We are at EOF if it is still NULL + if ( ( m_pReadPtr[0] == '\0' ) && ( m_pReadPtr[1] == '\0' ) ) + { + if( !bOkToFail ) + { + Error( E_INVALID_XML_SYNTAX, "Unexpected EOF while parsing XML file" ); + return E_INVALID_XML_SYNTAX; + } + else + { + return E_FAIL; + } + } + } + + if( m_bUnicode == FALSE ) + { + m_Ch = *((CHAR *)m_pReadPtr); + m_pReadPtr++; + } + else // if( m_bUnicode == TRUE ) + { + m_Ch = *((WCHAR *)m_pReadPtr); + + if( m_bReverseBytes ) + { + m_Ch = ( m_Ch << 8 ) + ( m_Ch >> 8 ); + } + + m_pReadPtr += 2; + } + + if( m_Ch == '\n' ) + { + m_pISAXCallback->m_LineNum++; + m_pISAXCallback->m_LinePos = 0; + } + else if( m_Ch != '\r' ) + m_pISAXCallback->m_LinePos++; + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceElement +// Desc: Builds data, calls callback +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceElement() +{ + HRESULT hr; + + // write ptr at the beginning of the buffer + m_pWritePtr = m_pWriteBuf; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + // if first character wasn't '<', we wouldn't be here + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( m_Ch == '!' ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + if ( m_Ch == '-' ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + if( m_Ch != '-' ) + { + Error( E_INVALID_XML_SYNTAX, "Expecting '-' after 'ElementEnd( pEntityRefVal, + (UINT) ( m_pWritePtr - pEntityRefVal ) ) ) ) + return E_ABORT; + + if( FAILED( hr = ConsumeSpace() ) ) + return hr; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( m_Ch != '>' ) + { + Error( E_INVALID_XML_SYNTAX, "Expecting '>' after name for closing entity reference" ); + return E_INVALID_XML_SYNTAX; + } + } + else if( m_Ch == '?' ) + { + // just skip any xml header tag since not really important after identifying character set + for( ;; ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if ( m_Ch == '>' ) + return S_OK; + } + } + else + { + XMLAttribute Attributes[ XML_MAX_ATTRIBUTES_PER_ELEMENT ]; + UINT NumAttrs; + + WCHAR *pEntityRefVal = m_pWritePtr; + UINT EntityRefLen; + + NumAttrs = 0; + + SkipNextAdvance(); + + // Entity tag + if( FAILED( hr = AdvanceName() ) ) + return hr; + + EntityRefLen = (UINT)( m_pWritePtr - pEntityRefVal ); + + if( FAILED( hr = ConsumeSpace() ) ) + return hr; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + // read attributes + while( ( m_Ch != '>' ) && ( m_Ch != '/' ) ) + { + SkipNextAdvance(); + + if ( NumAttrs >= XML_MAX_ATTRIBUTES_PER_ELEMENT ) + { + Error( E_INVALID_XML_SYNTAX, "Elements may not have more than %d attributes", XML_MAX_ATTRIBUTES_PER_ELEMENT ); + return E_INVALID_XML_SYNTAX; + } + + Attributes[ NumAttrs ].strName = m_pWritePtr; + + // Attribute name + if( FAILED( hr = AdvanceName() ) ) + return hr; + + Attributes[ NumAttrs ].NameLen = (UINT)( m_pWritePtr - Attributes[ NumAttrs ].strName ); + + if( FAILED( hr = ConsumeSpace() ) ) + return hr; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if( m_Ch != '=' ) + { + Error( E_INVALID_XML_SYNTAX, "Expecting '=' character after attribute name" ); + return E_INVALID_XML_SYNTAX; + } + + if( FAILED( hr = ConsumeSpace() ) ) + return hr; + + Attributes[ NumAttrs ].strValue = m_pWritePtr; + + if( FAILED( hr = AdvanceAttrVal() ) ) + return hr; + + Attributes[ NumAttrs ].ValueLen = (UINT)( m_pWritePtr - + Attributes[ NumAttrs ].strValue ); + + ++NumAttrs; + + if( FAILED( hr = ConsumeSpace() ) ) + return hr; + + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + } + + if( m_Ch == '/' ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + if( m_Ch != '>' ) + { + Error( E_INVALID_XML_SYNTAX, "Expecting '>' after '/' in element tag" ); + return E_INVALID_XML_SYNTAX; + } + + if( FAILED( m_pISAXCallback->ElementBegin( pEntityRefVal, EntityRefLen, + Attributes, NumAttrs ) ) ) + return E_ABORT; + + if( FAILED( m_pISAXCallback->ElementEnd( pEntityRefVal, EntityRefLen ) ) ) + return E_ABORT; + } + else + { + if( FAILED( m_pISAXCallback->ElementBegin( pEntityRefVal, EntityRefLen, + Attributes, NumAttrs ) ) ) + return E_ABORT; + } + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceCDATA +// Desc: Read a CDATA section +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceCDATA() +{ + HRESULT hr; + WORD wStage = 0; + + if( FAILED( m_pISAXCallback->CDATABegin() ) ) + return E_ABORT; + + for( ;; ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + *m_pWritePtr = m_Ch; + m_pWritePtr++; + + if( ( m_Ch == ']' ) && ( wStage == 0 ) ) + wStage = 1; + else if( ( m_Ch == ']' ) && ( wStage == 1 ) ) + wStage = 2; + else if( ( m_Ch == '>' ) && ( wStage == 2 ) ) + { + m_pWritePtr -= 3; + break; + } + else + wStage = 0; + + if( m_pWritePtr - m_pWriteBuf >= XML_WRITE_BUFFER_SIZE ) + { + if( FAILED( m_pISAXCallback->CDATAData( m_pWriteBuf, (UINT)( m_pWritePtr - m_pWriteBuf ), TRUE ) ) ) + return E_ABORT; + m_pWritePtr = m_pWriteBuf; + } + } + + if( FAILED( m_pISAXCallback->CDATAData( m_pWriteBuf, (UINT)( m_pWritePtr - m_pWriteBuf ), FALSE ) ) ) + return E_ABORT; + + m_pWritePtr = m_pWriteBuf; + + if( FAILED( m_pISAXCallback->CDATAEnd() ) ) + return E_ABORT; + + return S_OK; +} + +//------------------------------------------------------------------------------------- +// Name: XMLParser::AdvanceComment +// Desk: Skips over a comment +//------------------------------------------------------------------------------------- +HRESULT XMLParser::AdvanceComment() +{ + HRESULT hr; + WORD wStage; + + wStage = 0; + for( ;; ) + { + if( FAILED( hr = AdvanceCharacter() ) ) + return hr; + + if (( m_Ch == '-' ) && ( wStage == 0 )) + wStage = 1; + else if (( m_Ch == '-' ) && ( wStage == 1 )) + wStage = 2; + else if (( m_Ch == '>' ) && ( wStage == 2 )) + break; + else + wStage = 0; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::RegisterSAXCallbackInterface +// Desc: Registers callback interface +//------------------------------------------------------------------------------------- +VOID XMLParser::RegisterSAXCallbackInterface( ISAXCallback *pISAXCallback ) +{ + m_pISAXCallback = pISAXCallback; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::GetSAXCallbackInterface +// Desc: Returns current callback interface +//------------------------------------------------------------------------------------- +ISAXCallback* XMLParser::GetSAXCallbackInterface() +{ + return m_pISAXCallback; +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::MainParseLoop +// Desc: Main Loop to Parse Data - source agnostic +//------------------------------------------------------------------------------------- +HRESULT XMLParser::MainParseLoop() +{ + BOOL bWhiteSpaceOnly = TRUE; + HRESULT hr = S_OK; + + if( FAILED( m_pISAXCallback->StartDocument() ) ) + return E_ABORT; + + m_pWritePtr = m_pWriteBuf; + + FillBuffer(); + + if ( *((WCHAR *) m_pReadBuf ) == 0xFEFF ) + { + m_bUnicode = TRUE; + m_bReverseBytes = FALSE; + m_pReadPtr += 2; + } + else if ( *((WCHAR *) m_pReadBuf ) == 0xFFFE ) + { + m_bUnicode = TRUE; + m_bReverseBytes = TRUE; + m_pReadPtr += 2; + } + else if ( *((WCHAR *) m_pReadBuf ) == 0x003C ) + { + m_bUnicode = TRUE; + m_bReverseBytes = FALSE; + } + else if ( *((WCHAR *) m_pReadBuf ) == 0x3C00 ) + { + m_bUnicode = TRUE; + m_bReverseBytes = TRUE; + } + else if ( m_pReadBuf[ 0 ] == 0x3C ) + { + m_bUnicode = FALSE; + m_bReverseBytes = FALSE; + } + else + { + Error( E_INVALID_XML_SYNTAX, "Unrecognized encoding (parser does not support UTF-8 language encodings)" ); + return E_INVALID_XML_SYNTAX; + } + + for( ;; ) + { + if( FAILED( AdvanceCharacter( TRUE ) ) ) + { + if ( ( (UINT) ( m_pWritePtr - m_pWriteBuf ) != 0 ) && ( !bWhiteSpaceOnly ) ) + { + if( FAILED( m_pISAXCallback->ElementContent( m_pWriteBuf, (UINT)( m_pWritePtr - m_pWriteBuf ), FALSE ) ) ) + return E_ABORT; + + bWhiteSpaceOnly = TRUE; + } + + if( FAILED( m_pISAXCallback->EndDocument() ) ) + return E_ABORT; + + return S_OK; + } + + if( m_Ch == '<' ) + { + if( ( (UINT) ( m_pWritePtr - m_pWriteBuf ) != 0 ) && ( !bWhiteSpaceOnly ) ) + { + if( FAILED( m_pISAXCallback->ElementContent( m_pWriteBuf, (UINT)( m_pWritePtr - m_pWriteBuf ), FALSE ) ) ) + return E_ABORT; + + bWhiteSpaceOnly = TRUE; + } + + SkipNextAdvance(); + + m_pWritePtr = m_pWriteBuf; + + if( FAILED( hr = AdvanceElement() ) ) + return hr; + + m_pWritePtr = m_pWriteBuf; + } + else + { + if( m_Ch == '&' ) + { + SkipNextAdvance(); + if( FAILED( hr = ConvertEscape() ) ) + return hr; + } + + if( bWhiteSpaceOnly && ( m_Ch != ' ' ) && ( m_Ch != '\n' ) && ( m_Ch != '\r' ) && + ( m_Ch != '\t' ) ) + { + bWhiteSpaceOnly = FALSE; + } + + *m_pWritePtr = m_Ch; + m_pWritePtr++; + + if( m_pWritePtr - m_pWriteBuf >= XML_WRITE_BUFFER_SIZE ) + { + if( !bWhiteSpaceOnly ) + { + if( FAILED( m_pISAXCallback->ElementContent( m_pWriteBuf, + ( UINT ) ( m_pWritePtr - m_pWriteBuf ), + TRUE ) ) ) + { + return E_ABORT; + } + } + + m_pWritePtr = m_pWriteBuf; + bWhiteSpaceOnly = TRUE; + } + } + } +} + + +//------------------------------------------------------------------------------------- +// Name: XMLParser::ParseXMLFile +// Desc: Builds element data +//------------------------------------------------------------------------------------- +HRESULT XMLParser::ParseXMLFile( CONST CHAR *strFilename ) +{ + HRESULT hr; + + if( m_pISAXCallback == NULL ) + return E_NOINTERFACE; + + m_pISAXCallback->m_LineNum = 1; + m_pISAXCallback->m_LinePos = 0; + m_pISAXCallback->m_strFilename = strFilename; // save this off only while we parse the file + + m_bSkipNextAdvance = FALSE; + m_pReadPtr = m_pReadBuf; + + m_pReadBuf[ 0 ] = '\0'; + m_pReadBuf[ 1 ] = '\0'; + + m_pInXMLBuffer = NULL; + m_uInXMLBufferCharsLeft = 0; + + WCHAR wchFilename[ 64 ]; + + swprintf_s(wchFilename,64,L"%s",strFilename); + + m_hFile = CreateFile( wchFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); + + if( m_hFile == INVALID_HANDLE_VALUE ) + { + Error( E_COULD_NOT_OPEN_FILE, "Error opening file" ); + hr = E_COULD_NOT_OPEN_FILE; + + } + else + { + LARGE_INTEGER iFileSize; + GetFileSizeEx( m_hFile, &iFileSize ); + m_dwCharsTotal = (DWORD)iFileSize.QuadPart; + m_dwCharsConsumed = 0; + hr = MainParseLoop(); + } + + // Close the file + if( m_hFile != INVALID_HANDLE_VALUE ) + CloseHandle( m_hFile ); + m_hFile = INVALID_HANDLE_VALUE; + + // we no longer own strFilename, so un-set it + m_pISAXCallback->m_strFilename = NULL; + + return hr; +} + +//------------------------------------------------------------------------------------- +// Name: XMLParser::ParseXMLFile +// Desc: Builds element data +//------------------------------------------------------------------------------------- +HRESULT XMLParser::ParseXMLBuffer( CONST CHAR *strBuffer, UINT uBufferSize ) +{ + HRESULT hr; + + if( m_pISAXCallback == NULL ) + return E_NOINTERFACE; + + m_pISAXCallback->m_LineNum = 1; + m_pISAXCallback->m_LinePos = 0; + m_pISAXCallback->m_strFilename = ""; // save this off only while we parse the file + + m_bSkipNextAdvance = FALSE; + m_pReadPtr = m_pReadBuf; + + m_pReadBuf[ 0 ] = '\0'; + m_pReadBuf[ 1 ] = '\0'; + + m_hFile = NULL; + m_pInXMLBuffer = strBuffer; + m_uInXMLBufferCharsLeft = uBufferSize; + m_dwCharsTotal = uBufferSize; + m_dwCharsConsumed = 0; + + hr = MainParseLoop(); + + // we no longer own strFilename, so un-set it + m_pISAXCallback->m_strFilename = NULL; + + return hr; +} + +//------------------------------------------------------------------------------------- +// XMLParser::Error() +// Logs an error through the callback interface +//------------------------------------------------------------------------------------- +#ifdef _Printf_format_string_ // VC++ 2008 and later support this annotation +VOID XMLParser::Error( HRESULT hErr, _In_z_ _Printf_format_string_ CONST CHAR* strFormat, ... ) +#else +VOID XMLParser::Error( HRESULT hErr, CONST CHAR* strFormat, ... ) +#endif +{ + CONST INT MAX_OUTPUT_STR = 160; + CHAR strBuffer[ MAX_OUTPUT_STR ]; + va_list pArglist; + va_start( pArglist, strFormat ); + + vsprintf( strBuffer, strFormat, pArglist ); + + m_pISAXCallback->Error( hErr, strBuffer ); + va_end( pArglist ); +} + +} // namespace ATG diff --git a/Minecraft.Client/Durango/XML/ATGXmlParser.h b/Minecraft.Client/Durango/XML/ATGXmlParser.h new file mode 100644 index 0000000..75142e3 --- /dev/null +++ b/Minecraft.Client/Durango/XML/ATGXmlParser.h @@ -0,0 +1,156 @@ +// 4J-PB - +// The ATG Framework is a common set of C++ class libraries that is used by the samples in the XDK, and was developed by the Advanced Technology Group (ATG). +// The ATG Framework offers a clean and consistent format for the samples. These classes define functions used by all the samples. +// The ATG Framework together with the samples demonstrates best practices and innovative techniques for Xbox 360. There are many useful sections of code in the samples. +// You are encouraged to incorporate this code into your titles. + +//------------------------------------------------------------------------------------- +// AtgXmlParser.h +// +// XMLParser and SAX interface declaration +// +// Xbox Advanced Technology Group +// Copyright (C) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------------------------------- + +#pragma once +#ifndef ATGXMLPARSER_H +#define ATGXMLPARSER_H + +namespace ATG +{ + +//----------------------------------------------------------------------------- +// error returns from XMLParse +//----------------------------------------------------------------------------- +#define _ATGFAC 0x61B +#define E_COULD_NOT_OPEN_FILE MAKE_HRESULT(1, _ATGFAC, 0x0001 ) +#define E_INVALID_XML_SYNTAX MAKE_HRESULT(1, _ATGFAC, 0x0002 ) + + +CONST UINT XML_MAX_ATTRIBUTES_PER_ELEMENT = 32; +CONST UINT XML_MAX_NAME_LENGTH = 128; +CONST UINT XML_READ_BUFFER_SIZE = 2048; +CONST UINT XML_WRITE_BUFFER_SIZE = 2048; + +// No tag can be longer than XML_WRITE_BUFFER_SIZE - an error will be returned if +// it is + +//------------------------------------------------------------------------------------- +struct XMLAttribute +{ + WCHAR* strName; + UINT NameLen; + WCHAR* strValue; + UINT ValueLen; +}; + +//------------------------------------------------------------------------------------- +class ISAXCallback +{ +friend class XMLParser; +public: + ISAXCallback() {}; + virtual ~ISAXCallback() {}; + + virtual HRESULT StartDocument() = 0; + virtual HRESULT EndDocument() = 0; + + virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, + CONST XMLAttribute *pAttributes, UINT NumAttributes ) = 0; + virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) = 0; + virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ) = 0; + + virtual HRESULT CDATABegin( ) = 0; + virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ) = 0; + virtual HRESULT CDATAEnd( ) = 0; + + virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) = 0; + + virtual VOID SetParseProgress( DWORD dwProgress ) { } + + const CHAR* GetFilename() { return m_strFilename; } + UINT GetLineNumber() { return m_LineNum; } + UINT GetLinePosition() { return m_LinePos; } + +private: + CONST CHAR *m_strFilename; + UINT m_LineNum; + UINT m_LinePos; +}; + + +//------------------------------------------------------------------------------------- +class XMLParser +{ +public: + XMLParser(); + ~XMLParser(); + + // Register an interface inheiriting from ISAXCallback + VOID RegisterSAXCallbackInterface( ISAXCallback *pISAXCallback ); + + // Get the registered interface + ISAXCallback* GetSAXCallbackInterface(); + + // ParseXMLFile returns one of the following: + // E_COULD_NOT_OPEN_FILE - couldn't open the file + // E_INVALID_XML_SYNTAX - bad XML syntax according to this parser + // E_NOINTERFACE - RegisterSAXCallbackInterface not called + // E_ABORT - callback returned a fail code + // S_OK - file parsed and completed + + HRESULT ParseXMLFile( CONST CHAR *strFilename ); + + // Parses from a buffer- if you pass a WCHAR buffer (and cast it), it will + // correctly detect it and use unicode instead. Return codes are the + // same as for ParseXMLFile + + HRESULT ParseXMLBuffer( CONST CHAR* strBuffer, UINT uBufferSize ); + +private: + HRESULT MainParseLoop(); + + HRESULT AdvanceCharacter( BOOL bOkToFail = FALSE ); + VOID SkipNextAdvance(); + + HRESULT ConsumeSpace(); + HRESULT ConvertEscape(); + HRESULT AdvanceElement(); + HRESULT AdvanceName(); + HRESULT AdvanceAttrVal(); + HRESULT AdvanceCDATA(); + HRESULT AdvanceComment(); + + VOID FillBuffer(); + +#ifdef _Printf_format_string_ // VC++ 2008 and later support this annotation + VOID Error( HRESULT hRet, _In_z_ _Printf_format_string_ CONST CHAR* strFormat, ... ); +#else + VOID Error( HRESULT hRet, CONST CHAR* strFormat, ... ); +#endif + + ISAXCallback* m_pISAXCallback; + + HANDLE m_hFile; + CONST CHAR* m_pInXMLBuffer; + UINT m_uInXMLBufferCharsLeft; + DWORD m_dwCharsTotal; + DWORD m_dwCharsConsumed; + + BYTE m_pReadBuf[ XML_READ_BUFFER_SIZE + 2 ]; // room for a trailing NULL + WCHAR m_pWriteBuf[ XML_WRITE_BUFFER_SIZE ]; + + BYTE* m_pReadPtr; + WCHAR* m_pWritePtr; // write pointer within m_pBuf + + BOOL m_bUnicode; // TRUE = 16-bits, FALSE = 8-bits + BOOL m_bReverseBytes; // TRUE = reverse bytes, FALSE = don't reverse + + BOOL m_bSkipNextAdvance; + WCHAR m_Ch; // Current character being parsed +}; + +} // namespace ATG + +#endif diff --git a/Minecraft.Client/Durango/XML/xmlFilesCallback.h b/Minecraft.Client/Durango/XML/xmlFilesCallback.h new file mode 100644 index 0000000..deba843 --- /dev/null +++ b/Minecraft.Client/Durango/XML/xmlFilesCallback.h @@ -0,0 +1,337 @@ + +#pragma once +#ifndef XMLMOJANGCALLBACK_H +#define XMLMOJANGCALLBACK_H +// xml reading + +using namespace ATG; +/* +class xmlMojangCallback : public ATG::ISAXCallback +{ +public: + virtual HRESULT StartDocument() { return S_OK; }; + virtual HRESULT EndDocument() { return S_OK; }; + + virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, CONST XMLAttribute *pAttributes, UINT NumAttributes ) + { + WCHAR wTemp[35] = L""; + WCHAR wAttName[32] = L""; + WCHAR wNameXUID[32] = L""; + WCHAR wNameSkin[32] = L""; + WCHAR wNameCloak[32] = L""; + PlayerUID xuid=0LL; + + + if (NameLen >31) + return S_FALSE; + else + wcsncpy( wAttName, strName, NameLen); + + if ( _wcsicmp(wAttName,L"root") == 0) + { + return S_OK; + } + else if ( _wcsicmp(wAttName,L"data") == 0) + { + for(UINT i = 0; i < NumAttributes; i++) + { + wcsncpy_s( wAttName, pAttributes[i].strName, pAttributes[i].NameLen); + if (_wcsicmp(wAttName,L"name")==0) + { + if (pAttributes[i].ValueLen <= 32) + wcsncpy_s( wNameXUID, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + else if (_wcsicmp(wAttName,L"xuid")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + ZeroMemory(wTemp,sizeof(WCHAR)*35); + wcsncpy_s( wTemp, pAttributes[i].strValue, pAttributes[i].ValueLen); + xuid=_wcstoui64(wTemp,NULL,10); + } + } + else if (_wcsicmp(wAttName,L"cape")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wNameCloak, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if (_wcsicmp(wAttName,L"skin")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wNameSkin, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + + } + + // if the xuid hasn't been defined, then we can't use the data + if(xuid!=0LL) + { + return CConsoleMinecraftApp::RegisterMojangData(wNameXUID , xuid, wNameSkin, wNameCloak ); + } + else return S_FALSE; + } + else + { + return S_FALSE; + } + }; + + virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) { return S_OK; }; + + virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ){ return S_OK; }; + + virtual HRESULT CDATABegin( ) { return S_OK; }; + + virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ){ return S_OK; }; + + virtual HRESULT CDATAEnd( ){ return S_OK; }; + + virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) { app.DebugPrintf("Error when Parsing xuids.XML\n"); }; + +}; +*/ +class xmlConfigCallback : public ATG::ISAXCallback +{ +public: + virtual HRESULT StartDocument() { return S_OK; }; + virtual HRESULT EndDocument() { return S_OK; }; + + virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, CONST XMLAttribute *pAttributes, UINT NumAttributes ) + { + WCHAR wTemp[35] = L""; + WCHAR wType[32] = L""; + WCHAR wAttName[32] = L""; + WCHAR wValue[32] = L""; + int iValue=-1; + + if (NameLen >31) + return S_FALSE; + else + wcsncpy_s( wAttName, strName, NameLen); + + if ( _wcsicmp(wAttName,L"root") == 0) + { + return S_OK; + } + else if ( _wcsicmp(wAttName,L"data") == 0) + { + for(UINT i = 0; i < NumAttributes; i++) + { + wcsncpy_s( wAttName, pAttributes[i].strName, pAttributes[i].NameLen); + if (_wcsicmp(wAttName,L"Type")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wType, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if(_wcsicmp(wAttName,L"Value")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wValue, pAttributes[i].strValue, pAttributes[i].ValueLen); + +#ifdef _XBOX + iValue=_wtoi(wValue); +#else + iValue=wcstol(wValue, NULL, 10); +#endif + } + } + } + + // if the xuid hasn't been defined, then we can't use the data + if(iValue!=-1) + { + #ifdef _DEBUG + wprintf(L"Type - %s, Value - %d, ",wType, iValue); + #endif + + return CConsoleMinecraftApp::RegisterConfigValues(wType, iValue ); + } + else + { + return S_FALSE; + } + } + else + { + return S_FALSE; + } + } + + + virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) { return S_OK; }; + + virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ){ return S_OK; }; + + virtual HRESULT CDATABegin( ) { return S_OK; }; + + virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ){ return S_OK; }; + + virtual HRESULT CDATAEnd( ){ return S_OK; }; + + virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) { app.DebugPrintf("Error when Parsing xuids.XML\n"); }; + +}; + +class xmlDLCInfoCallback : public ATG::ISAXCallback +{ +public: + virtual HRESULT StartDocument() { return S_OK; }; + virtual HRESULT EndDocument() { return S_OK; }; + + virtual HRESULT ElementBegin( CONST WCHAR* strName, UINT NameLen, CONST XMLAttribute *pAttributes, UINT NumAttributes ) + { + WCHAR wTemp[35] = L""; + WCHAR wAttName[32] = L""; + WCHAR wNameBanner[32] = L""; + WCHAR wDataFile[32] = L""; + WCHAR wType[32] = L""; + WCHAR wFirstSkin[32] = L""; + WCHAR wConfig[32] = L""; + WCHAR wUID[64] = L""; + WCHAR wName[64] = L""; + unsigned int uiSortIndex=0L; + int iGender=0; + int iConfig=0; + + if (NameLen >31) + return S_FALSE; + else + wcsncpy_s( wAttName, strName, NameLen); + + if ( _wcsicmp(wAttName,L"root") == 0) + { + return S_OK; + } + else if ( _wcsicmp(wAttName,L"data") == 0) + { + for(UINT i = 0; i < NumAttributes; i++) + { + wcsncpy_s( wAttName, pAttributes[i].strName, pAttributes[i].NameLen); + if (_wcsicmp(wAttName,L"SortIndex")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + ZeroMemory(wTemp,sizeof(WCHAR)*35); + wcsncpy_s( wTemp, pAttributes[i].strValue, pAttributes[i].ValueLen); + uiSortIndex=wcstoul(wTemp,NULL,10); + } + } + else if (_wcsicmp(wAttName,L"Banner")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wNameBanner, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if (_wcsicmp(wAttName,L"Full")==0) + { + if (pAttributes[i].ValueLen <= 64) + { + ZeroMemory(wUID,sizeof(WCHAR)*64); + wcsncpy_s( wUID, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if (_wcsicmp(wAttName,L"Name")==0) + { + if (pAttributes[i].ValueLen <= 64) + { + ZeroMemory(wName,sizeof(WCHAR)*64); + wcsncpy_s( wName, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if (_wcsicmp(wAttName,L"FirstSkin")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wFirstSkin, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if (_wcsicmp(wAttName,L"Type")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wType, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + else if(_wcsicmp(wAttName,L"Config")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wConfig, pAttributes[i].strValue, pAttributes[i].ValueLen); + +#ifdef _XBOX + iConfig=_wtoi(wConfig); +#else + iConfig=wcstol(wConfig, NULL, 10); +#endif + } + } + else if (_wcsicmp(wAttName,L"DataFile")==0) + { + if (pAttributes[i].ValueLen <= 32) + { + wcsncpy_s( wDataFile, pAttributes[i].strValue, pAttributes[i].ValueLen); + } + } + + } + + // if the xuid hasn't been defined, then we can't use the data + if(wUID[0]!=0) + { +#ifdef _DEBUG + wprintf(L"Type - %ls, Name - %ls, ",wType, wNameBanner); +#endif + + eDLCContentType eDLCType=e_DLC_NotDefined; + if(wType[0]!=0) + { + if(wcscmp(wType,L"Skin")==0) + { + eDLCType=e_DLC_SkinPack; + } + else if(wcscmp(wType,L"MashUpPack")==0) + { + eDLCType=e_DLC_MashupPacks; + } + else if(wcscmp(wType,L"TexturePack")==0) + { + eDLCType=e_DLC_TexturePacks; + } + } + return CConsoleMinecraftApp::RegisterDLCData(eDLCType, wNameBanner , wUID, wName, wFirstSkin, iConfig, uiSortIndex ); + } + else + { + return S_FALSE; + } + } + else + { + return S_FALSE; + } + }; + + virtual HRESULT ElementContent( CONST WCHAR *strData, UINT DataLen, BOOL More ) { return S_OK; }; + + virtual HRESULT ElementEnd( CONST WCHAR *strName, UINT NameLen ){ return S_OK; }; + + virtual HRESULT CDATABegin( ) { return S_OK; }; + + virtual HRESULT CDATAData( CONST WCHAR *strCDATA, UINT CDATALen, BOOL bMore ){ return S_OK; }; + + virtual HRESULT CDATAEnd( ){ return S_OK; }; + + virtual VOID Error( HRESULT hError, CONST CHAR *strMessage ) { app.DebugPrintf("Error when Parsing DLC.XML\n"); }; + +}; + + +#endif \ No newline at end of file diff --git a/Minecraft.Client/Durango/XboxGameMode.cpp b/Minecraft.Client/Durango/XboxGameMode.cpp new file mode 100644 index 0000000..1b55fdd --- /dev/null +++ b/Minecraft.Client/Durango/XboxGameMode.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "XboxGameMode.h" +#include "..\Common\Tutorial\Tutorial.h" + +XboxGameMode::XboxGameMode(int iPad, Minecraft *minecraft, ClientConnection *connection) + : TutorialMode(iPad, minecraft, connection) +{ + tutorial = new Tutorial(iPad); +} \ No newline at end of file diff --git a/Minecraft.Client/Durango/XboxGameMode.h b/Minecraft.Client/Durango/XboxGameMode.h new file mode 100644 index 0000000..347cdf3 --- /dev/null +++ b/Minecraft.Client/Durango/XboxGameMode.h @@ -0,0 +1,10 @@ +#pragma once +#include "..\Common\Tutorial\TutorialMode.h" + +class XboxGameMode : public TutorialMode +{ +public: + XboxGameMode(int iPad, Minecraft *minecraft, ClientConnection *connection); + + virtual bool isImplemented() { return true; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Xbox_Awards_enum.h b/Minecraft.Client/Durango/Xbox_Awards_enum.h new file mode 100644 index 0000000..5917022 --- /dev/null +++ b/Minecraft.Client/Durango/Xbox_Awards_enum.h @@ -0,0 +1,46 @@ +#pragma once + +enum eAward +{ + eAward_TakingInventory=0, + eAward_GettingWood, + eAward_Benchmarking, + eAward_TimeToMine, + eAward_HotTopic, + eAward_AquireHardware, + eAward_TimeToFarm, + eAward_BakeBread, + eAward_TheLie, + eAward_GettingAnUpgrade, + eAward_DeliciousFish, + eAward_OnARail, + eAward_TimeToStrike, + eAward_MonsterHunter, + eAward_CowTipper, + eAward_WhenPigsFly, + eAward_LeaderOfThePack, + eAward_MOARTools, + eAward_DispenseWithThis, + eAward_InToTheNether, + eAward_mine100Blocks, + eAward_kill10Creepers, + eAward_eatPorkChop, + eAward_play100Days, + eAward_arrowKillCreeper, + eAward_socialPost, + + // 4J Stu - Does not map to any Xbox achievements + eAward_snipeSkeleton, + eAward_diamonds, + eAward_portal, + eAward_ghast, + eAward_blazeRod, + eAward_potion, + eAward_theEnd, + eAward_winGame, + eAward_enchantments, + eAward_overkill, + eAward_bookcase, + + eAward_Max, +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Xbox_BuildVer.h b/Minecraft.Client/Durango/Xbox_BuildVer.h new file mode 100644 index 0000000..1fc3e4c --- /dev/null +++ b/Minecraft.Client/Durango/Xbox_BuildVer.h @@ -0,0 +1,53 @@ + +#pragma once + + +#define VER_PRODUCTMAJORVERSION 0 +#define VER_PRODUCTMINORVERSION 0 +#define VER_PRODUCTBUILD 170 +#define VER_PRODUCTBUILD_QFE 0 + +#define VER_FILEVERSION_STRING "1.1" +#define VER_PRODUCTVERSION_STRING VER_FILEVERSION_STRING +#define VER_FILEVERSION_STRING_W L"1.1" +#define VER_PRODUCTVERSION_STRING_W VER_FILEVERSION_STRING_W + +#define VER_FILEBETA_STR "" +#undef VER_FILEVERSION +#define VER_FILEVERSION VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION, VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE +#define VER_PRODUCTVERSION VER_PRODUCTMAJORVERSION, VER_PRODUCTMINORVERSION, VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE + +#if (VER_PRODUCTBUILD < 10) +#define VER_FILEBPAD "000" +#define VER_FILEBPAD_W L"000" +#elif (VER_PRODUCTBUILD < 100) +#define VER_FILEBPAD "00" +#define VER_FILEBPAD_W L"00" +#elif (VER_PRODUCTBUILD < 1000) +#define VER_FILEBPAD "0" +#define VER_FILEBPAD_W L"0" +#else +#define VER_FILEBPAD +#define VER_FILEBPAD_W +#endif + +#define VER_WIDE_PREFIX(x) L##x + +#define VER_FILEVERSION_STR2(x,y) VER_FILEVERSION_STRING "." VER_FILEBPAD #x "." #y +#define VER_FILEVERSION_STR2_W(x,y) VER_FILEVERSION_STRING_W L"." VER_FILEBPAD_W VER_WIDE_PREFIX(#x) L"." VER_WIDE_PREFIX(#y) +#define VER_FILEVERSION_STR1(x,y) VER_FILEVERSION_STR2(x, y) +#define VER_FILEVERSION_STR1_W(x,y) VER_FILEVERSION_STR2_W(x, y) + +#undef VER_FILEVERSION_STR +#define VER_FILEVERSION_STR VER_FILEVERSION_STR1(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR1(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) + +#define VER_FILEVERSION_STR_W VER_FILEVERSION_STR1_W(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) +#define VER_PRODUCTVERSION_STR_W VER_FILEVERSION_STR1_W(VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE) + +#if (VER_PRODUCTBUILD_QFE >= 256) +#error "QFE number cannot exceed 255" +#endif + + + diff --git a/Minecraft.Client/Durango/Xbox_Debug_enum.h b/Minecraft.Client/Durango/Xbox_Debug_enum.h new file mode 100644 index 0000000..685cf3f --- /dev/null +++ b/Minecraft.Client/Durango/Xbox_Debug_enum.h @@ -0,0 +1,36 @@ +#pragma once + +enum eDebugSetting +{ + eDebugSetting_LoadSavesFromDisk, + eDebugSetting_WriteSavesToDisk, + eDebugSetting_InterfaceOff, + eDebugSetting_Safearea, + eDebugSetting_MobsDontAttack, + eDebugSetting_FreezeTime, + eDebugSetting_DisableWeather, + eDebugSetting_CraftAnything, + eDebugSetting_UseDpadForDebug, + eDebugSetting_MobsDontTick, + eDebugSetting_InstantDestroy, + eDebugSetting_HandRenderingOff, + eDebugSetting_RemoveAllPlayerData, + eDebugSetting_DebugLeaderboards, + eDebugSetting_TipsAlwaysOn, + //eDebugSetting_LightDarkBackground, + eDebugSetting_RegularLightning, + eDebugSetting_Max, +}; + +enum eDebugButton +{ + eDebugButton_Theme=0, + eDebugButton_Avatar_Item_1, + eDebugButton_Avatar_Item_2, + eDebugButton_Avatar_Item_3, + eDebugButton_Gamerpic_1, + eDebugButton_Gamerpic_2, + eDebugButton_CheckTips, + eDebugButton_WipeLeaderboards, + eDebugButton_Max, +}; \ No newline at end of file diff --git a/Minecraft.Client/Durango/Xbox_Utils.cpp b/Minecraft.Client/Durango/Xbox_Utils.cpp new file mode 100644 index 0000000..ab6186b --- /dev/null +++ b/Minecraft.Client/Durango/Xbox_Utils.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" + +//-------------------------------------------------------------------------------------- +// Name: DebugSpewV() +// Desc: Internal helper function +//-------------------------------------------------------------------------------------- +#ifndef _CONTENT_PACKAGE +static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList ) +{ +#ifdef __PS3__ + assert(0); +#else + CHAR str[2048]; + // Use the secure CRT to avoid buffer overruns. Specify a count of + // _TRUNCATE so that too long strings will be silently truncated + // rather than triggering an error. + _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList ); + OutputDebugStringA( str ); +#endif +} +#endif + +//-------------------------------------------------------------------------------------- +// Name: DebugSpew() +// Desc: Prints formatted debug spew +//-------------------------------------------------------------------------------------- +#ifdef _Printf_format_string_ // VC++ 2008 and later support this annotation +VOID CDECL DebugSpew( _In_z_ _Printf_format_string_ const CHAR* strFormat, ... ) +#else +VOID CDECL DebugPrintf( const CHAR* strFormat, ... ) +#endif +{ +#ifndef _CONTENT_PACKAGE + va_list pArgList; + va_start( pArgList, strFormat ); + DebugSpewV( strFormat, pArgList ); + va_end( pArgList ); +#endif +} + diff --git a/Minecraft.Client/Durango/manifest.xml b/Minecraft.Client/Durango/manifest.xml new file mode 100644 index 0000000..690ef41 --- /dev/null +++ b/Minecraft.Client/Durango/manifest.xml @@ -0,0 +1,194 @@ + + + + + + + Minecraft: Xbox One Edition + Microsoft Studios + StoreLogo.png + Minecraft + + + + 6.2 + 6.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ESRB:E10 + PEGI:7 + PEGI-PT:6 + FPB:PG + USK:6 + COB-AU:PG + OFLC-NZ:PG + DJCTQ:L + CERO:A + GRB:All + CSRR:P + PCBP:6 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Durango/targetver.h b/Minecraft.Client/Durango/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/Minecraft.Client/Durango/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/Minecraft.Client/EchantmentTableParticle.cpp b/Minecraft.Client/EchantmentTableParticle.cpp new file mode 100644 index 0000000..5af7ef3 --- /dev/null +++ b/Minecraft.Client/EchantmentTableParticle.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "..\Minecraft.World\JavaMath.h" +#include "EchantmentTableParticle.h" + +EchantmentTableParticle::EchantmentTableParticle(Level *level, double x, double y, double z, double xd, double yd, double zd) : Particle(level, x, y, z, xd, yd, zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + this->xStart = this->x = x; + this->yStart = this->y = y; + this->zStart = this->z = z; + + unsigned int clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_EnchantmentTable ); //0xE5E5FF + double r = ( (clr>>16)&0xFF )/255.0f, g = ( (clr>>8)&0xFF )/255.0, b = ( clr&0xFF )/255.0; + + float br = random->nextFloat() * 0.6f + 0.4f; + rCol = r * br; + gCol = g * br; + bCol = b * br; + + oSize = size = random->nextFloat() * 0.5f + 0.2f; + + lifetime = (int) (Math::random() * 10) + 30; + noPhysics = true; + setMiscTex( (int) (Math::random() * 26 + 1 + 14 * 16) ); +} + +int EchantmentTableParticle::getLightColor(float a) +{ + int br = Particle::getLightColor(a); + + float pos = age / (float) lifetime; + pos = pos * pos; + pos = pos * pos; + + int br1 = (br) & 0xff; + int br2 = (br >> 16) & 0xff; + br2 += (int) (pos * 15 * 16); + if (br2 > 15 * 16) br2 = 15 * 16; + return br1 | br2 << 16; +} + +float EchantmentTableParticle::getBrightness(float a) +{ + float br = Particle::getBrightness(a); + float pos = age / (float) lifetime; + pos = pos * pos; + pos = pos * pos; + return br * (1 - pos) + pos; +} + +void EchantmentTableParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + float pos = age / (float) lifetime; + + pos = 1 - pos; + + float pp = 1 - pos; + pp = pp * pp; + pp = pp * pp; + x = xStart + xd * pos; + y = yStart + yd * pos - pp * 1.2f; + z = zStart + zd * pos; + + if (age++ >= lifetime) remove(); +} \ No newline at end of file diff --git a/Minecraft.Client/EchantmentTableParticle.h b/Minecraft.Client/EchantmentTableParticle.h new file mode 100644 index 0000000..98b027c --- /dev/null +++ b/Minecraft.Client/EchantmentTableParticle.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Particle.h" + +class Level; + +class EchantmentTableParticle : public Particle +{ +private: + float oSize; + double xStart, yStart, zStart; + +public: + virtual eINSTANCEOF GetType() { return eTYPE_ENCHANTMENTTABLEPARTICLE; } + + EchantmentTableParticle(Level *level, double x, double y, double z, double xd, double yd, double zd); + + virtual int getLightColor(float a); + virtual float getBrightness(float a); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/EditBox.cpp b/Minecraft.Client/EditBox.cpp new file mode 100644 index 0000000..54ee62e --- /dev/null +++ b/Minecraft.Client/EditBox.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" +#include "EditBox.h" +#include "..\Minecraft.World\SharedConstants.h" + +EditBox::EditBox(Screen *screen, Font *font, int x, int y, int width, int height, const wstring& value) +{ + // 4J - added initialisers + maxLength = 0; + frame = 0; + + this->screen = screen; + this->font = font; + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->setValue(value); +} + +void EditBox::setValue(const wstring& value) +{ + this->value = value; +} + +wstring EditBox::getValue() +{ + return value; +} + +void EditBox::tick() +{ + frame++; +} + +void EditBox::keyPressed(wchar_t ch, int eventKey) +{ + if (!active || !inFocus) { + return; + } + + + if (ch == 9) + { + screen->tabPressed(); + } +/* 4J removed + if (ch == 22) + { + String msg = Screen.getClipboard(); + if (msg == null) msg = ""; + int toAdd = 32 - value.length(); + if (toAdd > msg.length()) toAdd = msg.length(); + if (toAdd > 0) { + value += msg.substring(0, toAdd); + } + } + */ + + if (eventKey == Keyboard::KEY_BACK && value.length() > 0) + { + value = value.substr(0, value.length() - 1); + } + if (SharedConstants::acceptableLetters.find(ch) != wstring::npos && (value.length() < maxLength || maxLength == 0)) + { + value += ch; + } + +} + +void EditBox::mouseClicked(int mouseX, int mouseY, int buttonNum) +{ + bool newFocus = active && (mouseX >= x && mouseX < (x + width) && mouseY >= y && mouseY < (y + height)); + focus(newFocus); +} + +void EditBox::focus(bool newFocus) +{ + if (newFocus && !inFocus) + { + // reset the underscore counter to give quicker selection feedback + frame = 0; + } + inFocus = newFocus; +} + +void EditBox::render() +{ + fill(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); + fill(x, y, x + width, y + height, 0xff000000); + + if (active) + { + bool renderUnderscore = inFocus && (frame / 6 % 2 == 0); + drawString(font, value + (renderUnderscore ? L"_" : L""), x + 4, y + (height - 8) / 2, 0xe0e0e0); + } + else + { + drawString(font, value, x + 4, y + (height - 8) / 2, 0x707070); + } +} + +void EditBox::setMaxLength(int maxLength) +{ + this->maxLength = maxLength; +} + +int EditBox::getMaxLength() +{ + return maxLength; +} \ No newline at end of file diff --git a/Minecraft.Client/EditBox.h b/Minecraft.Client/EditBox.h new file mode 100644 index 0000000..9e24553 --- /dev/null +++ b/Minecraft.Client/EditBox.h @@ -0,0 +1,36 @@ +#pragma once +#include "GuiComponent.h" +using namespace std; +class Font; +class Screen; + +class EditBox : public GuiComponent +{ +private: + Font *font; + int x; + int y; + int width; + int height; + wstring value; + unsigned int maxLength; + int frame; + +public: + bool inFocus; + bool active; +private: + Screen *screen; + +public: + EditBox(Screen *screen, Font *font, int x, int y, int width, int height, const wstring& value); + void setValue(const wstring& value); + wstring getValue(); + void tick(); + void keyPressed(wchar_t ch, int eventKey); + void mouseClicked(int mouseX, int mouseY, int buttonNum); + void focus(bool newFocus); + void render(); + void setMaxLength(int maxLength); + int getMaxLength(); +}; \ No newline at end of file diff --git a/Minecraft.Client/EnchantTableRenderer.cpp b/Minecraft.Client/EnchantTableRenderer.cpp new file mode 100644 index 0000000..43f2b04 --- /dev/null +++ b/Minecraft.Client/EnchantTableRenderer.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "BookModel.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\Minecraft.World\Mth.h" +#include "EnchantTableRenderer.h" + +EnchantTableRenderer::EnchantTableRenderer() +{ + bookModel = new BookModel(); +} + +EnchantTableRenderer::~EnchantTableRenderer() +{ + delete bookModel; +} + +void EnchantTableRenderer::render(shared_ptr _table, double x, double y, double z, float a, bool setColor, float alpha, bool useCompiled) +{ + // 4J Convert as we aren't using a templated class + shared_ptr table = dynamic_pointer_cast(_table); + +#ifdef __PSVITA__ + // AP - the book pages are made with 0 depth so the front and back polys are at the same location. This can cause z-fighting if culling is disabled which can sometimes happen + // depending on what object was last seen so make sure culling is always enabled. Should this be a problem for other platforms? + glEnable(GL_CULL_FACE); +#endif + + glPushMatrix(); + glTranslatef((float) x + 0.5f, (float) y + 12 / 16.0f, (float) z + 0.5f); + + float tt = table->time + a; + + glTranslatef(0, 0.1f + sin(tt * 0.1f) * 0.01f, 0); + float orot = (table->rot - table->oRot); + while (orot >= PI) + orot -= PI * 2; + while (orot < -PI) + orot += PI * 2; + + float yRot = table->oRot + orot * a; + + glRotatef(-yRot * 180 / PI, 0, 1, 0); + glRotatef(80, 0, 0, 1); + bindTexture(TN_ITEM_BOOK); // 4J was "/item/book.png" + + float ff1 = table->oFlip + (table->flip - table->oFlip) * a + 0.25f; + float ff2 = table->oFlip + (table->flip - table->oFlip) * a + 0.75f; + ff1 = (ff1 - Mth::fastFloor(ff1)) * 1.6f - 0.3f; + ff2 = (ff2 - Mth::fastFloor(ff2)) * 1.6f - 0.3f; + + if (ff1 < 0) ff1 = 0; + if (ff2 < 0) ff2 = 0; + if (ff1 > 1) ff1 = 1; + if (ff2 > 1) ff2 = 1; + + float o = table->oOpen + (table->open - table->oOpen) * a; + bookModel->render(nullptr, tt, ff1, ff2, o, 0, 1 / 16.0f,true); + glPopMatrix(); +} diff --git a/Minecraft.Client/EnchantTableRenderer.h b/Minecraft.Client/EnchantTableRenderer.h new file mode 100644 index 0000000..0b738ca --- /dev/null +++ b/Minecraft.Client/EnchantTableRenderer.h @@ -0,0 +1,19 @@ +#pragma once + +#include "TileEntityRenderer.h" + +class BookModel; + +class EnchantTableRenderer : public TileEntityRenderer +{ + friend class CXuiCtrlEnchantmentBook; + friend class UIControl_EnchantmentBook; +private: + BookModel *bookModel; + +public: + EnchantTableRenderer(); + ~EnchantTableRenderer(); + + virtual void render(shared_ptr _table, double x, double y, double z, float a, bool setColor, float alpha=1.0f, bool useCompiled = true); +}; diff --git a/Minecraft.Client/EnderChestRenderer.cpp b/Minecraft.Client/EnderChestRenderer.cpp new file mode 100644 index 0000000..2996b65 --- /dev/null +++ b/Minecraft.Client/EnderChestRenderer.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "ModelPart.h" +#include "EnderChestRenderer.h" + +void EnderChestRenderer::render(shared_ptr _chest, double x, double y, double z, float a, bool setColor, float alpha, bool useCompiled) +{ + // 4J Convert as we aren't using a templated class + shared_ptr chest = dynamic_pointer_cast(_chest); + + int data = 0; + + if (chest->hasLevel()) + { + data = chest->getData(); + } + + bindTexture(TN_TILE_ENDER_CHEST); //"/item/enderchest.png"); + + glPushMatrix(); + glEnable(GL_RESCALE_NORMAL); + //glColor4f(1, 1, 1, 1); + if( setColor ) glColor4f(1, 1, 1, alpha); + glTranslatef((float) x, (float) y + 1, (float) z + 1); + glScalef(1, -1, -1); + + glTranslatef(0.5f, 0.5f, 0.5f); + int rot = 0; + if (data == 2) rot = 180; + if (data == 3) rot = 0; + if (data == 4) rot = 90; + if (data == 5) rot = -90; + + // if (data == 2) { + // glTranslatef(1, 0, 0); + // } + // if (data == 5) { + // glTranslatef(0, 0, -1); + // } + glRotatef(rot, 0, 1, 0); + glTranslatef(-0.5f, -0.5f, -0.5f); + + float open = chest->oOpenness + (chest->openness - chest->oOpenness) * a; + open = 1 - open; + open = 1 - open * open * open; + + chestModel.lid->xRot = -(open * PI / 2); + chestModel.render(useCompiled); + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); + if( setColor ) glColor4f(1, 1, 1, 1); +} diff --git a/Minecraft.Client/EnderChestRenderer.h b/Minecraft.Client/EnderChestRenderer.h new file mode 100644 index 0000000..d0521ae --- /dev/null +++ b/Minecraft.Client/EnderChestRenderer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "TileEntityRenderer.h" +#include "ChestModel.h" + +class EnderChestRenderer : public TileEntityRenderer +{ +private: + ChestModel chestModel; + +public: + void render(shared_ptr _chest, double x, double y, double z, float a, bool setColor, float alpha=1.0f, bool useCompiled = true); // 4J added setColor param +}; diff --git a/Minecraft.Client/EnderCrystalModel.cpp b/Minecraft.Client/EnderCrystalModel.cpp new file mode 100644 index 0000000..fde03cd --- /dev/null +++ b/Minecraft.Client/EnderCrystalModel.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "EnderCrystalModel.h" + + + +EnderCrystalModel::EnderCrystalModel(float g) +{ + glass = new ModelPart(this, L"glass"); + glass->texOffs(0, 0)->addBox(-4, -4, -4, 8, 8, 8); + + cube = new ModelPart(this, L"cube"); + cube->texOffs(32, 0)->addBox(-4, -4, -4, 8, 8, 8); + + base = new ModelPart(this, L"base"); + base->texOffs(0, 16)->addBox(-6, 0, -6, 12, 4, 12); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + glass->compile(1.0f/16.0f); + cube->compile(1.0f/16.0f); + base->compile(1.0f/16.0f); +} + + +void EnderCrystalModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + glPushMatrix(); + glScalef(2, 2, 2); + glTranslatef(0, -0.5f, 0); + base->render(scale,usecompiled); + glRotatef(r, 0, 1, 0); + glTranslatef(0, 0.8f + bob, 0); + glRotatef(60, 0.7071f, 0, 0.7071f); + glass->render(scale,usecompiled); + float ss = 14 / 16.0f; + glScalef(ss, ss, ss); + glRotatef(60, 0.7071f, 0, 0.7071f); + glRotatef(r, 0, 1, 0); + glass->render(scale,usecompiled); + glScalef(ss, ss, ss); + glRotatef(60, 0.7071f, 0, 0.7071f); + glRotatef(r, 0, 1, 0); + cube->render(scale,usecompiled); + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/EnderCrystalModel.h b/Minecraft.Client/EnderCrystalModel.h new file mode 100644 index 0000000..71f1db1 --- /dev/null +++ b/Minecraft.Client/EnderCrystalModel.h @@ -0,0 +1,18 @@ +#pragma once +#include "Model.h" +#include "ModelPart.h" + +class EnderCrystalModel : public Model +{ +public: + static const int MODEL_ID = 1; + +private: + ModelPart *cube; + ModelPart *glass; + ModelPart *base; + +public: + EnderCrystalModel(float g); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +}; \ No newline at end of file diff --git a/Minecraft.Client/EnderCrystalRenderer.cpp b/Minecraft.Client/EnderCrystalRenderer.cpp new file mode 100644 index 0000000..452206b --- /dev/null +++ b/Minecraft.Client/EnderCrystalRenderer.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "EnderCrystalModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "EnderCrystalRenderer.h" + +EnderCrystalRenderer::EnderCrystalRenderer() +{ + currentModel = -1; + this->shadowRadius = 0.5f; +} + +void EnderCrystalRenderer::render(shared_ptr _crystal, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type EnderCrystal rather than shared_ptr we have here - + // do some casting around instead + shared_ptr crystal = dynamic_pointer_cast(_crystal); + if (currentModel != EnderCrystalModel::MODEL_ID) + { + model = new EnderCrystalModel(0); + currentModel = EnderCrystalModel::MODEL_ID; + } + + + float tt = crystal->time + a; + glPushMatrix(); + glTranslatef((float) x, (float) y, (float) z); + bindTexture(TN_MOB_ENDERDRAGON_ENDERCRYSTAL); // 4J was "/mob/enderdragon/crystal.png" + float hh = sin(tt * 0.2f) / 2 + 0.5f; + hh = hh * hh + hh; + model->render(crystal, 0, tt * 3, hh * 0.2f, 0, 0, 1 / 16.0f, true); + + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/EnderCrystalRenderer.h b/Minecraft.Client/EnderCrystalRenderer.h new file mode 100644 index 0000000..de5dc82 --- /dev/null +++ b/Minecraft.Client/EnderCrystalRenderer.h @@ -0,0 +1,17 @@ +#pragma once + +#include "EntityRenderer.h" + +class Model; + +class EnderCrystalRenderer : public EntityRenderer +{ +private: + int currentModel; + Model *model; + +public: + EnderCrystalRenderer(); + + virtual void render(shared_ptr _crystal, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/EnderDragonRenderer.cpp b/Minecraft.Client/EnderDragonRenderer.cpp new file mode 100644 index 0000000..8c531d4 --- /dev/null +++ b/Minecraft.Client/EnderDragonRenderer.cpp @@ -0,0 +1,267 @@ +#include "stdafx.h" +#include "DragonModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "Tesselator.h" +#include "Lighting.h" +#include "EnderDragonRenderer.h" + +shared_ptr EnderDragonRenderer::bossInstance; +int EnderDragonRenderer::currentModel; + +EnderDragonRenderer::EnderDragonRenderer() : MobRenderer(new DragonModel(0), 0.5f) +{ + currentModel = 0; + dragonModel = (DragonModel *) model; + this->setArmor(model); +} + +void EnderDragonRenderer::setupRotations(shared_ptr _mob, float bob, float bodyRot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + + // 4J - reorganised a bit so we can free allocations + double lpComponents[3]; + doubleArray lp = doubleArray(lpComponents, 3); + mob->getLatencyPos(lp, 7, a); + float yr = lp[0]; + //mob->getLatencyPos(lp, 5, a); + //float rot2 = lp[1]; + //mob->getLatencyPos(lp, 10,a); + //rot2 -= lp[1]; + float rot2 = mob->getTilt(a); + + glRotatef(-yr, 0, 1, 0); + + glRotatef(rot2, 1, 0, 0); + //glRotatef(rot2 * 10, 1, 0, 0); + + glTranslatef(0, 0, 1); + if (mob->deathTime > 0) + { + float fall = (mob->deathTime + a - 1) / 20.0f * 1.6f; + fall = sqrt(fall); + if (fall > 1) fall = 1; + glRotatef(fall * getFlipDegrees(mob), 0, 0, 1); + } +} + +void EnderDragonRenderer::renderModel(shared_ptr _mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + + if (mob->dragonDeathTime > 0) + { + float tt = (mob->dragonDeathTime / 200.0f); + glDepthFunc(GL_LEQUAL); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, tt); + bindTexture(mob->customTextureUrl, TN_MOB_ENDERDRAGON_SHUFFLE); // 4J was "/mob/enderdragon/shuffle.png" + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); + glAlphaFunc(GL_GREATER, 0.1f); + + glDepthFunc(GL_EQUAL); + } + + + bindTexture(mob->customTextureUrl, mob->getTexture()); + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); + + if (mob->hurtTime > 0) + { + glDepthFunc(GL_EQUAL); + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1, 0, 0, 0.5f); +#ifdef __PSVITA__ + // AP - not sure that the usecompiled flag is supposed to be false. This makes it really slow on vita. Making it true still seems to look the same + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); +#else + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, false); +#endif + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + } +} + +void EnderDragonRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + EnderDragonRenderer::bossInstance = mob; + if (currentModel != DragonModel::MODEL_ID) + { + model = new DragonModel(0); + currentModel = DragonModel::MODEL_ID; + } + MobRenderer::render(mob, x, y, z, rot, a); + if (mob->nearestCrystal != NULL) + { + float tt = mob->nearestCrystal->time + a; + float hh = sin(tt * 0.2f) / 2 + 0.5f; + hh = (hh * hh + hh) * 0.2f; + + float xd = (float) (mob->nearestCrystal->x - mob->x - (mob->xo - mob->x) * (1 - a)); + float yd = (float) (hh + mob->nearestCrystal->y - 1 - mob->y - (mob->yo - mob->y) * (1 - a)); + float zd = (float) (mob->nearestCrystal->z - mob->z - (mob->zo - mob->z) * (1 - a)); + + float sdd = sqrt(xd * xd + zd * zd); + float dd = sqrt(xd * xd + yd * yd + zd * zd); + + // this fixes a problem when the dragon is hit and the beam goes black because the diffuse colour isn't being reset in MobRenderer::render + glColor4f(1, 1, 1, 1); + + glPushMatrix(); + glTranslatef((float) x, (float) y + 2, (float) z); + glRotatef((float) (-atan2(zd, xd)) * 180.0f / PI - 90.0f, 0, 1, 0); + glRotatef((float) (-atan2(sdd, yd)) * 180.0f / PI - 90.0f, 1, 0, 0); + + // 4J-PB - Rotating the healing beam too + static float fRot=0.0f; + glRotatef(fRot, 0, 0, 1); + fRot+=0.5f; // 4J - rate of rotation changed from 5.0 to 0.5 for photosensitivity reasons + if(fRot>=360.0f) + { + fRot=0.0f; + } + + Tesselator *t = Tesselator::getInstance(); + Lighting::turnOff(); + glDisable(GL_CULL_FACE); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA); + + bindTexture(TN_MOB_ENDERDRAGON_BEAM); // 4J was "/mob/enderdragon/beam.png" + + glShadeModel(GL_SMOOTH); + + float v0 = 0 - (mob->tickCount + a) * 0.005f; // 4J - rate of movement changed from 0.01 to 0.005 for photosensitivity reasons + float v1 = sqrt(xd * xd + yd * yd + zd * zd) / 32.0f - (mob->tickCount + a) * 0.005f; + + t->begin(GL_TRIANGLE_STRIP); + + int steps = 8; + for (int i = 0; i <= steps; i++) + { + double d=i % steps * PI * 2 / steps; + float s = sin(i % steps * PI * 2 / steps) * 0.75f; + float c = cos(i % steps * PI * 2 / steps) * 0.75f; + float u = i % steps * 1.0f / steps; + //t->color(0x000000); + t->vertexUV(s * 0.2f, c * 0.2f, 0, u, v1); + //t->color(0xffffff); + t->vertexUV(s, c, dd, u, v0); + } + + t->end(); + glEnable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + glDisable(GL_BLEND); + + glPopMatrix(); + Lighting::turnOn(); + } +} + +void EnderDragonRenderer::additionalRendering(shared_ptr _mob, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + MobRenderer::additionalRendering(mob, a); + Tesselator *t = Tesselator::getInstance(); + + if (mob->dragonDeathTime > 0) + { + Lighting::turnOff(); + float tt = ((mob->dragonDeathTime + a) / 200.0f); + float overDrive = 0; + if (tt > 0.8f) + { + overDrive = (tt - 0.8f) / 0.2f; + } + + Random random(432); + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDisable(GL_ALPHA_TEST); + glEnable(GL_CULL_FACE); + glDepthMask(false); + glPushMatrix(); + glTranslatef(0, -1, -2); + for (int i = 0; i < (tt + tt * tt) / 2 * 60; i++) + { + glRotatef(random.nextFloat() * 360, 1, 0, 0); + glRotatef(random.nextFloat() * 360, 0, 1, 0); + glRotatef(random.nextFloat() * 360, 0, 0, 1); + glRotatef(random.nextFloat() * 360, 1, 0, 0); + glRotatef(random.nextFloat() * 360, 0, 1, 0); + glRotatef(random.nextFloat() * 360 + tt * 90, 0, 0, 1); + t->begin(GL_TRIANGLE_FAN); + float dist = random.nextFloat() * 20 + 5 + overDrive * 10; + float w = random.nextFloat() * 2 + 1 + overDrive * 2; + t->color(0xffffff, (int) (255 * (1 - overDrive))); + t->vertex(0, 0, 0); + t->color(0xff00ff, 0); + t->vertex(-0.866 * w, dist, -0.5f * w); + t->vertex(+0.866 * w, dist, -0.5f * w); + t->vertex(0, dist, 1 * w); + t->vertex(-0.866 * w, dist, -0.5f * w); + t->end(); + } + glPopMatrix(); + glDepthMask(true); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glShadeModel(GL_FLAT); + glColor4f(1, 1, 1, 1); + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + Lighting::turnOn(); + } + +} + +int EnderDragonRenderer::prepareArmor(shared_ptr _mob, int layer, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr mob = dynamic_pointer_cast(_mob); + + if (layer == 1) + { + glDepthFunc(GL_LEQUAL); + } + if (layer != 0) return -1; + + bindTexture(TN_MOB_ENDERDRAGON_ENDEREYES); // 4J was "/mob/enderdragon/ender_eyes.png" + float br = 1; + glEnable(GL_BLEND); + // 4J Stu - We probably don't need to do this on 360 either (as we force it back on the renderer) + // However we do want it off for other platforms that don't force it on in the render lib CBuff handling + // Several texture packs have fully transparent bits that break if this is off +#ifdef _XBOX + glDisable(GL_ALPHA_TEST); +#endif + glBlendFunc(GL_ONE, GL_ONE); + glDisable(GL_LIGHTING); + glDepthFunc(GL_EQUAL); + + if (SharedConstants::TEXTURE_LIGHTING) + { + int col = 0xf0f0; + int u = col % 65536; + int v = col / 65536; + + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + } + + glEnable(GL_LIGHTING); + glColor4f(1, 1, 1, br); + return 1; +} diff --git a/Minecraft.Client/EnderDragonRenderer.h b/Minecraft.Client/EnderDragonRenderer.h new file mode 100644 index 0000000..ab508d9 --- /dev/null +++ b/Minecraft.Client/EnderDragonRenderer.h @@ -0,0 +1,36 @@ +#pragma once + +#include "MobRenderer.h" + +#ifdef _XBOX +class EnderDragon; +#endif +class DragonModel; + +class EnderDragonRenderer : public MobRenderer +{ +public: + static shared_ptr bossInstance; + +private: + static int currentModel; + +protected: + DragonModel *dragonModel; + +public: + EnderDragonRenderer(); + +protected: + virtual void setupRotations(shared_ptr _mob, float bob, float bodyRot, float a); + +protected: + void renderModel(shared_ptr _mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale); + +public: + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); + +protected: + virtual void additionalRendering(shared_ptr _mob, float a); + virtual int prepareArmor(shared_ptr _mob, int layer, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/EnderParticle.cpp b/Minecraft.Client/EnderParticle.cpp new file mode 100644 index 0000000..3889bf9 --- /dev/null +++ b/Minecraft.Client/EnderParticle.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "EnderParticle.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Random.h" + +// 4J Stu - This class was originally "PortalParticle" but I have split the two uses of the particle +// End creatures/items (e.g. EnderMan, EyeOfEnder, etc) use this particle + +EnderParticle::EnderParticle(Level *level, double x, double y, double z, double xd, double yd, double zd) : Particle(level, x, y, z, xd, yd, zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + this->xStart = this->x = x; + this->yStart = this->y = y; + this->zStart = this->z = z; + + // 4J-JEV: Set particle colour from colour-table. + unsigned int col = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_Ender ); //0xE54CFF + rCol = ( (col>>16)&0xFF )/255.0f, gCol = ( (col>>8)&0xFF )/255.0, bCol = ( col&0xFF )/255.0; + + float br = random->nextFloat() * 0.6f + 0.4f; + rCol *= br; gCol *= br; bCol *= br; + + //rCol = gCol = bCol = 1.0f*br; + //gCol *= 0.3f; + //rCol *= 0.9f; + + oSize = size = random->nextFloat()*0.2f+0.5f; + + lifetime = (int) (Math::random()*10) + 40; + noPhysics = true; + setMiscTex((int)(Math::random()*8)); +} + +void EnderParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float s = (age + a) / (float) lifetime; + s = 1-s; + s = s*s; + s = 1-s; + size = oSize * (s); + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +// 4J - brought forward from 1.8.2 +int EnderParticle::getLightColor(float a) +{ + int br = Particle::getLightColor(a); + + float pos = age/(float)lifetime; + pos = pos*pos; + pos = pos*pos; + + int br1 = (br) & 0xff; + int br2 = (br >> 16) & 0xff; + br2 += (int) (pos * 15 * 16); + if (br2 > 15 * 16) br2 = 15 * 16; + return br1 | br2 << 16; +} + +float EnderParticle::getBrightness(float a) +{ + float br = Particle::getBrightness(a); + float pos = age/(float)lifetime; + pos = pos*pos; + pos = pos*pos; + return br*(1-pos)+pos; +} + +void EnderParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + float pos = age/(float)lifetime; + float a = pos; + pos = -pos+pos*pos*2; +// pos = pos*pos; +// pos = pos*pos; + pos = 1-pos; + + x = xStart+xd*pos; + y = yStart+yd*pos+(1-a); + z = zStart+zd*pos; + + +// spd+=0.002/lifetime*age; + + if (age++ >= lifetime) remove(); + +// move(xd*spd, yd*spd, zd*spd); +} diff --git a/Minecraft.Client/EnderParticle.h b/Minecraft.Client/EnderParticle.h new file mode 100644 index 0000000..56b75b6 --- /dev/null +++ b/Minecraft.Client/EnderParticle.h @@ -0,0 +1,21 @@ +#pragma once +#include "Particle.h" + +// 4J Stu - This class was originally "PortalParticle" but I have split the two uses of the particle +// End creatures/items (e.g. EnderMan, EyeOfEnder, etc) use this particle + +class EnderParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_ENDERPARTICLE; } +private: + float oSize; + double xStart, yStart, zStart; + +public: + EnderParticle(Level *level, double x, double y, double z, double xd, double yd, double zd); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual int getLightColor(float a); // 4J - brought forward from 1.8.2 + virtual float getBrightness(float a); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/EndermanModel.cpp b/Minecraft.Client/EndermanModel.cpp new file mode 100644 index 0000000..02b9a53 --- /dev/null +++ b/Minecraft.Client/EndermanModel.cpp @@ -0,0 +1,121 @@ +#include "stdafx.h" +#include "EndermanModel.h" +#include "ModelPart.h" + +EndermanModel::EndermanModel() : HumanoidModel(0, -14, 64, 32) +{ + carrying = false; + creepy = false; + + float yOffset = -14.0f; + float g = 0; + + delete hair; + hair = new ModelPart(this, 0, 16); + hair->addBox(-4.0f, -8.0f, -4.0f, 8, 8, 8, g - 0.5f); // Head + hair->setPos(0.0f, 0.0f + yOffset, 0.0f); + + delete body; + body = new ModelPart(this, 32, 16); + body->addBox(-4.0f, 0.0f, -2.0f, 8, 12, 4, g); // Body + body->setPos(0.0f, 0.0f + yOffset, 0.0f); + + + delete arm0; + arm0 = new ModelPart(this, 56, 0); + arm0->addBox(-1.0f, -2.0f, -1.0f, 2, 30, 2, g); // Arm0 + arm0->setPos(-3.0f, 2.0f + yOffset, 0.0f); + + + delete arm1; + arm1 = new ModelPart(this, 56, 0); + arm1->bMirror = true; + arm1->addBox(-1.0f, -2.0f, -1.0f, 2, 30, 2, g); // Arm1 + arm1->setPos(5.0f, 2.0f + yOffset, 0.0f); + + delete leg0; + leg0 = new ModelPart(this, 56, 0); + leg0->addBox(-1.0f, 0.0f, -1.0f, 2, 30, 2, g); // Leg0 + leg0->setPos(-2.0f, 12.0f + yOffset, 0.0f); + + delete leg1; + leg1 = new ModelPart(this, 56, 0); + leg1->bMirror = true; + leg1->addBox(-1.0f, 0.0f, -1.0f, 2, 30, 2, g); // Leg1 + leg1->setPos(2.0f, 12.0f + yOffset, 0.0f); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + body->compile(1.0f/16.0f); + arm0->compile(1.0f/16.0f); + arm1->compile(1.0f/16.0f); + leg0->compile(1.0f/16.0f); + leg1->compile(1.0f/16.0f); + hair->compile(1.0f/16.0f); + +} + +void EndermanModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + HumanoidModel::setupAnim(time, r, bob, yRot, xRot, scale, uiBitmaskOverrideAnim); + + head->visible = true; + + float yOffs = -14.0f; + body->xRot = 0.0f; + body->y = yOffs; + body->z = -0.0f; + + leg0->xRot -= 0.0f; + leg1->xRot -= 0.0f; + + arm0->xRot *= 0.5f; + arm1->xRot *= 0.5f; + leg0->xRot *= 0.5f; + leg1->xRot *= 0.5f; + + float max = 0.4f; + if (arm0->xRot > +max) arm0->xRot = +max; + if (arm1->xRot > +max) arm1->xRot = +max; + if (arm0->xRot < -max) arm0->xRot = -max; + if (arm1->xRot < -max) arm1->xRot = -max; + if (leg0->xRot > +max) leg0->xRot = +max; + if (leg1->xRot > +max) leg1->xRot = +max; + if (leg0->xRot < -max) leg0->xRot = -max; + if (leg1->xRot < -max) leg1->xRot = -max; + + + if (carrying) + { + arm0->xRot = -0.5f; + arm1->xRot = -0.5f; + arm0->zRot = 0.05f; + arm1->zRot = -0.05f; + } + + arm0->z = -0.0f; + arm1->z = -0.0f; + leg0->z = -0.0f; + leg1->z = -0.0f; + + arm0->y = 2.0f + yOffs; + arm1->y = 2.0f + yOffs; + + leg0->y = +9.0f + yOffs; + leg1->y = +9.0f + yOffs; + + head->z = -0.0f; + head->y = +yOffs + 1; + + hair->x = head->x; + hair->y = head->y; + hair->z = head->z; + hair->xRot = head->xRot; + hair->yRot = head->yRot; + hair->zRot = head->zRot; + + if (creepy) + { + float amt = 1; + head->y -= (float) (amt * 5); + } +} \ No newline at end of file diff --git a/Minecraft.Client/EndermanModel.h b/Minecraft.Client/EndermanModel.h new file mode 100644 index 0000000..3760e79 --- /dev/null +++ b/Minecraft.Client/EndermanModel.h @@ -0,0 +1,13 @@ +#pragma once + +#include "HumanoidModel.h" + +class EndermanModel : public HumanoidModel +{ +public: + bool carrying; + bool creepy; + + EndermanModel(); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); +}; \ No newline at end of file diff --git a/Minecraft.Client/EndermanRenderer.cpp b/Minecraft.Client/EndermanRenderer.cpp new file mode 100644 index 0000000..8802376 --- /dev/null +++ b/Minecraft.Client/EndermanRenderer.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "EndermanRenderer.h" +#include "EndermanModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" + +EndermanRenderer::EndermanRenderer() : MobRenderer(new EndermanModel(), 0.5f) +{ + model = (EndermanModel *) MobRenderer::model; + this->setArmor(model); +} + +void EndermanRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Boat rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + + model->carrying = mob->getCarryingTile() > 0; + model->creepy = mob->isCreepy(); + + if (mob->isCreepy()) + { + double d = 0.02; + x += random.nextGaussian() * d; + z += random.nextGaussian() * d; + } + + MobRenderer::render(mob, x, y, z, rot, a); +} + +void EndermanRenderer::additionalRendering(shared_ptr _mob, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Boat rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + + MobRenderer::additionalRendering(_mob, a); + + if (mob->getCarryingTile() > 0) + { + glEnable(GL_RESCALE_NORMAL); + glPushMatrix(); + + float s = 8 / 16.0f; + glTranslatef(-0 / 16.0f, 11 / 16.0f, -12 / 16.0f); + s *= 1.00f; + glRotatef(20, 1, 0, 0); + glRotatef(45, 0, 1, 0); + glScalef(s, -s, s); + + + if (SharedConstants::TEXTURE_LIGHTING) + { + int col = mob->getLightColor(a); + int u = col % 65536; + int v = col / 65536; + + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + } + + glColor4f(1, 1, 1, 1); + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + tileRenderer->renderTile(Tile::tiles[mob->getCarryingTile()], mob->getCarryingData(), 1); + glPopMatrix(); + glDisable(GL_RESCALE_NORMAL); + } +} + +int EndermanRenderer::prepareArmor(shared_ptr _mob, int layer, float a) +{ + // 4J - original version used generics and thus had an input parameter of type Boat rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + + if (layer != 0) return -1; + + bindTexture(TN_MOB_ENDERMAN_EYES); // 4J was L"/mob/enderman_eyes.png" + float br = 1; + glEnable(GL_BLEND); + // 4J Stu - We probably don't need to do this on 360 either (as we force it back on the renderer) + // However we do want it off for other platforms that don't force it on in the render lib CBuff handling + // Several texture packs have fully transparent bits that break if this is off +#ifdef _XBOX + glDisable(GL_ALPHA_TEST); +#endif + glBlendFunc(GL_ONE, GL_ONE); + glDisable(GL_LIGHTING); + + if (mob->isInvisible()) glDepthMask(false); + else glDepthMask(true); + + if (SharedConstants::TEXTURE_LIGHTING) + { + int col = 0xf0f0; + int u = col % 65536; + int v = col / 65536; + + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + } + + glEnable(GL_LIGHTING); + glColor4f(1, 1, 1, br); + return 1; +} \ No newline at end of file diff --git a/Minecraft.Client/EndermanRenderer.h b/Minecraft.Client/EndermanRenderer.h new file mode 100644 index 0000000..6f5126b --- /dev/null +++ b/Minecraft.Client/EndermanRenderer.h @@ -0,0 +1,22 @@ +#pragma once + +#include "MobRenderer.h" + +class EnderMan; +class EndermanModel; + +class EndermanRenderer : public MobRenderer +{ +private: + EndermanModel *model; + Random random; + +public: + EndermanRenderer(); + + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); + virtual void additionalRendering(shared_ptr _mob, float a); + +protected: + virtual int prepareArmor(shared_ptr _mob, int layer, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/EntityRenderDispatcher.cpp b/Minecraft.Client/EntityRenderDispatcher.cpp new file mode 100644 index 0000000..dfc9a11 --- /dev/null +++ b/Minecraft.Client/EntityRenderDispatcher.cpp @@ -0,0 +1,280 @@ +#include "stdafx.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.entity.global.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\Minecraft.World\net.minecraft.world.entity.npc.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.alchemy.h" +#include "SpiderRenderer.h" +#include "PigRenderer.h" +#include "SheepRenderer.h" +#include "CowRenderer.h" +#include "WolfRenderer.h" +#include "ChickenRenderer.h" +#include "CreeperRenderer.h" +#include "SlimeRenderer.h" +#include "PlayerRenderer.h" +#include "GhastRenderer.h" +#include "SquidRenderer.h" +#include "MobRenderer.h" +#include "GiantMobRenderer.h" +#include "EntityRenderer.h" +#include "PaintingRenderer.h" +#include "ArrowRenderer.h" +#include "FireballRenderer.h" +#include "ItemRenderer.h" +#include "ItemSpriteRenderer.h" +#include "TntRenderer.h" +#include "FallingTileRenderer.h" +#include "MinecartRenderer.h" +#include "BoatRenderer.h" +#include "FishingHookRenderer.h" +#include "LightningBoltRenderer.h" +#include "HumanoidMobRenderer.h" +#include "DefaultRenderer.h" +#include "EndermanRenderer.h" +#include "ExperienceOrbRenderer.h" +#include "SilverfishRenderer.h" +#include "MushroomCowRenderer.h" +#include "SnowmanRenderer.h" +#include "LavaSlimeRenderer.h" +#include "VillagerRenderer.h" +#include "EnderDragonRenderer.h" +#include "EnderCrystalRenderer.h" +#include "BlazeRenderer.h" +#include "SpiderModel.h" +#include "PigModel.h" +#include "SheepModel.h" +#include "CowModel.h" +#include "WolfModel.h" +#include "ChickenModel.h" +#include "CreeperModel.h" +#include "SlimeModel.h" +#include "GhastModel.h" +#include "SquidModel.h" +#include "MinecartModel.h" +#include "BoatModel.h" +#include "HumanoidModel.h" +#include "SheepFurModel.h" +#include "SkeletonModel.h" +#include "Options.h" +#include "ItemFrameRenderer.h" +#include "OzelotRenderer.h" +#include "VillagerGolemRenderer.h" +#include "OzelotModel.h" +#include "ZombieRenderer.h" + +double EntityRenderDispatcher::xOff = 0.0; +double EntityRenderDispatcher::yOff = 0.0; +double EntityRenderDispatcher::zOff = 0.0; + +EntityRenderDispatcher *EntityRenderDispatcher::instance = NULL; + +void EntityRenderDispatcher::staticCtor() +{ + instance = new EntityRenderDispatcher(); +} + +EntityRenderDispatcher::EntityRenderDispatcher() +{ + glEnable(GL_LIGHTING); + renderers[eTYPE_SPIDER] = new SpiderRenderer(); + renderers[eTYPE_CAVESPIDER] = new SpiderRenderer(); + renderers[eTYPE_PIG] = new PigRenderer(new PigModel(), new PigModel(0.5f), 0.7f); + renderers[eTYPE_SHEEP] = new SheepRenderer(new SheepModel(), new SheepFurModel(), 0.7f); + renderers[eTYPE_COW] = new CowRenderer(new CowModel(), 0.7f); + renderers[eTYPE_MUSHROOMCOW] = new MushroomCowRenderer(new CowModel(), 0.7f); + renderers[eTYPE_WOLF] = new WolfRenderer(new WolfModel(), new WolfModel(), 0.5f); + renderers[eTYPE_CHICKEN] = new ChickenRenderer(new ChickenModel(), 0.3f); + renderers[eTYPE_OZELOT] = new OzelotRenderer(new OzelotModel(), 0.4f); + renderers[eTYPE_SILVERFISH] = new SilverfishRenderer(); + renderers[eTYPE_CREEPER] = new CreeperRenderer(); + renderers[eTYPE_ENDERMAN] = new EndermanRenderer(); + renderers[eTYPE_SNOWMAN] = new SnowManRenderer(); + renderers[eTYPE_SKELETON] = new HumanoidMobRenderer(new SkeletonModel(), 0.5f); + renderers[eTYPE_BLAZE] = new BlazeRenderer(); + renderers[eTYPE_ZOMBIE] = new ZombieRenderer(); + renderers[eTYPE_PIGZOMBIE] = new HumanoidMobRenderer(new ZombieModel(), 0.5f); + renderers[eTYPE_SLIME] = new SlimeRenderer(new SlimeModel(16), new SlimeModel(0), 0.25f); + renderers[eTYPE_LAVASLIME] = new LavaSlimeRenderer(); + renderers[eTYPE_PLAYER] = new PlayerRenderer(); + renderers[eTYPE_GIANT] = new GiantMobRenderer(new ZombieModel(), 0.5f, 6); + renderers[eTYPE_GHAST] = new GhastRenderer(); + renderers[eTYPE_SQUID] = new SquidRenderer(new SquidModel(), 0.7f); + renderers[eTYPE_VILLAGER] = new VillagerRenderer(); + renderers[eTYPE_VILLAGERGOLEM] = new VillagerGolemRenderer(); + renderers[eTYPE_MOB] = new MobRenderer(new HumanoidModel(), 0.5f); + renderers[eTYPE_ENDERDRAGON] = new EnderDragonRenderer(); + renderers[eTYPE_ENDER_CRYSTAL] = new EnderCrystalRenderer(); + renderers[eTYPE_ENTITY] = new DefaultRenderer(); + renderers[eTYPE_PAINTING] = new PaintingRenderer(); + renderers[eTYPE_ITEM_FRAME] = new ItemFrameRenderer(); + renderers[eTYPE_ARROW] = new ArrowRenderer(); + renderers[eTYPE_SNOWBALL] = new ItemSpriteRenderer(Item::snowBall); + renderers[eTYPE_THROWNENDERPEARL] = new ItemSpriteRenderer(Item::enderPearl); + renderers[eTYPE_EYEOFENDERSIGNAL] = new ItemSpriteRenderer(Item::eyeOfEnder); + renderers[eTYPE_THROWNEGG] = new ItemSpriteRenderer(Item::egg); + renderers[eTYPE_THROWNPOTION] = new ItemSpriteRenderer(Item::potion, PotionBrewing::THROWABLE_MASK); + renderers[eTYPE_THROWNEXPBOTTLE] = new ItemSpriteRenderer(Item::expBottle); + renderers[eTYPE_FIREBALL] = new FireballRenderer(2.0f); + renderers[eTYPE_SMALL_FIREBALL] = new FireballRenderer(0.5f); + renderers[eTYPE_DRAGON_FIREBALL] = new FireballRenderer(2.0f); // 4J Added TU9 + renderers[eTYPE_ITEMENTITY] = new ItemRenderer(); + renderers[eTYPE_EXPERIENCEORB] = new ExperienceOrbRenderer(); + renderers[eTYPE_PRIMEDTNT] = new TntRenderer(); + renderers[eTYPE_FALLINGTILE] = new FallingTileRenderer(); + renderers[eTYPE_MINECART] = new MinecartRenderer(); + renderers[eTYPE_BOAT] = new BoatRenderer(); + renderers[eTYPE_FISHINGHOOK] = new FishingHookRenderer(); + renderers[eTYPE_LIGHTNINGBOLT] = new LightningBoltRenderer(); + renderers[eTYPE_ARROW] = new ArrowRenderer(); + glDisable(GL_LIGHTING); + + AUTO_VAR(itEnd, renderers.end()); + for( classToRendererMap::iterator it = renderers.begin(); it != itEnd; it++ ) + { + it->second->init(this); + } + + isGuiRender = false; // 4J added +} + +EntityRenderer *EntityRenderDispatcher::getRenderer(eINSTANCEOF e) +{ + //EntityRenderer * r = renderers[e]; + AUTO_VAR(it, renderers.find( e )); // 4J Stu - The .at and [] accessors insert elements if they don't exist + + if( it == renderers.end() ) + { + // New renderer mapping required in above table + __debugbreak(); + } + /* 4J - not doing this hierarchical search anymore. We need to explicitly add renderers for any eINSTANCEOF type that we want to be able to render + if (it == renderers.end() && e != Entity::_class) + { + EntityRenderer *r = getRenderer(dynamic_cast( e->getSuperclass() )); + renderers.insert( classToRendererMap::value_type( e, r ) ); + return r; + //assert(false); + }*/ + return it->second; +} + +EntityRenderer *EntityRenderDispatcher::getRenderer(shared_ptr e) +{ + return getRenderer(e->GetType()); +} + +void EntityRenderDispatcher::prepare(Level *level, Textures *textures, Font *font, shared_ptr player, Options *options, float a) +{ + this->level = level; + this->textures = textures; + this->options = options; + this->cameraEntity = player; + this->font = font; + + if (player->isSleeping()) + { + int t = level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + if (t == Tile::bed_Id) + { + int data = level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + int direction = data & 3; + playerRotY = (float)(direction * 90 + 180); + playerRotX = 0; + } + } else { + playerRotY = player->yRotO + (player->yRot - player->yRotO) * a; + playerRotX = player->xRotO + (player->xRot - player->xRotO) * a; + } + + shared_ptr pl = dynamic_pointer_cast(player); + if (pl->ThirdPersonView() == 2) + { + playerRotY += 180; + } + + xPlayer = player->xOld + (player->x - player->xOld) * a; + yPlayer = player->yOld + (player->y - player->yOld) * a; + zPlayer = player->zOld + (player->z - player->zOld) * a; + +} + +void EntityRenderDispatcher::render(shared_ptr entity, float a) +{ + double x = entity->xOld + (entity->x - entity->xOld) * a; + double y = entity->yOld + (entity->y - entity->yOld) * a; + double z = entity->zOld + (entity->z - entity->zOld) * a; + + // Fix for #61057 - TU7: Gameplay: Boat is glitching when player float forward and turning. + // Fix to handle the case that yRot and yRotO wrap over the 0/360 line + float rotDiff = entity->yRot - entity->yRotO; + if( rotDiff > 180 || rotDiff < -180) + { + if(entity->yRot > entity->yRotO) + { + rotDiff = (entity->yRot - 360) - entity->yRotO; + } + else + { + rotDiff = entity->yRot - (entity->yRotO - 360); + } + } + float r = entity->yRotO + (rotDiff) * a; + + int col = entity->getLightColor(a); + if (entity->isOnFire()) + { + col = SharedConstants::FULLBRIGHT_LIGHTVALUE; + } + int u = col % 65536; + int v = col / 65536; + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + + render(entity, x - xOff, y - yOff, z - zOff, r, a); +} + +void EntityRenderDispatcher::render(shared_ptr entity, double x, double y, double z, float rot, float a, bool bItemFrame, bool bRenderPlayerShadow) +{ + EntityRenderer *renderer = getRenderer(entity); + if (renderer != NULL) + { + renderer->SetItemFrame(bItemFrame); + + renderer->render(entity, x, y, z, rot, a); + renderer->postRender(entity, x, y, z, rot, a, bRenderPlayerShadow); + } +} + +double EntityRenderDispatcher::distanceToSqr(double x, double y, double z) +{ + double xd = x - xPlayer; + double yd = y - yPlayer; + double zd = z - zPlayer; + return xd * xd + yd * yd + zd * zd; +} + +Font *EntityRenderDispatcher::getFont() +{ + return font; +} + +void EntityRenderDispatcher::registerTerrainTextures(IconRegister *iconRegister) +{ + //for (EntityRenderer renderer : renderers.values()) + for(AUTO_VAR(it, renderers.begin()); it != renderers.end(); ++it) + { + EntityRenderer *renderer = it->second; + renderer->registerTerrainTextures(iconRegister); + } +} \ No newline at end of file diff --git a/Minecraft.Client/EntityRenderDispatcher.h b/Minecraft.Client/EntityRenderDispatcher.h new file mode 100644 index 0000000..248be18 --- /dev/null +++ b/Minecraft.Client/EntityRenderDispatcher.h @@ -0,0 +1,49 @@ +#pragma once +#include "EntityRenderer.h" +#include "..\Minecraft.World\Entity.h" +#include "..\Minecraft.World\JavaIntHash.h" +class font; +using namespace std; + +class EntityRenderDispatcher +{ +public: + static void staticCtor(); // 4J added +private: + typedef unordered_map classToRendererMap; + classToRendererMap renderers; + // 4J - was: +// Map, EntityRenderer> renderers = new HashMap, EntityRenderer>(); + +public: + static EntityRenderDispatcher *instance; +private: + Font *font; + +public: + static double xOff, yOff, zOff; + + Textures *textures; + ItemInHandRenderer *itemInHandRenderer; + Level *level; + shared_ptr cameraEntity; + float playerRotY; + float playerRotX; + Options *options; + bool isGuiRender; // 4J added + + double xPlayer, yPlayer, zPlayer; + +private: + EntityRenderDispatcher(); +public: + EntityRenderer *getRenderer(eINSTANCEOF e); + EntityRenderer *getRenderer(shared_ptr e); + void prepare(Level *level, Textures *textures, Font *font, shared_ptr player, Options *options, float a); + void render(shared_ptr entity, float a); + void render(shared_ptr entity, double x, double y, double z, float rot, float a, bool bItemFrame = false, bool bRenderPlayerShadow = true); + void setLevel(Level *level); + double distanceToSqr(double x, double y, double z); + Font *getFont(); + void registerTerrainTextures(IconRegister *iconRegister); +}; diff --git a/Minecraft.Client/EntityRenderer.cpp b/Minecraft.Client/EntityRenderer.cpp new file mode 100644 index 0000000..6c0247e --- /dev/null +++ b/Minecraft.Client/EntityRenderer.cpp @@ -0,0 +1,410 @@ +#include "stdafx.h" +#include "EntityRenderer.h" +#include "HumanoidModel.h" +#include "EntityRenderDispatcher.h" +#include "Options.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "..\Minecraft.World\Entity.h" +#include "..\Minecraft.World\Level.h" +#include "..\Minecraft.World\AABB.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "LocalPlayer.h" + +// 4J - added +EntityRenderer::EntityRenderer() +{ + model = NULL; + tileRenderer = new TileRenderer(); + shadowRadius = 0; + shadowStrength = 1.0f; +} + +EntityRenderer::~EntityRenderer() +{ + delete tileRenderer; +} + +void EntityRenderer::bindTexture(int resourceName) +{ + entityRenderDispatcher->textures->bindTexture(resourceName); +} + +void EntityRenderer::bindTexture(const wstring& resourceName) +{ + entityRenderDispatcher->textures->bindTexture(resourceName); +} + +bool EntityRenderer::bindTexture(const wstring& urlTexture, int backupTexture) +{ + Textures *t = entityRenderDispatcher->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + glBindTexture(GL_TEXTURE_2D, id); + t->clearLastBoundId(); + return true; + } + else + { + return false; + } +} + +bool EntityRenderer::bindTexture(const wstring& urlTexture, const wstring& backupTexture) +{ + Textures *t = entityRenderDispatcher->textures; + + // 4J-PB - no http textures on the xbox, mem textures instead + + //int id = t->loadHttpTexture(urlTexture, backupTexture); + int id = t->loadMemTexture(urlTexture, backupTexture); + + if (id >= 0) + { + glBindTexture(GL_TEXTURE_2D, id); + t->clearLastBoundId(); + return true; + } + else + { + return false; + } +} + +void EntityRenderer::renderFlame(shared_ptr e, double x, double y, double z, float a) +{ + glDisable(GL_LIGHTING); + + Icon *fire1 = Tile::fire->getTextureLayer(0); + Icon *fire2 = Tile::fire->getTextureLayer(1); + + glPushMatrix(); + glTranslatef((float) x, (float) y, (float) z); + + float s = e->bbWidth * 1.4f; + glScalef(s, s, s); + MemSect(31); + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + + float r = 0.5f; + float xo = 0.0f; + + float h = e->bbHeight / s; + float yo = (float) (e->y - e->bb->y0); + + glRotatef(-entityRenderDispatcher->playerRotY, 0, 1, 0); + + glTranslatef(0, 0, -0.3f + ((int) h) * 0.02f); + glColor4f(1, 1, 1, 1); + float zo = 0; + int ss = 0; + t->begin(); + while (h > 0) + { + Icon *tex = NULL; + if (ss % 2 == 0) + { + tex = fire1; + } + else + { + tex = fire2; + } + + float u0 = tex->getU0(); + float v0 = tex->getV0(); + float u1 = tex->getU1(); + float v1 = tex->getV1(); + + if (ss / 2 % 2 == 0) + { + float tmp = u1; + u1 = u0; + u0 = tmp; + } + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( zo), (float)( u1), (float)( v1)); + t->vertexUV((float)(-r - xo), (float)( 0 - yo), (float)( zo), (float)( u0), (float)( v1)); + t->vertexUV((float)(-r - xo), (float)( 1.4f - yo), (float)( zo), (float)( u0), (float)( v0)); + t->vertexUV((float)(r - xo), (float)( 1.4f - yo), (float)( zo), (float)( u1), (float)( v0)); + h -= 0.45f; + yo -= 0.45f; + r *= 0.9f; + zo += 0.03f; + ss++; + } + t->end(); + glPopMatrix(); + glEnable(GL_LIGHTING); + +} +void EntityRenderer::renderShadow(shared_ptr e, double x, double y, double z, float pow, float a) +{ + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + MemSect(31); + entityRenderDispatcher->textures->bindTexture(TN__CLAMP__MISC_SHADOW);//L"%clamp%/misc/shadow.png")); + MemSect(0); + + Level *level = getLevel(); + + glDepthMask(false); + float r = shadowRadius; + shared_ptr mob = dynamic_pointer_cast(e); + bool isLocalPlayer = false; + float fYLocalPlayerShadowOffset=0.0f; + + //if (dynamic_pointer_cast(e) != NULL) + if (mob != NULL) + { + //shared_ptr mob = dynamic_pointer_cast(e); + r *= mob->getSizeScale(); + + shared_ptr animal = dynamic_pointer_cast(mob); + if (animal != NULL) + { + if (animal->isBaby()) + { + r *= 0.5f; + } + } + + if(dynamic_pointer_cast(mob)!=NULL) + { + isLocalPlayer=true; + } + } + + double ex = e->xOld + (e->x - e->xOld) * a; + double ey = e->yOld + (e->y - e->yOld) * a + e->getShadowHeightOffs(); + + // 4J-PB - local players seem to have a position at their head, and remote players have a foot position. + // get the shadow to render by changing the check here depending on the player type + if(isLocalPlayer) + { + ey-=1.62; + fYLocalPlayerShadowOffset=-1.62f; + } + double ez = e->zOld + (e->z - e->zOld) * a; + + int x0 = Mth::floor(ex - r); + int x1 = Mth::floor(ex + r); + int y0 = Mth::floor(ey - r); + int y1 = Mth::floor(ey); + int z0 = Mth::floor(ez - r); + int z1 = Mth::floor(ez + r); + + double xo = x - ex; + double yo = y - ey; + double zo = z - ez; + + Tesselator *tt = Tesselator::getInstance(); + tt->begin(); + for (int xt = x0; xt <= x1; xt++) + for (int yt = y0; yt <= y1; yt++) + for (int zt = z0; zt <= z1; zt++) + { + int t = level->getTile(xt, yt - 1, zt); + if (t > 0 && level->getRawBrightness(xt, yt, zt) > 3) + { + renderTileShadow(Tile::tiles[t], x, y + e->getShadowHeightOffs() + fYLocalPlayerShadowOffset, z, xt, yt , zt, pow, r, xo, yo + e->getShadowHeightOffs() + fYLocalPlayerShadowOffset, zo); + } + } + tt->end(); + + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + glDepthMask(true); + glEnable(GL_LIGHTING); + +} + +Level *EntityRenderer::getLevel() +{ + return entityRenderDispatcher->level; +} + +void EntityRenderer::renderTileShadow(Tile *tt, double x, double y, double z, int xt, int yt, int zt, float pow, float r, double xo, double yo, double zo) +{ + Tesselator *t = Tesselator::getInstance(); + if (!tt->isCubeShaped()) return; + + double a = ((pow - (y - (yt + yo)) / 2) * 0.5f) * getLevel()->getBrightness(xt, yt, zt); + if (a < 0) return; + if (a > 1) a = 1; + + t->color(1.0f, 1.0f, 1.0f, (float) a); + // glColor4f(1, 1, 1, (float) a); + + double x0 = xt + tt->getShapeX0() + xo; + double x1 = xt + tt->getShapeX1() + xo; + double y0 = yt + tt->getShapeY0() + yo + 1.0 / 64.0f; + double z0 = zt + tt->getShapeZ0() + zo; + double z1 = zt + tt->getShapeZ1() + zo; + + float u0 = (float) ((x - (x0)) / 2 / r + 0.5f); + float u1 = (float) ((x - (x1)) / 2 / r + 0.5f); + float v0 = (float) ((z - (z0)) / 2 / r + 0.5f); + float v1 = (float) ((z - (z1)) / 2 / r + 0.5f); + + // u0 = 0; + // v0 = 0; + // u1 = 1; + // v1 = 1; + + t->vertexUV((float)(x0), (float)( y0), (float)( z0), (float)( u0), (float)( v0)); + t->vertexUV((float)(x0), (float)( y0), (float)( z1), (float)( u0), (float)( v1)); + t->vertexUV((float)(x1), (float)( y0), (float)( z1), (float)( u1), (float)( v1)); + t->vertexUV((float)(x1), (float)( y0), (float)( z0), (float)( u1), (float)( v0)); +} + +void EntityRenderer::render(AABB *bb, double xo, double yo, double zo) +{ + glDisable(GL_TEXTURE_2D); + Tesselator *t = Tesselator::getInstance(); + glColor4f(1, 1, 1, 1); + t->begin(); + t->offset((float)xo, (float)yo, (float)zo); + t->normal(0, 0, -1); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + + t->normal(0, 0, 1); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + + t->normal(0, -1, 0); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + + t->normal(0, 1, 0); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + + t->normal(-1, 0, 0); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + + t->normal(1, 0, 0); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->offset(0, 0, 0); + t->end(); + glEnable(GL_TEXTURE_2D); + // model.render(0, 1) +} + +void EntityRenderer::renderFlat(AABB *bb) +{ + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x0), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x0), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z0)); + t->vertex((float)(bb->x1), (float)( bb->y1), (float)( bb->z1)); + t->vertex((float)(bb->x1), (float)( bb->y0), (float)( bb->z1)); + t->end(); +} + +void EntityRenderer::renderFlat(float x0, float y0, float z0, float x1, float y1, float z1) +{ + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertex(x0, y1, z0); + t->vertex(x1, y1, z0); + t->vertex(x1, y0, z0); + t->vertex(x0, y0, z0); + t->vertex(x0, y0, z1); + t->vertex(x1, y0, z1); + t->vertex(x1, y1, z1); + t->vertex(x0, y1, z1); + t->vertex(x0, y0, z0); + t->vertex(x1, y0, z0); + t->vertex(x1, y0, z1); + t->vertex(x0, y0, z1); + t->vertex(x0, y1, z1); + t->vertex(x1, y1, z1); + t->vertex(x1, y1, z0); + t->vertex(x0, y1, z0); + t->vertex(x0, y0, z1); + t->vertex(x0, y1, z1); + t->vertex(x0, y1, z0); + t->vertex(x0, y0, z0); + t->vertex(x1, y0, z0); + t->vertex(x1, y1, z0); + t->vertex(x1, y1, z1); + t->vertex(x1, y0, z1); + t->end(); +} + +void EntityRenderer::init(EntityRenderDispatcher *entityRenderDispatcher) +{ + this->entityRenderDispatcher = entityRenderDispatcher; +} + +void EntityRenderer::postRender(shared_ptr entity, double x, double y, double z, float rot, float a, bool bRenderPlayerShadow) +{ + if( !entityRenderDispatcher->isGuiRender ) // 4J - added, don't render shadow in gui as it uses its own blending, and we have globally enabled blending for interface opacity + { + if (bRenderPlayerShadow && entityRenderDispatcher->options->fancyGraphics && shadowRadius > 0 && !entity->isInvisible()) + { + double dist = entityRenderDispatcher->distanceToSqr(entity->x, entity->y, entity->z); + float pow = (float) ((1 - dist / (16.0f * 16.0f)) * shadowStrength); + if (pow > 0) + { + renderShadow(entity, x, y, z, pow, a); + } + } + } + if (entity->isOnFire()) renderFlame(entity, x, y, z, a); +} + +Font *EntityRenderer::getFont() +{ + return entityRenderDispatcher->getFont(); +} + +void EntityRenderer::registerTerrainTextures(IconRegister *iconRegister) +{ +} \ No newline at end of file diff --git a/Minecraft.Client/EntityRenderer.h b/Minecraft.Client/EntityRenderer.h new file mode 100644 index 0000000..e0c264a --- /dev/null +++ b/Minecraft.Client/EntityRenderer.h @@ -0,0 +1,65 @@ +#pragma once + +#include "Model.h" +#include "TileRenderer.h" +#include "Tesselator.h" +#include "Textures.h" +#include "ItemInHandRenderer.h" +class Tile; +class Entity; +class Level; +class AABB; +class IconRegister; + +using namespace std; + +class EntityRenderDispatcher; +class Font; + +// 4J - this was originally a generic of type EntityRenderer +class EntityRenderer +{ + friend class PlayerRenderer; // 4J Added to allow PlayerRenderer to call renderShadow +protected: + EntityRenderDispatcher *entityRenderDispatcher; + +private: + Model *model; // 4J - TODO - check why exactly this is here, it seems to get shadowed by classes inheriting from this by their own +protected: + TileRenderer *tileRenderer; // 4J - changed to protected so derived classes can use instead of shadowing their own + +protected: + float shadowRadius; + float shadowStrength; + +public: + EntityRenderer(); // 4J - added + virtual ~EntityRenderer(); +public: + virtual void render(shared_ptr entity, double x, double y, double z, float rot, float a) = 0; +protected: + virtual void bindTexture(int resourceName); // 4J - added + virtual void bindTexture(const wstring& resourceName); + + virtual bool bindTexture(const wstring& urlTexture, int backupTexture); // 4J added + virtual bool bindTexture(const wstring& urlTexture, const wstring& backupTexture); +private: + virtual void renderFlame(shared_ptr e, double x, double y, double z, float a); + virtual void renderShadow(shared_ptr e, double x, double y, double z, float pow, float a); + + virtual Level *getLevel(); + virtual void renderTileShadow(Tile *tt, double x, double y, double z, int xt, int yt, int zt, float pow, float r, double xo, double yo, double zo); +public: + virtual void render(AABB *bb, double xo, double yo, double zo); + static void renderFlat(AABB *bb); + static void renderFlat(float x0, float y0, float z0, float x1, float y1, float z1); + virtual void init(EntityRenderDispatcher *entityRenderDispatcher); + virtual void postRender(shared_ptr entity, double x, double y, double z, float rot, float a, bool bRenderPlayerShadow); + virtual Font *getFont(); + virtual void registerTerrainTextures(IconRegister *iconRegister); + +public: + // 4J Added + virtual Model *getModel() { return model; } + virtual void SetItemFrame(bool bSet) {} +}; diff --git a/Minecraft.Client/EntityTileRenderer.cpp b/Minecraft.Client/EntityTileRenderer.cpp new file mode 100644 index 0000000..a2301d4 --- /dev/null +++ b/Minecraft.Client/EntityTileRenderer.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "EntityTileRenderer.h" +#include "TileEntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" + +EntityTileRenderer *EntityTileRenderer::instance = new EntityTileRenderer; + +EntityTileRenderer::EntityTileRenderer() +{ + chest = shared_ptr(new ChestTileEntity()); + enderChest = shared_ptr(new EnderChestTileEntity()); +} + +void EntityTileRenderer::render(Tile *tile, int data, float brightness, float alpha, bool setColor, bool useCompiled) +{ + if (tile->id == Tile::enderChest_Id) + { + TileEntityRenderDispatcher::instance->render(enderChest, 0, 0, 0, 0, setColor, alpha, useCompiled); + } + else + { + TileEntityRenderDispatcher::instance->render(chest, 0, 0, 0, 0, setColor, alpha, useCompiled); + } +} diff --git a/Minecraft.Client/EntityTileRenderer.h b/Minecraft.Client/EntityTileRenderer.h new file mode 100644 index 0000000..cc572ca --- /dev/null +++ b/Minecraft.Client/EntityTileRenderer.h @@ -0,0 +1,19 @@ +#pragma once + +class ChestTileEntity; +class EnderChestTileEntity; +class Tile; + +class EntityTileRenderer + { + public: + static EntityTileRenderer *instance; + + private: + shared_ptr chest; + shared_ptr enderChest; + + public: + EntityTileRenderer(); + void render(Tile *tile, int data, float brightness, float alpha, bool setColor = true, bool useCompiled = true); // 4J - added setColor parameter and alpha for chest in the crafting menu, and added useCompiled +}; diff --git a/Minecraft.Client/EntityTracker.cpp b/Minecraft.Client/EntityTracker.cpp new file mode 100644 index 0000000..8f1dbcd --- /dev/null +++ b/Minecraft.Client/EntityTracker.cpp @@ -0,0 +1,229 @@ +#include "stdafx.h" +#include "EntityTracker.h" +#include "MinecraftServer.h" +#include "PlayerList.h" +#include "TrackedEntity.h" +#include "ServerPlayer.h" +#include "ServerLevel.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "..\Minecraft.World\net.minecraft.world.entity.global.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "..\Minecraft.World\net.minecraft.network.packet.h" +#include "..\Minecraft.World\net.minecraft.network.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\BasicTypeContainers.h" +#include "PlayerConnection.h" + +EntityTracker::EntityTracker(ServerLevel *level) +{ + this->level = level; + maxRange = level->getServer()->getPlayers()->getMaxRange(); +} + +void EntityTracker::addEntity(shared_ptr e) +{ + if (e->GetType() == eTYPE_SERVERPLAYER) + { + addEntity(e, 32 * 16, 2); + shared_ptr player = dynamic_pointer_cast(e); + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + if( (*it)->e != player ) + { + (*it)->updatePlayer(this, player); + } + } + } + else if (e->GetType() == eTYPE_FISHINGHOOK) addEntity(e, 16 * 4, 5, true); + else if (e->GetType() == eTYPE_SMALL_FIREBALL) addEntity(e, 16 * 4, 10, false); + else if (e->GetType() == eTYPE_DRAGON_FIREBALL) addEntity(e, 16 * 4, 10, false); // 4J Added TU9 + else if (e->GetType() == eTYPE_ARROW) addEntity(e, 16 * 4, 20, false); + else if (e->GetType() == eTYPE_FIREBALL) addEntity(e, 16 * 4, 10, false); + else if (e->GetType() == eTYPE_SNOWBALL) addEntity(e, 16 * 4, 10, true); + else if (e->GetType() == eTYPE_THROWNENDERPEARL) addEntity(e, 16 * 4, 10, true); + else if (e->GetType() == eTYPE_EYEOFENDERSIGNAL ) addEntity(e, 16 * 4, 4, true); + else if (e->GetType() == eTYPE_THROWNEGG) addEntity(e, 16 * 4, 10, true); + else if (e->GetType() == eTYPE_THROWNPOTION ) addEntity(e, 16 * 4, 10, true); + else if (e->GetType() == eTYPE_THROWNEXPBOTTLE) addEntity(e, 16 * 4, 10, true); + else if (e->GetType() == eTYPE_ITEMENTITY) addEntity(e, 16 * 4, 20, true); + else if (e->GetType() == eTYPE_MINECART) addEntity(e, 16 * 5, 3, true); + else if (e->GetType() == eTYPE_BOAT) addEntity(e, 16 * 5, 3, true); + else if (e->GetType() == eTYPE_SQUID) addEntity(e, 16 * 4, 3, true); + else if (dynamic_pointer_cast(e)!=NULL) addEntity(e, 16 * 5, 3, true); + else if (e->GetType() == eTYPE_ENDERDRAGON ) addEntity(e, 16 * 10, 3, true); + else if (e->GetType() == eTYPE_PRIMEDTNT) addEntity(e, 16 * 10, 10, true); + else if (e->GetType() == eTYPE_FALLINGTILE) addEntity(e, 16 * 10, 20, true); + else if (e->GetType() == eTYPE_PAINTING) addEntity(e, 16 * 10, INT_MAX, false); + else if (e->GetType() == eTYPE_EXPERIENCEORB) addEntity(e, 16 * 10, 20, true); + else if (e->GetType() == eTYPE_ENDER_CRYSTAL) addEntity(e, 16 * 16, INT_MAX, false); + else if (e->GetType() == eTYPE_ITEM_FRAME) addEntity(e, 16 * 10, INT_MAX, false); +} + +void EntityTracker::addEntity(shared_ptr e, int range, int updateInterval) +{ + addEntity(e, range, updateInterval, false); +} + +void EntityTracker::addEntity(shared_ptr e, int range, int updateInterval, bool trackDeltas) +{ + if (range > maxRange) range = maxRange; + if (entityMap.find(e->entityId) != entityMap.end()) + { + assert(false); // Entity already tracked + } + if( e->entityId >= 2048 ) + { + __debugbreak(); + } + shared_ptr te = shared_ptr( new TrackedEntity(e, range, updateInterval, trackDeltas) ); + entities.insert(te); + entityMap[e->entityId] = te; + te->updatePlayers(this, &level->players); +} + +// 4J - have split removeEntity into two bits - it used to do the equivalent of EntityTracker::removePlayer followed by EntityTracker::removeEntity. +// This is to allow us to now choose to remove the player as a "seenBy" only when the player has actually been removed from the level's own player array +void EntityTracker::removeEntity(shared_ptr e) +{ + AUTO_VAR(it, entityMap.find(e->entityId)); + if( it != entityMap.end() ) + { + shared_ptr te = it->second; + entityMap.erase(it); + entities.erase(te); + te->broadcastRemoved(); + } +} + +void EntityTracker::removePlayer(shared_ptr e) +{ + if (e->GetType() == eTYPE_SERVERPLAYER) + { + shared_ptr player = dynamic_pointer_cast(e); + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + (*it)->removePlayer(player); + } + } +} + +void EntityTracker::tick() +{ + vector > movedPlayers; + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + shared_ptr te = *it; + te->tick(this, &level->players); + if (te->moved && te->e->GetType() == eTYPE_SERVERPLAYER) + { + movedPlayers.push_back(dynamic_pointer_cast(te->e)); + } + } + + // 4J Stu - If one player on a system is updated, then make sure they all are as they all have their + // range extended to include entities visible by any other player on the system + // Fix for #11194 - Gameplay: Host player and their split-screen avatars can become invisible and invulnerable to client. + MinecraftServer *server = MinecraftServer::getInstance(); + for( unsigned int i = 0; i < server->getPlayers()->players.size(); i++ ) + { + shared_ptr ep = server->getPlayers()->players[i]; + if( ep->dimension != level->dimension->id ) continue; + + if( ep->connection == NULL ) continue; + INetworkPlayer *thisPlayer = ep->connection->getNetworkPlayer(); + if( thisPlayer == NULL ) continue; + + bool addPlayer = false; + for (unsigned int j = 0; j < movedPlayers.size(); j++) + { + shared_ptr sp = movedPlayers[j]; + + if( sp == ep ) break; + + if(sp->connection == NULL) continue; + INetworkPlayer *otherPlayer = sp->connection->getNetworkPlayer(); + if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) ) + { + addPlayer = true; + break; + } + } + if( addPlayer ) movedPlayers.push_back( ep ); + } + + for (unsigned int i = 0; i < movedPlayers.size(); i++) + { + shared_ptr player = movedPlayers[i]; + if(player->connection == NULL) continue; + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + shared_ptr te = *it; + if (te->e != player) + { + te->updatePlayer(this, player); + } + } + } + + // 4J Stu - We want to do this for dead players as they don't tick normally + for(AUTO_VAR(it, level->players.begin()); it != level->players.end(); ++it) + { + shared_ptr player = dynamic_pointer_cast(*it); + if(!player->isAlive()) + { + player->flushEntitiesToRemove(); + } + } +} + +void EntityTracker::broadcast(shared_ptr e, shared_ptr packet) +{ + AUTO_VAR(it, entityMap.find( e->entityId )); + if( it != entityMap.end() ) + { + shared_ptr te = it->second; + te->broadcast(packet); + } +} + +void EntityTracker::broadcastAndSend(shared_ptr e, shared_ptr packet) +{ + AUTO_VAR(it, entityMap.find( e->entityId )); + if( it != entityMap.end() ) + { + shared_ptr te = it->second; + te->broadcastAndSend(packet); + } +} + +void EntityTracker::clear(shared_ptr serverPlayer) +{ + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + shared_ptr te = *it; + te->clear(serverPlayer); + } +} + +// AP added for Vita so the range can be increased once the level starts +void EntityTracker::updateMaxRange() +{ + maxRange = level->getServer()->getPlayers()->getMaxRange(); +} + + +shared_ptr EntityTracker::getTracker(shared_ptr e) +{ + AUTO_VAR(it, entityMap.find(e->entityId)); + if( it != entityMap.end() ) + { + return it->second; + } + return nullptr; +} \ No newline at end of file diff --git a/Minecraft.Client/EntityTracker.h b/Minecraft.Client/EntityTracker.h new file mode 100644 index 0000000..82b01d0 --- /dev/null +++ b/Minecraft.Client/EntityTracker.h @@ -0,0 +1,36 @@ +#pragma once +#include "..\Minecraft.World\HashExtension.h" +#include "..\Minecraft.World\JavaIntHash.h" +class Entity; +class ServerPlayer; +class TrackedEntity; +class MinecraftServer; +class Packet; + +using namespace std; + +class EntityTracker +{ +private: + ServerLevel *level; + unordered_set > entities; + unordered_map , IntKeyHash2, IntKeyEq> entityMap; // was IntHashMap + int maxRange; + +public: + EntityTracker(ServerLevel *level); + void addEntity(shared_ptr e); + void addEntity(shared_ptr e, int range, int updateInterval); + void addEntity(shared_ptr e, int range, int updateInterval, bool trackDeltas); + void removeEntity(shared_ptr e); + void removePlayer(shared_ptr e); // 4J added + void tick(); + void broadcast(shared_ptr e, shared_ptr packet); + void broadcastAndSend(shared_ptr e, shared_ptr packet); + void clear(shared_ptr serverPlayer); + void updateMaxRange(); // AP added for Vita + + + // 4J-JEV: Added, needed access to tracked entity of a riders mount. + shared_ptr getTracker(shared_ptr entity); +}; diff --git a/Minecraft.Client/ErrorScreen.cpp b/Minecraft.Client/ErrorScreen.cpp new file mode 100644 index 0000000..44cb650 --- /dev/null +++ b/Minecraft.Client/ErrorScreen.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "ErrorScreen.h" + +ErrorScreen::ErrorScreen(const wstring& title, const wstring& message) +{ + this->title = title; + this->message = message; +} + +void ErrorScreen::init() +{ +} + +void ErrorScreen::render(int xm, int ym, float a) +{ + // fill(0, 0, width, height, 0x40000000); + fillGradient(0, 0, width, height, 0xff402020, 0xff501010); + + drawCenteredString(font, title, width/2, 90, 0xffffff); + drawCenteredString(font, message, width/2, 110, 0xffffff); + + Screen::render(xm, ym, a); +} + +void ErrorScreen::keyPressed(wchar_t eventCharacter, int eventKey) +{ +} \ No newline at end of file diff --git a/Minecraft.Client/ErrorScreen.h b/Minecraft.Client/ErrorScreen.h new file mode 100644 index 0000000..84ff667 --- /dev/null +++ b/Minecraft.Client/ErrorScreen.h @@ -0,0 +1,14 @@ +#pragma once +#include "Screen.h" + +class ErrorScreen : public Screen +{ +private: + wstring title, message; +public: + ErrorScreen(const wstring& title, const wstring& message); + virtual void init(); + virtual void render(int xm, int ym, float a); +protected: + virtual void keyPressed(wchar_t eventCharacter, int eventKey); +}; \ No newline at end of file diff --git a/Minecraft.Client/ExperienceOrbRenderer.cpp b/Minecraft.Client/ExperienceOrbRenderer.cpp new file mode 100644 index 0000000..1771f83 --- /dev/null +++ b/Minecraft.Client/ExperienceOrbRenderer.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "ExperienceOrbRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "Tesselator.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\JavaMath.h" + +ExperienceOrbRenderer::ExperienceOrbRenderer() +{ + // 4J In class Java initialisors + tileRenderer = new TileRenderer(); + setColor = true; + + this->shadowRadius = 0.15f; + this->shadowStrength = 0.75f; +} + + +void ExperienceOrbRenderer::render(shared_ptr _orb, double x, double y, double z, float rot, float a) +{ + shared_ptr orb = dynamic_pointer_cast(_orb); + glPushMatrix(); + glTranslatef((float) x, (float) y, (float) z); + + int icon = orb->getIcon(); + bindTexture(TN_ITEM_EXPERIENCE_ORB); // 4J was L"/item/xporb.png" + Tesselator *t = Tesselator::getInstance(); + + float u0 = ((icon % 4) * 16 + 0) / 64.0f; + float u1 = ((icon % 4) * 16 + 16) / 64.0f; + float v0 = ((icon / 4) * 16 + 0) / 64.0f; + float v1 = ((icon / 4) * 16 + 16) / 64.0f; + + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + if (SharedConstants::TEXTURE_LIGHTING) + { + int col = orb->getLightColor(a); + int u = col % 65536; + int v = col / 65536; + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + } + else + { + float br = orb->getBrightness(a); + glColor4f(br, br, br, 1); + } + float br = 255.0f; + float rr = (orb->tickCount + a) / 2; + int rc = (int) ((Mth::sin(rr + 0 * PI * 2 / 3) + 1) * 0.5f * br); + int gc = (int) (br); + int bc = (int) ((Mth::sin(rr + 2 * PI * 2 / 3) + 1) * 0.1f * br); + int col = rc << 16 | gc << 8 | bc; + glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(-entityRenderDispatcher->playerRotX, 1, 0, 0); + float s = 0.3f; + glScalef(s, s, s); + t->begin(); + t->color(col, 128); + t->normal(0, 1, 0); + t->vertexUV(0 - xo, 0 - yo, 0, u0, v1); + t->vertexUV(r - xo, 0 - yo, 0, u1, v1); + t->vertexUV(r - xo, 1 - yo, 0, u1, v0); + t->vertexUV(0 - xo, 1 - yo, 0, u0, v0); + t->end(); + + glDisable(GL_BLEND); + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); +} + +void ExperienceOrbRenderer::blit(int x, int y, int sx, int sy, int w, int h) +{ + float blitOffset = 0; + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertexUV(x + 0, y + h, blitOffset, (sx + 0) * us, (sy + h) * vs); + t->vertexUV(x + w, y + h, blitOffset, (sx + w) * us, (sy + h) * vs); + t->vertexUV(x + w, y + 0, blitOffset, (sx + w) * us, (sy + 0) * vs); + t->vertexUV(x + 0, y + 0, blitOffset, (sx + 0) * us, (sy + 0) * vs); + t->end(); +} \ No newline at end of file diff --git a/Minecraft.Client/ExperienceOrbRenderer.h b/Minecraft.Client/ExperienceOrbRenderer.h new file mode 100644 index 0000000..ebd166f --- /dev/null +++ b/Minecraft.Client/ExperienceOrbRenderer.h @@ -0,0 +1,17 @@ +#pragma once + +#include "EntityRenderer.h" + +class ExperienceOrbRenderer : public EntityRenderer +{ +private: + TileRenderer *tileRenderer; + +public: + bool setColor; + + ExperienceOrbRenderer(); + + void render(shared_ptr _orb, double x, double y, double z, float rot, float a); + void blit(int x, int y, int sx, int sy, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.Client/ExplodeParticle.cpp b/Minecraft.Client/ExplodeParticle.cpp new file mode 100644 index 0000000..fa950a0 --- /dev/null +++ b/Minecraft.Client/ExplodeParticle.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "ExplodeParticle.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Random.h" + +ExplodeParticle::ExplodeParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, xa, ya, za) +{ + xd = xa+(float)(Math::random()*2-1)*0.05f; + yd = ya+(float)(Math::random()*2-1)*0.05f; + zd = za+(float)(Math::random()*2-1)*0.05f; + + //rCol = gCol = bCol = random->nextFloat()*.3f+.7; + + unsigned int clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_Explode ); //0xFFFFFF + double r = ( (clr>>16)&0xFF )/255.0f, g = ( (clr>>8)&0xFF )/255.0, b = ( clr&0xFF )/255.0; + + float br = random->nextFloat() * 0.3f + 0.7f; + rCol = r * br; + gCol = g * br; + bCol = b * br; + + size = random->nextFloat()*random->nextFloat()*6+1; + + lifetime = (int)(16/(random->nextFloat()*0.8+0.2))+2; +// noPhysics = true; +} + +void ExplodeParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + // 4J - don't render explosion particles that are less than 3 metres away, to try and avoid large particles that are causing us problems with photosensitivity testing + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + + float distSq = (x*x + y*y + z*z); + if( distSq < (3.0f * 3.0f) ) return; + + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void ExplodeParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + setMiscTex(7-age*8/lifetime); + + yd += 0.004; + move(xd, yd, zd); + xd *= 0.90f; + yd *= 0.90f; + zd *= 0.90f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } +} \ No newline at end of file diff --git a/Minecraft.Client/ExplodeParticle.h b/Minecraft.Client/ExplodeParticle.h new file mode 100644 index 0000000..e25243d --- /dev/null +++ b/Minecraft.Client/ExplodeParticle.h @@ -0,0 +1,11 @@ +#pragma once +#include "Particle.h" + +class ExplodeParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_EXPLODEPARTICLE; } + ExplodeParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp new file mode 100644 index 0000000..4ebd89e --- /dev/null +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -0,0 +1,755 @@ +#include "stdafx.h" +#ifndef __PS3__ +//#include +#endif // __PS3__ + +#ifdef __PS3__ +#include "PS3\Sentient\SentientManager.h" +#include "StatsCounter.h" +#include "PS3\Social\SocialManager.h" +#include +#include +#elif defined _DURANGO +#include "Durango\Sentient\SentientManager.h" +#include "StatsCounter.h" +#include "Durango\Social\SocialManager.h" +#include "Durango\Sentient\DynamicConfigurations.h" +#include "Durango\DurangoExtras\xcompress.h" +#elif defined _WINDOWS64 +#include "Windows64\Sentient\SentientManager.h" +#include "StatsCounter.h" +#include "Windows64\Social\SocialManager.h" +#include "Windows64\Sentient\DynamicConfigurations.h" +#elif defined __PSVITA__ +#include "PSVita\Sentient\SentientManager.h" +#include "StatsCounter.h" +#include "PSVita\Social\SocialManager.h" +#include "PSVita\Sentient\DynamicConfigurations.h" +#include +#else +#include "Orbis\Sentient\SentientManager.h" +#include "StatsCounter.h" +#include "Orbis\Social\SocialManager.h" +#include "Orbis\Sentient\DynamicConfigurations.h" +#include +#endif + +#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) +#ifdef _WINDOWS64 +//C4JStorage StorageManager; +C_4JProfile ProfileManager; +#endif +#endif // __PS3__ +CSentientManager SentientManager; +CXuiStringTable StringTable; + +#ifndef _XBOX_ONE +ATG::XMLParser::XMLParser() {} +ATG::XMLParser::~XMLParser() {} +HRESULT ATG::XMLParser::ParseXMLBuffer( CONST CHAR* strBuffer, UINT uBufferSize ) { return S_OK; } +VOID ATG::XMLParser::RegisterSAXCallbackInterface( ISAXCallback *pISAXCallback ) {} +#endif + +bool CSocialManager::IsTitleAllowedToPostAnything() { return false; } +bool CSocialManager::AreAllUsersAllowedToPostImages() { return false; } +bool CSocialManager::IsTitleAllowedToPostImages() { return false; } + +bool CSocialManager::PostLinkToSocialNetwork( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect ) { return false; } +bool CSocialManager::PostImageToSocialNetwork( ESocialNetwork eSocialNetwork, DWORD dwUserIndex, bool bUsingKinect ) { return false; } +CSocialManager *CSocialManager::Instance() { return NULL; } +void CSocialManager::SetSocialPostText(LPCWSTR Title, LPCWSTR Caption, LPCWSTR Desc) {}; + +DWORD XShowPartyUI(DWORD dwUserIndex) { return 0; } +DWORD XShowFriendsUI(DWORD dwUserIndex) { return 0; } +HRESULT XPartyGetUserList(XPARTY_USER_LIST *pUserList) { return S_OK; } +DWORD XContentGetThumbnail(DWORD dwUserIndex, const XCONTENT_DATA *pContentData, PBYTE pbThumbnail, PDWORD pcbThumbnail, PXOVERLAPPED *pOverlapped) { return 0; } +void XShowAchievementsUI(int i) {} +DWORD XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE Mode) { return 0; } + +#ifndef _DURANGO +void PIXAddNamedCounter(int a, char *b, ...) {} +//#define PS3_USE_PIX_EVENTS +//#define PS4_USE_PIX_EVENTS +void PIXBeginNamedEvent(int a, char *b, ...) +{ +#ifdef PS4_USE_PIX_EVENTS + char buf[512]; + va_list args; + va_start(args,b); + vsprintf(buf,b,args); + sceRazorCpuPushMarker(buf, 0xffffffff, SCE_RAZOR_MARKER_ENABLE_HUD); + +#endif +#ifdef PS3_USE_PIX_EVENTS + char buf[256]; + wchar_t wbuf[256]; + va_list args; + va_start(args,b); + vsprintf(buf,b,args); + snPushMarker(buf); + +// mbstowcs(wbuf,buf,256); +// RenderManager.BeginEvent(wbuf); + va_end(args); +#endif +} +#if 0//__PSVITA__ + if( PixDepth < 64 ) + { + char buf[512]; + va_list args; + va_start(args,b); + vsprintf(buf,b,args); + sceRazorCpuPushMarkerWithHud(buf, 0xffffffff, SCE_RAZOR_MARKER_ENABLE_HUD); + } + PixDepth += 1; +#endif + + +void PIXEndNamedEvent() +{ +#ifdef PS4_USE_PIX_EVENTS + sceRazorCpuPopMarker(); +#endif +#ifdef PS3_USE_PIX_EVENTS + snPopMarker(); +// RenderManager.EndEvent(); +#endif +#if 0//__PSVITA__ + if( PixDepth <= 64 ) + { + sceRazorCpuPopMarker(); + } + PixDepth -= 1; +#endif +} +void PIXSetMarkerDeprecated(int a, char *b, ...) {} +#else +// 4J Stu - Removed this implementation in favour of a macro that will convert our string format +// conversion at compile time rather than at runtime +//void PIXBeginNamedEvent(int a, char *b, ...) +//{ +// char buf[256]; +// wchar_t wbuf[256]; +// va_list args; +// va_start(args,b); +// vsprintf(buf,b,args); +// +// mbstowcs(wbuf,buf,256); +// PIXBeginEvent(a,wbuf); +//} +// +//void PIXEndNamedEvent() +//{ +// PIXEndEvent(); +//} +// +//void PIXSetMarkerDeprecated(int a, char *b, ...) +//{ +// char buf[256]; +// wchar_t wbuf[256]; +// va_list args; +// va_start(args,b); +// vsprintf(buf,b,args); +// +// mbstowcs(wbuf,buf,256); +// PIXSetMarker(a, wbuf); +//} +#endif + +// void *D3DXBUFFER::GetBufferPointer() { return NULL; } +// int D3DXBUFFER::GetBufferSize() { return 0; } +// void D3DXBUFFER::Release() {} + +// #ifdef _DURANGO +// void GetLocalTime(SYSTEMTIME *time) {} +// #endif + + +bool IsEqualXUID(PlayerUID a, PlayerUID b) +{ +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) || defined(_DURANGO) + return (a == b); +#else + return false; +#endif +} + +void XMemCpy(void *a, const void *b, size_t s) { memcpy(a, b, s); } +void XMemSet(void *a, int t, size_t s) { memset(a, t, s); } +void XMemSet128(void *a, int t, size_t s) { memset(a, t, s); } +void *XPhysicalAlloc(SIZE_T a, ULONG_PTR b, ULONG_PTR c, DWORD d) { return malloc(a); } +void XPhysicalFree(void *a) { free(a); } + +D3DXVECTOR3::D3DXVECTOR3() {} +D3DXVECTOR3::D3DXVECTOR3(float x,float y,float z) : x(x), y(y), z(z) {} +D3DXVECTOR3& D3DXVECTOR3::operator += ( CONST D3DXVECTOR3& add ) { x += add.x; y += add.y; z += add.z; return *this; } + +#include "Windows64\Network\WinsockNetLayer.h" + +BYTE IQNetPlayer::GetSmallId() { return m_smallId; } +void IQNetPlayer::SendData(IQNetPlayer *player, const void *pvData, DWORD dwDataSize, DWORD dwFlags) +{ + if (WinsockNetLayer::IsActive()) + { + WinsockNetLayer::SendToSmallId(player->m_smallId, pvData, dwDataSize); + } +} +bool IQNetPlayer::IsSameSystem(IQNetPlayer *player) { return (this == player) || (!m_isRemote && !player->m_isRemote); } +DWORD IQNetPlayer::GetSendQueueSize( IQNetPlayer *player, DWORD dwFlags ) { return 0; } +DWORD IQNetPlayer::GetCurrentRtt() { return 0; } +bool IQNetPlayer::IsHost() { return m_isHostPlayer; } +bool IQNetPlayer::IsGuest() { return false; } +bool IQNetPlayer::IsLocal() { return !m_isRemote; } +PlayerUID IQNetPlayer::GetXuid() { return (PlayerUID)(0xe000d45248242f2e + m_smallId); } +LPCWSTR IQNetPlayer::GetGamertag() { return m_gamertag; } +int IQNetPlayer::GetSessionIndex() { return m_smallId; } +bool IQNetPlayer::IsTalking() { return false; } +bool IQNetPlayer::IsMutedByLocalUser(DWORD dwUserIndex) { return false; } +bool IQNetPlayer::HasVoice() { return false; } +bool IQNetPlayer::HasCamera() { return false; } +int IQNetPlayer::GetUserIndex() { return this - &IQNet::m_player[0]; } +void IQNetPlayer::SetCustomDataValue(ULONG_PTR ulpCustomDataValue) { + m_customData = ulpCustomDataValue; +} +ULONG_PTR IQNetPlayer::GetCustomDataValue() { + return m_customData; +} + +IQNetPlayer IQNet::m_player[MINECRAFT_NET_MAX_PLAYERS]; +DWORD IQNet::s_playerCount = 1; +bool IQNet::s_isHosting = true; + +QNET_STATE _iQNetStubState = QNET_STATE_IDLE; + +void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal) +{ + player->m_smallId = smallId; + player->m_isRemote = !isLocal; + player->m_isHostPlayer = isHost; + swprintf_s(player->m_gamertag, 32, L"Player%d", smallId); + if (smallId >= IQNet::s_playerCount) + IQNet::s_playerCount = smallId + 1; +} + +HRESULT IQNet::AddLocalPlayerByUserIndex(DWORD dwUserIndex){ return S_OK; } +IQNetPlayer *IQNet::GetHostPlayer() { return &m_player[0]; } +IQNetPlayer *IQNet::GetLocalPlayerByUserIndex(DWORD dwUserIndex) +{ + if (s_isHosting) + { + if (dwUserIndex < MINECRAFT_NET_MAX_PLAYERS && !m_player[dwUserIndex].m_isRemote) + return &m_player[dwUserIndex]; + return NULL; + } + if (dwUserIndex != 0) + return NULL; + for (DWORD i = 0; i < s_playerCount; i++) + { + if (!m_player[i].m_isRemote) + return &m_player[i]; + } + return NULL; +} +static bool Win64_IsActivePlayer(IQNetPlayer *p, DWORD index) +{ + if (index == 0) return true; + return (p->GetCustomDataValue() != 0); +} + +IQNetPlayer *IQNet::GetPlayerByIndex(DWORD dwPlayerIndex) +{ + DWORD found = 0; + for (DWORD i = 0; i < s_playerCount; i++) + { + if (Win64_IsActivePlayer(&m_player[i], i)) + { + if (found == dwPlayerIndex) return &m_player[i]; + found++; + } + } + return &m_player[0]; +} +IQNetPlayer *IQNet::GetPlayerBySmallId(BYTE SmallId) +{ + for (DWORD i = 0; i < s_playerCount; i++) + { + if (m_player[i].m_smallId == SmallId && Win64_IsActivePlayer(&m_player[i], i)) return &m_player[i]; + } + return NULL; +} +IQNetPlayer *IQNet::GetPlayerByXuid(PlayerUID xuid) +{ + for (DWORD i = 0; i < s_playerCount; i++) + { + if (Win64_IsActivePlayer(&m_player[i], i) && m_player[i].GetXuid() == xuid) return &m_player[i]; + } + return &m_player[0]; +} +DWORD IQNet::GetPlayerCount() +{ + DWORD count = 0; + for (DWORD i = 0; i < s_playerCount; i++) + { + if (Win64_IsActivePlayer(&m_player[i], i)) count++; + } + return count; +} +QNET_STATE IQNet::GetState() { return _iQNetStubState; } +bool IQNet::IsHost() { return s_isHosting; } +HRESULT IQNet::JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo) { return S_OK; } +void IQNet::HostGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = true; } +void IQNet::ClientJoinGame() { _iQNetStubState = QNET_STATE_SESSION_STARTING; s_isHosting = false; } +void IQNet::EndGame() +{ + _iQNetStubState = QNET_STATE_IDLE; + s_isHosting = false; + s_playerCount = 1; + for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++) + { + m_player[i].m_smallId = 0; + m_player[i].m_isRemote = false; + m_player[i].m_isHostPlayer = false; + m_player[i].m_gamertag[0] = 0; + m_player[i].SetCustomDataValue(0); + } +} + +DWORD MinecraftDynamicConfigurations::GetTrialTime() { return DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME; } + +void XSetThreadProcessor(HANDLE a, int b) {} +// #if !(defined __PS3__) && !(defined __ORBIS__) +// BOOL XCloseHandle(HANDLE a) { return CloseHandle(a); } +// #endif // __PS3__ + +DWORD XUserGetSigninInfo( + DWORD dwUserIndex, + DWORD dwFlags, + PXUSER_SIGNIN_INFO pSigninInfo +) +{ + return 0; +} + +LPCWSTR CXuiStringTable::Lookup(LPCWSTR szId) { return szId; } +LPCWSTR CXuiStringTable::Lookup(UINT nIndex) { return L"String"; } +void CXuiStringTable::Clear() {} +HRESULT CXuiStringTable::Load(LPCWSTR szId) { return S_OK; } + +DWORD XUserAreUsersFriends( DWORD dwUserIndex, PPlayerUID pXuids, DWORD dwXuidCount, PBOOL pfResult, void *pOverlapped) { return 0; } + +#if defined __ORBIS__ || defined __PS3__ || defined _XBOX_ONE +#else +HRESULT XMemDecompress( + XMEMDECOMPRESSION_CONTEXT Context, + VOID *pDestination, + SIZE_T *pDestSize, + CONST VOID *pSource, + SIZE_T SrcSize +) +{ + memcpy(pDestination, pSource, SrcSize); + *pDestSize = SrcSize; + return S_OK; + + /* + DECOMPRESSOR_HANDLE Decompressor = (DECOMPRESSOR_HANDLE)Context; + if( Decompress( + Decompressor, // Decompressor handle + (void *)pSource, // Compressed data + SrcSize, // Compressed data size + pDestination, // Decompressed buffer + *pDestSize, // Decompressed buffer size + pDestSize) ) // Decompressed data size + { + return S_OK; + } + else + */ + { + return E_FAIL; + } +} + +HRESULT XMemCompress( + XMEMCOMPRESSION_CONTEXT Context, + VOID *pDestination, + SIZE_T *pDestSize, + CONST VOID *pSource, + SIZE_T SrcSize +) +{ + memcpy(pDestination, pSource, SrcSize); + *pDestSize = SrcSize; + return S_OK; + + /* + COMPRESSOR_HANDLE Compressor = (COMPRESSOR_HANDLE)Context; + if( Compress( + Compressor, // Compressor Handle + (void *)pSource, // Input buffer, Uncompressed data + SrcSize, // Uncompressed data size + pDestination, // Compressed Buffer + *pDestSize, // Compressed Buffer size + pDestSize) ) // Compressed Data size + { + return S_OK; + } + else + */ + { + return E_FAIL; + } +} + +HRESULT XMemCreateCompressionContext( + XMEMCODEC_TYPE CodecType, + CONST VOID *pCodecParams, + DWORD Flags, + XMEMCOMPRESSION_CONTEXT *pContext +) +{ + /* + COMPRESSOR_HANDLE Compressor = NULL; + + HRESULT hr = CreateCompressor( + COMPRESS_ALGORITHM_XPRESS_HUFF, // Compression Algorithm + NULL, // Optional allocation routine + &Compressor); // Handle + + pContext = (XMEMDECOMPRESSION_CONTEXT *)Compressor; + return hr; + */ + return 0; +} + +HRESULT XMemCreateDecompressionContext( + XMEMCODEC_TYPE CodecType, + CONST VOID *pCodecParams, + DWORD Flags, + XMEMDECOMPRESSION_CONTEXT *pContext +) +{ + /* + DECOMPRESSOR_HANDLE Decompressor = NULL; + + HRESULT hr = CreateDecompressor( + COMPRESS_ALGORITHM_XPRESS_HUFF, // Compression Algorithm + NULL, // Optional allocation routine + &Decompressor); // Handle + + pContext = (XMEMDECOMPRESSION_CONTEXT *)Decompressor; + return hr; + */ + return 0; +} + +void XMemDestroyCompressionContext(XMEMCOMPRESSION_CONTEXT Context) +{ +// COMPRESSOR_HANDLE Compressor = (COMPRESSOR_HANDLE)Context; +// CloseCompressor(Compressor); +} + +void XMemDestroyDecompressionContext(XMEMDECOMPRESSION_CONTEXT Context) +{ +// DECOMPRESSOR_HANDLE Decompressor = (DECOMPRESSOR_HANDLE)Context; +// CloseDecompressor(Decompressor); +} +#endif + +//#ifndef __PS3__ +#if !(defined _DURANGO || defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) +DWORD XGetLanguage() { return 1; } +DWORD XGetLocale() { return 0; } +DWORD XEnableGuestSignin(BOOL fEnable) { return 0; } +#endif + + + +/////////////////////////////////////////////// Profile library +#ifdef _WINDOWS64 +static void *profileData[4]; +static bool s_bProfileIsFullVersion; +void C_4JProfile::Initialise( DWORD dwTitleID, + DWORD dwOfferID, + unsigned short usProfileVersion, + UINT uiProfileValuesC, + UINT uiProfileSettingsC, + DWORD *pdwProfileSettingsA, + int iGameDefinedDataSizeX4, + unsigned int *puiGameDefinedDataChangedBitmask) +{ + for( int i = 0; i < 4; i++ ) + { + profileData[i] = new byte[iGameDefinedDataSizeX4/4]; + ZeroMemory(profileData[i],sizeof(byte)*iGameDefinedDataSizeX4/4); + + // Set some sane initial values! + GAME_SETTINGS *pGameSettings = (GAME_SETTINGS *)profileData[i]; + pGameSettings->ucMenuSensitivity=100; //eGameSetting_Sensitivity_InMenu + pGameSettings->ucInterfaceOpacity=80; //eGameSetting_Sensitivity_InMenu + pGameSettings->usBitmaskValues|=0x0200; //eGameSetting_DisplaySplitscreenGamertags - on + pGameSettings->usBitmaskValues|=0x0400; //eGameSetting_Hints - on + pGameSettings->usBitmaskValues|=0x1000; //eGameSetting_Autosave - 2 + pGameSettings->usBitmaskValues|=0x8000; //eGameSetting_Tooltips - on + pGameSettings->uiBitmaskValues=0L; // reset + pGameSettings->uiBitmaskValues|=GAMESETTING_CLOUDS; //eGameSetting_Clouds - on + pGameSettings->uiBitmaskValues|=GAMESETTING_ONLINE; //eGameSetting_GameSetting_Online - on + pGameSettings->uiBitmaskValues|=GAMESETTING_FRIENDSOFFRIENDS; //eGameSetting_GameSetting_FriendsOfFriends - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYUPDATEMSG; //eGameSetting_DisplayUpdateMessage (counter) + pGameSettings->uiBitmaskValues&=~GAMESETTING_BEDROCKFOG; //eGameSetting_BedrockFog - off + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHUD; //eGameSetting_DisplayHUD - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DISPLAYHAND; //eGameSetting_DisplayHand - on + pGameSettings->uiBitmaskValues|=GAMESETTING_CUSTOMSKINANIM; //eGameSetting_CustomSkinAnim - on + pGameSettings->uiBitmaskValues|=GAMESETTING_DEATHMESSAGES; //eGameSetting_DeathMessages - on + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE&0x00000800); // uisize 2 + pGameSettings->uiBitmaskValues|=(GAMESETTING_UISIZE_SPLITSCREEN&0x00004000); // splitscreen ui size 3 + pGameSettings->uiBitmaskValues|=GAMESETTING_ANIMATEDCHARACTER; //eGameSetting_AnimatedCharacter - on + + // TU12 + // favorite skins added, but only set in TU12 - set to FFs + for(int i=0;iuiFavoriteSkinA[i]=0xFFFFFFFF; + } + pGameSettings->ucCurrentFavoriteSkinPos=0; + // Added a bitmask in TU13 to enable/disable display of the Mash-up pack worlds in the saves list + pGameSettings->uiMashUpPackWorldsDisplay = 0xFFFFFFFF; + + // PS3DEC13 + pGameSettings->uiBitmaskValues&=~GAMESETTING_PS3EULAREAD; //eGameSetting_PS3_EULA_Read - off + + // PS3 1.05 - added Greek + pGameSettings->ucLanguage = MINECRAFT_LANGUAGE_DEFAULT; // use the system language + + // PS Vita - network mode added + pGameSettings->uiBitmaskValues&=~GAMESETTING_PSVITANETWORKMODEADHOC; //eGameSetting_PSVita_NetworkModeAdhoc - off + + + // Tutorials for most menus, and a few other things + pGameSettings->ucTutorialCompletion[0] = 0xFF; + pGameSettings->ucTutorialCompletion[1] = 0xFF; + pGameSettings->ucTutorialCompletion[2] = 0xF; + + // Has gone halfway through the tutorial + pGameSettings->ucTutorialCompletion[28] |= 1<<0; + } +} +void C_4JProfile::SetTrialTextStringTable(CXuiStringTable *pStringTable,int iAccept,int iReject) {} +void C_4JProfile::SetTrialAwardText(eAwardType AwardType,int iTitle,int iText) {} +int C_4JProfile::GetLockedProfile() { return 0; } +void C_4JProfile::SetLockedProfile(int iProf) {} +bool C_4JProfile::IsSignedIn(int iQuadrant) { return ( iQuadrant == 0); } +bool C_4JProfile::IsSignedInLive(int iProf) { return true; } +bool C_4JProfile::IsGuest(int iQuadrant) { return false; } +UINT C_4JProfile::RequestSignInUI(bool bFromInvite,bool bLocalGame,bool bNoGuestsAllowed,bool bMultiplayerSignIn,bool bAddUser, int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; } +UINT C_4JProfile::DisplayOfflineProfile(int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; } +UINT C_4JProfile::RequestConvertOfflineToGuestUI(int( *Func)(LPVOID,const bool, const int iPad),LPVOID lpParam,int iQuadrant) { return 0; } +void C_4JProfile::SetPrimaryPlayerChanged(bool bVal) {} +bool C_4JProfile::QuerySigninStatus(void) { return true; } +void C_4JProfile::GetXUID(int iPad, PlayerUID *pXuid,bool bOnlineXuid) +{ +#ifdef _WINDOWS64 + if (iPad != 0) + { + *pXuid = INVALID_XUID; + return; + } + if (IQNet::s_isHosting) + *pXuid = 0xe000d45248242f2e; + else + *pXuid = 0xe000d45248242f2e + WinsockNetLayer::GetLocalSmallId(); +#else + *pXuid = 0xe000d45248242f2e + iPad; +#endif +} +BOOL C_4JProfile::AreXUIDSEqual(PlayerUID xuid1,PlayerUID xuid2) { return xuid1 == xuid2; } +BOOL C_4JProfile::XUIDIsGuest(PlayerUID xuid) { return false; } +bool C_4JProfile::AllowedToPlayMultiplayer(int iProf) { return true; } + +#if defined(__ORBIS__) +bool C_4JProfile::GetChatAndContentRestrictions(int iPad, bool thisQuadrantOnly, bool *pbChatRestricted,bool *pbContentRestricted,int *piAge) +{ + if(pbChatRestricted) *pbChatRestricted = false; + if(pbContentRestricted) *pbContentRestricted = false; + if(piAge) *piAge = 100; + return true; +} +#endif + +void C_4JProfile::StartTrialGame() {} +void C_4JProfile::AllowedPlayerCreatedContent(int iPad, bool thisQuadrantOnly, BOOL *allAllowed, BOOL *friendsAllowed) {} +BOOL C_4JProfile::CanViewPlayerCreatedContent(int iPad, bool thisQuadrantOnly, PPlayerUID pXuids, DWORD dwXuidCount ) { return true; } +bool C_4JProfile::GetProfileAvatar(int iPad,int( *Func)(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes), LPVOID lpParam) { return false; } +void C_4JProfile::CancelProfileAvatarRequest() {} +int C_4JProfile::GetPrimaryPad() { return 0; } +void C_4JProfile::SetPrimaryPad(int iPad) {} +#ifdef _DURANGO +char fakeGamerTag[32] = "PlayerName"; +void SetFakeGamertag(char *name){ strcpy_s(fakeGamerTag, name); } +char* C_4JProfile::GetGamertag(int iPad){ return fakeGamerTag; } +#else +char* C_4JProfile::GetGamertag(int iPad){ extern char g_Win64Username[17]; return g_Win64Username; } +wstring C_4JProfile::GetDisplayName(int iPad){ extern wchar_t g_Win64UsernameW[17]; return g_Win64UsernameW; } +#endif +bool C_4JProfile::IsFullVersion() { return s_bProfileIsFullVersion; } +void C_4JProfile::SetSignInChangeCallback(void ( *Func)(LPVOID, bool, unsigned int),LPVOID lpParam) {} +void C_4JProfile::SetNotificationsCallback(void ( *Func)(LPVOID, DWORD, unsigned int),LPVOID lpParam) {} +bool C_4JProfile::RegionIsNorthAmerica(void) { return false; } +bool C_4JProfile::LocaleIsUSorCanada(void) { return false; } +HRESULT C_4JProfile::GetLiveConnectionStatus() { return S_OK; } +bool C_4JProfile::IsSystemUIDisplayed() { return false; } +void C_4JProfile::SetProfileReadErrorCallback(void ( *Func)(LPVOID), LPVOID lpParam) {} +int( *defaultOptionsCallback)(LPVOID,C_4JProfile::PROFILESETTINGS *, const int iPad) = NULL; +LPVOID lpProfileParam = NULL; +int C_4JProfile::SetDefaultOptionsCallback(int( *Func)(LPVOID,PROFILESETTINGS *, const int iPad),LPVOID lpParam) +{ + defaultOptionsCallback = Func; + lpProfileParam = lpParam; + return 0; +} +int C_4JProfile::SetOldProfileVersionCallback(int( *Func)(LPVOID,unsigned char *, const unsigned short,const int),LPVOID lpParam) { return 0; } + +// To store the dashboard preferences for controller flipped, etc. +C_4JProfile::PROFILESETTINGS ProfileSettingsA[XUSER_MAX_COUNT]; + +C_4JProfile::PROFILESETTINGS * C_4JProfile::GetDashboardProfileSettings(int iPad) { return &ProfileSettingsA[iPad]; } +void C_4JProfile::WriteToProfile(int iQuadrant, bool bGameDefinedDataChanged, bool bOverride5MinuteLimitOnProfileWrites) {} +void C_4JProfile::ForceQueuedProfileWrites(int iPad) {} +void *C_4JProfile::GetGameDefinedProfileData(int iQuadrant) +{ + // 4J Stu - Don't reset the options when we call this!! + //defaultOptionsCallback(lpProfileParam, (C_4JProfile::PROFILESETTINGS *)profileData[iQuadrant], iQuadrant); + //pApp->SetDefaultOptions(pSettings,iPad); + + return profileData[iQuadrant]; +} +void C_4JProfile::ResetProfileProcessState() {} +void C_4JProfile::Tick( void ) {} +void C_4JProfile::RegisterAward(int iAwardNumber,int iGamerconfigID, eAwardType eType, bool bLeaderboardAffected, + CXuiStringTable*pStringTable, int iTitleStr, int iTextStr, int iAcceptStr, char *pszThemeName, unsigned int ulThemeSize) {} +int C_4JProfile::GetAwardId(int iAwardNumber) { return 0; } +eAwardType C_4JProfile::GetAwardType(int iAwardNumber) { return eAwardType_Achievement; } +bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber) { return false; } +void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce) {} +bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward) { return false; } +void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount) {} +void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID) {} +void C_4JProfile::SetRichPresenceContextValue(int iPad,int iContextID, int iVal) {} +void C_4JProfile::SetCurrentGameActivity(int iPad,int iNewPresence, bool bSetOthersToIdle) {} +void C_4JProfile::DisplayFullVersionPurchase(bool bRequired, int iQuadrant, int iUpsellParam) {} +void C_4JProfile::SetUpsellCallback(void ( *Func)(LPVOID lpParam, eUpsellType type, eUpsellResponse response, int iUserData),LPVOID lpParam) {} +void C_4JProfile::SetDebugFullOverride(bool bVal) {s_bProfileIsFullVersion = bVal;} +void C_4JProfile::ShowProfileCard(int iPad, PlayerUID targetUid) {} + +/////////////////////////////////////////////// Storage library +//#ifdef _WINDOWS64 +#if 0 +C4JStorage::C4JStorage() {} +void C4JStorage::Tick() {} +C4JStorage::EMessageResult C4JStorage::RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA,UINT uiOptionC, DWORD dwPad, int( *Func)(LPVOID,int,const C4JStorage::EMessageResult),LPVOID lpParam, C4JStringTable *pStringTable, WCHAR *pwchFormatString,DWORD dwFocusButton) { return C4JStorage::EMessage_Undefined; } +C4JStorage::EMessageResult C4JStorage::GetMessageBoxResult() { return C4JStorage::EMessage_Undefined; } +bool C4JStorage::SetSaveDevice(int( *Func)(LPVOID,const bool),LPVOID lpParam, bool bForceResetOfSaveDevice) { return true; } +void C4JStorage::Init(LPCWSTR pwchDefaultSaveName,char *pszSavePackName,int iMinimumSaveSize, int( *Func)(LPVOID, const ESavingMessage, int),LPVOID lpParam) {} +void C4JStorage::ResetSaveData() {} +void C4JStorage::SetDefaultSaveNameForKeyboardDisplay(LPCWSTR pwchDefaultSaveName) {} +void C4JStorage::SetSaveTitle(LPCWSTR pwchDefaultSaveName) {} +LPCWSTR C4JStorage::GetSaveTitle() { return L""; } +bool C4JStorage::GetSaveUniqueNumber(INT *piVal) { return true; } +bool C4JStorage::GetSaveUniqueFilename(char *pszName) { return true; } +void C4JStorage::SetSaveUniqueFilename(char *szFilename) { } +void C4JStorage::SetState(ESaveGameControlState eControlState,int( *Func)(LPVOID,const bool),LPVOID lpParam) {} +void C4JStorage::SetSaveDisabled(bool bDisable) {} +bool C4JStorage::GetSaveDisabled(void) { return false; } +unsigned int C4JStorage::GetSaveSize() { return 0; } +void C4JStorage::GetSaveData(void *pvData,unsigned int *pulBytes) {} +PVOID C4JStorage::AllocateSaveData(unsigned int ulBytes) { return new char[ulBytes]; } +void C4JStorage::SaveSaveData(unsigned int ulBytes,PBYTE pbThumbnail,DWORD cbThumbnail,PBYTE pbTextData, DWORD dwTextLen) {} +void C4JStorage::CopySaveDataToNewSave(PBYTE pbThumbnail,DWORD cbThumbnail,WCHAR *wchNewName,int ( *Func)(LPVOID lpParam, bool), LPVOID lpParam) {} +void C4JStorage::SetSaveDeviceSelected(unsigned int uiPad,bool bSelected) {} +bool C4JStorage::GetSaveDeviceSelected(unsigned int iPad) { return true; } +C4JStorage::ELoadGameStatus C4JStorage::DoesSaveExist(bool *pbExists) { return C4JStorage::ELoadGame_Idle; } +bool C4JStorage::EnoughSpaceForAMinSaveGame() { return true; } +void C4JStorage::SetSaveMessageVPosition(float fY) {} +//C4JStorage::ESGIStatus C4JStorage::GetSavesInfo(int iPad,bool ( *Func)(LPVOID, int, CACHEINFOSTRUCT *, int, HRESULT),LPVOID lpParam,char *pszSavePackName) { return C4JStorage::ESGIStatus_Idle; } +C4JStorage::ESaveGameState C4JStorage::GetSavesInfo(int iPad,int ( *Func)(LPVOID lpParam,SAVE_DETAILS *pSaveDetails,const bool),LPVOID lpParam,char *pszSavePackName) { return C4JStorage::ESaveGame_Idle; } + +void C4JStorage::GetSaveCacheFileInfo(DWORD dwFile,XCONTENT_DATA &xContentData) {} +void C4JStorage::GetSaveCacheFileInfo(DWORD dwFile, PBYTE *ppbImageData, DWORD *pdwImageBytes) {} +C4JStorage::ESaveGameState C4JStorage::LoadSaveData(PSAVE_INFO pSaveInfo,int( *Func)(LPVOID lpParam,const bool, const bool), LPVOID lpParam) {return C4JStorage::ESaveGame_Idle;} +C4JStorage::EDeleteGameStatus C4JStorage::DeleteSaveData(PSAVE_INFO pSaveInfo,int( *Func)(LPVOID lpParam,const bool), LPVOID lpParam) { return C4JStorage::EDeleteGame_Idle; } +PSAVE_DETAILS C4JStorage::ReturnSavesInfo() {return NULL;} + +void C4JStorage::RegisterMarketplaceCountsCallback(int ( *Func)(LPVOID lpParam, C4JStorage::DLC_TMS_DETAILS *, int), LPVOID lpParam ) {} +void C4JStorage::SetDLCPackageRoot(char *pszDLCRoot) {} +C4JStorage::EDLCStatus C4JStorage::GetDLCOffers(int iPad,int( *Func)(LPVOID, int, DWORD, int),LPVOID lpParam, DWORD dwOfferTypesBitmaskT) { return C4JStorage::EDLC_Idle; } +DWORD C4JStorage::CancelGetDLCOffers() { return 0; } +void C4JStorage::ClearDLCOffers() {} +XMARKETPLACE_CONTENTOFFER_INFO& C4JStorage::GetOffer(DWORD dw) { static XMARKETPLACE_CONTENTOFFER_INFO retval = {0}; return retval; } +int C4JStorage::GetOfferCount() { return 0; } +DWORD C4JStorage::InstallOffer(int iOfferIDC,ULONGLONG *ullOfferIDA,int( *Func)(LPVOID, int, int),LPVOID lpParam, bool bTrial) { return 0; } +DWORD C4JStorage::GetAvailableDLCCount( int iPad) { return 0; } +XCONTENT_DATA& C4JStorage::GetDLC(DWORD dw) { static XCONTENT_DATA retval = {0}; return retval; } +C4JStorage::EDLCStatus C4JStorage::GetInstalledDLC(int iPad,int( *Func)(LPVOID, int, int),LPVOID lpParam) { return C4JStorage::EDLC_Idle; } +DWORD C4JStorage::MountInstalledDLC(int iPad,DWORD dwDLC,int( *Func)(LPVOID, int, DWORD,DWORD),LPVOID lpParam,LPCSTR szMountDrive) { return 0; } +DWORD C4JStorage::UnmountInstalledDLC(LPCSTR szMountDrive) { return 0; } +C4JStorage::ETMSStatus C4JStorage::ReadTMSFile(int iQuadrant,eGlobalStorage eStorageFacility,C4JStorage::eTMS_FileType eFileType, WCHAR *pwchFilename,BYTE **ppBuffer,DWORD *pdwBufferSize,int( *Func)(LPVOID, WCHAR *,int, bool, int),LPVOID lpParam, int iAction) { return C4JStorage::ETMSStatus_Idle; } +bool C4JStorage::WriteTMSFile(int iQuadrant,eGlobalStorage eStorageFacility,WCHAR *pwchFilename,BYTE *pBuffer,DWORD dwBufferSize) { return true; } +bool C4JStorage::DeleteTMSFile(int iQuadrant,eGlobalStorage eStorageFacility,WCHAR *pwchFilename) { return true; } +void C4JStorage::StoreTMSPathName(WCHAR *pwchName) {} +unsigned int C4JStorage::CRC(unsigned char *buf, int len) { return 0; } + +struct PTMSPP_FILEDATA; +C4JStorage::ETMSStatus C4JStorage::TMSPP_ReadFile(int iPad,C4JStorage::eGlobalStorage eStorageFacility,C4JStorage::eTMS_FILETYPEVAL eFileTypeVal,LPCSTR szFilename,int( *Func)(LPVOID,int,int,PTMSPP_FILEDATA, LPCSTR)/*=NULL*/,LPVOID lpParam/*=NULL*/, int iUserData/*=0*/) {return C4JStorage::ETMSStatus_Idle;} +#endif // _WINDOWS64 + +#endif // __PS3__ + +/////////////////////////////////////////////////////// Sentient manager + +HRESULT CSentientManager::Init() { return S_OK; } +HRESULT CSentientManager::Tick() { return S_OK; } +HRESULT CSentientManager::Flush() { return S_OK; } +BOOL CSentientManager::RecordPlayerSessionStart(DWORD dwUserId) { return true; } +BOOL CSentientManager::RecordPlayerSessionExit(DWORD dwUserId, int exitStatus) { return true; } +BOOL CSentientManager::RecordHeartBeat(DWORD dwUserId) { return true; } +BOOL CSentientManager::RecordLevelStart(DWORD dwUserId, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, DWORD numberOfLocalPlayers, DWORD numberOfOnlinePlayers) { return true; } +BOOL CSentientManager::RecordLevelExit(DWORD dwUserId, ESen_LevelExitStatus levelExitStatus) { return true; } +BOOL CSentientManager::RecordLevelSaveOrCheckpoint(DWORD dwUserId, INT saveOrCheckPointID, INT saveSizeInBytes) { return true; } +BOOL CSentientManager::RecordLevelResume(DWORD dwUserId, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, DWORD numberOfLocalPlayers, DWORD numberOfOnlinePlayers, INT saveOrCheckPointID) { return true; } +BOOL CSentientManager::RecordPauseOrInactive(DWORD dwUserId) { return true; } +BOOL CSentientManager::RecordUnpauseOrActive(DWORD dwUserId) { return true; } +BOOL CSentientManager::RecordMenuShown(DWORD dwUserId, INT menuID, INT optionalMenuSubID) { return true; } +BOOL CSentientManager::RecordAchievementUnlocked(DWORD dwUserId, INT achievementID, INT achievementGamerscore) { return true; } +BOOL CSentientManager::RecordMediaShareUpload(DWORD dwUserId, ESen_MediaDestination mediaDestination, ESen_MediaType mediaType) { return true; } +BOOL CSentientManager::RecordUpsellPresented(DWORD dwUserId, ESen_UpsellID upsellId, INT marketplaceOfferID) { return true; } +BOOL CSentientManager::RecordUpsellResponded(DWORD dwUserId, ESen_UpsellID upsellId, INT marketplaceOfferID, ESen_UpsellOutcome upsellOutcome) { return true; } +BOOL CSentientManager::RecordPlayerDiedOrFailed(DWORD dwUserId, INT lowResMapX, INT lowResMapY, INT lowResMapZ, INT mapID, INT playerWeaponID, INT enemyWeaponID, ETelemetryChallenges enemyTypeID) { return true; } +BOOL CSentientManager::RecordEnemyKilledOrOvercome(DWORD dwUserId, INT lowResMapX, INT lowResMapY, INT lowResMapZ, INT mapID, INT playerWeaponID, INT enemyWeaponID, ETelemetryChallenges enemyTypeID) { return true; } +BOOL CSentientManager::RecordSkinChanged(DWORD dwUserId, DWORD dwSkinId) { return true; } +BOOL CSentientManager::RecordBanLevel(DWORD dwUserId) { return true; } +BOOL CSentientManager::RecordUnBanLevel(DWORD dwUserId) { return true; } +INT CSentientManager::GetMultiplayerInstanceID() { return 0; } +INT CSentientManager::GenerateMultiplayerInstanceId() { return 0; } +void CSentientManager::SetMultiplayerInstanceId(INT value) {} + +//////////////////////////////////////////////////////// Stats counter + +/* +StatsCounter::StatsCounter() {} +void StatsCounter::award(Stat *stat, unsigned int difficulty, unsigned int count) {} +bool StatsCounter::hasTaken(Achievement *ach) { return true; } +bool StatsCounter::canTake(Achievement *ach) { return true; } +unsigned int StatsCounter::getValue(Stat *stat, unsigned int difficulty) { return 0; } +unsigned int StatsCounter::getTotalValue(Stat *stat) { return 0; } +void StatsCounter::tick(int player) {} +void StatsCounter::parse(void* data) {} +void StatsCounter::clear() {} +void StatsCounter::save(int player, bool force) {} +void StatsCounter::flushLeaderboards() {} +void StatsCounter::saveLeaderboards() {} +void StatsCounter::setupStatBoards() {} +#ifdef _DEBUG +void StatsCounter::WipeLeaderboards() {} +#endif +*/ diff --git a/Minecraft.Client/FallingTileRenderer.cpp b/Minecraft.Client/FallingTileRenderer.cpp new file mode 100644 index 0000000..02e23da --- /dev/null +++ b/Minecraft.Client/FallingTileRenderer.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "FallingTileRenderer.h" +#include "TileRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "EntityRenderDispatcher.h" + +FallingTileRenderer::FallingTileRenderer() : EntityRenderer() +{ + tileRenderer = new TileRenderer(); + this->shadowRadius = 0.5f; +} + +void FallingTileRenderer::render(shared_ptr _tile, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr tile = dynamic_pointer_cast(_tile); + glPushMatrix(); + glTranslatef((float) x, (float) y, (float) z); + + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + Tile *tt = Tile::tiles[tile->tile]; + + Level *level = tile->getLevel(); + + glDisable(GL_LIGHTING); + glColor4f(1, 1, 1, 1); // 4J added - this wouldn't be needed in real opengl as the block render has vertex colours and so this isn't use, but our pretend gl always modulates with this + if (tt == Tile::anvil && tt->getRenderShape() == Tile::SHAPE_ANVIL) + { + tileRenderer->level = level; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->offset(-Mth::floor(tile->x) - 0.5f, -Mth::floor(tile->y) - 0.5f, -Mth::floor(tile->z) - 0.5f); + tileRenderer->tesselateAnvilInWorld((AnvilTile *) tt, Mth::floor(tile->x), Mth::floor(tile->y), Mth::floor(tile->z), tile->data); + t->offset(0, 0, 0); + t->end(); + } + else if (tt == Tile::dragonEgg) + { + tileRenderer->level = level; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->offset(-Mth::floor(tile->x) - 0.5f, -Mth::floor(tile->y) - 0.5f, -Mth::floor(tile->z) - 0.5f); + tileRenderer->tesselateInWorld(tt, Mth::floor(tile->x), Mth::floor(tile->y), Mth::floor(tile->z)); + t->offset(0, 0, 0); + t->end(); + } + else if( tt != NULL ) + { + tileRenderer->setShape(tt); + tileRenderer->renderBlock(tt, level, Mth::floor(tile->x), Mth::floor(tile->y), Mth::floor(tile->z), tile->data); + } + glEnable(GL_LIGHTING); + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/FallingTileRenderer.h b/Minecraft.Client/FallingTileRenderer.h new file mode 100644 index 0000000..0ece603 --- /dev/null +++ b/Minecraft.Client/FallingTileRenderer.h @@ -0,0 +1,14 @@ +#pragma once +#include "EntityRenderer.h" + + +class FallingTileRenderer : public EntityRenderer +{ +private: + TileRenderer *tileRenderer; + +public: + FallingTileRenderer(); + + virtual void render(shared_ptr _tile, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/FileTexturePack.cpp b/Minecraft.Client/FileTexturePack.cpp new file mode 100644 index 0000000..af58dc1 --- /dev/null +++ b/Minecraft.Client/FileTexturePack.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "FileTexturePack.h" + +FileTexturePack::FileTexturePack(DWORD id, File *file, TexturePack *fallback) : AbstractTexturePack(id, file, file->getName(), fallback) +{ + // 4J Stu - These calls need to be in the most derived version of the class + loadIcon(); + loadName(); + loadDescription(); +} + +void FileTexturePack::unload(Textures *textures) +{ +#if 0 + super.unload(textures); + + try { + if (zipFile != null) zipFile.close(); + } + catch (IOException ignored) + { + } + zipFile = null; +#endif +} + +InputStream *FileTexturePack::getResourceImplementation(const wstring &name) //throws IOException +{ +#if 0 + loadZipFile(); + + ZipEntry entry = zipFile.getEntry(name.substring(1)); + if (entry == null) { + throw new FileNotFoundException(name); + } + + return zipFile.getInputStream(entry); +#endif + return NULL; +} + +bool FileTexturePack::hasFile(const wstring &name) +{ +#if 0 + try { + loadZipFile(); + + return zipFile.getEntry(name.substring(1)) != null; + } catch (Exception e) { + return false; + } +#endif + return false; +} + +void FileTexturePack::loadZipFile() //throws IOException +{ +#if 0 + if (zipFile != null) { + return; + } + + zipFile = new ZipFile(file); +#endif +} + +bool FileTexturePack::isTerrainUpdateCompatible() +{ +#if 0 + try { + loadZipFile(); + + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (entry.getName().startsWith("textures/")) { + return true; + } + } + } catch (Exception ignored) { + } + boolean hasOldFiles = hasFile("terrain.png") || hasFile("gui/items.png"); + return !hasOldFiles; +#endif + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/FileTexturePack.h b/Minecraft.Client/FileTexturePack.h new file mode 100644 index 0000000..85221d1 --- /dev/null +++ b/Minecraft.Client/FileTexturePack.h @@ -0,0 +1,32 @@ +#pragma once +#include "AbstractTexturePack.h" +//class ZipFile; +class BufferedImage; +class File; +class Textures; +using namespace std; + +class FileTexturePack : public AbstractTexturePack +{ +private: + //ZipFile *zipFile; + +public: + FileTexturePack(DWORD id, File *file, TexturePack *fallback); + + //@Override + void unload(Textures *textures); + +protected: + InputStream *getResourceImplementation(const wstring &name); //throws IOException + +public: + //@Override + bool hasFile(const wstring &name); + +private: + void loadZipFile(); //throws IOException + +public: + bool isTerrainUpdateCompatible(); +}; diff --git a/Minecraft.Client/FireballRenderer.cpp b/Minecraft.Client/FireballRenderer.cpp new file mode 100644 index 0000000..836e8be --- /dev/null +++ b/Minecraft.Client/FireballRenderer.cpp @@ -0,0 +1,109 @@ +#include "stdafx.h" +#include "FireballRenderer.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\net.minecraft.world.h" + +FireballRenderer::FireballRenderer(float scale) +{ + this->scale = scale; +} + +void FireballRenderer::render(shared_ptr _fireball, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr fireball = dynamic_pointer_cast(_fireball); + + glPushMatrix(); + + glTranslatef((float) x, (float) y, (float) z); + glEnable(GL_RESCALE_NORMAL); + float s = scale; + glScalef(s / 1.0f, s / 1.0f, s / 1.0f); + Icon *icon = Item::fireball->getIcon(fireball->GetType()==eTYPE_DRAGON_FIREBALL?1:0);//14 + 2 * 16; + MemSect(31); + bindTexture(TN_GUI_ITEMS); // 4J was L"/gui/items.png" + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + + float u0 = icon->getU0(); + float u1 = icon->getU1(); + float v0 = icon->getV0(); + float v1 = icon->getV1(); + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(-entityRenderDispatcher->playerRotX, 1, 0, 0); + t->begin(); + t->normal(0, 1, 0); + t->vertexUV((float)(0 - xo), (float)( 0 - yo), (float)( 0), (float)( u0), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 1 - yo), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(0 - xo), (float)( 1 - yo), (float)( 0), (float)( u0), (float)( v0)); + t->end(); + + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); + +} + +// 4J Added override. Based on EntityRenderer::renderFlame +void FireballRenderer::renderFlame(shared_ptr e, double x, double y, double z, float a) +{ + glDisable(GL_LIGHTING); + Icon *tex = Tile::fire->getTextureLayer(0); + + glPushMatrix(); + glTranslatef((float) x, (float) y, (float) z); + + float s = e->bbWidth * 1.4f; + glScalef(s, s, s); + MemSect(31); + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + + float r = 1.0f; + float xo = 0.5f; +// float yo = 0.0f; + + float h = e->bbHeight / s; + float yo = (float) (e->y - e->bb->y0); + + //glRotatef(-entityRenderDispatcher->playerRotY, 0, 1, 0); + + + glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(-entityRenderDispatcher->playerRotX, 1, 0, 0); + glTranslatef(0,0,0.1f); + //glTranslatef(0, 0, -0.3f + ((int) h) * 0.02f); + glColor4f(1, 1, 1, 1); + // glRotatef(-playerRotX, 1, 0, 0); + float zo = 0; + t->begin(); + t->normal(0, 1, 0); + + float u0 = tex->getU0(); + float v0 = tex->getV0(); + float u1 = tex->getU1(); + float v1 = tex->getV1(); + + float tmp = u1; + u1 = u0; + u0 = tmp; + + t->vertexUV((float)(0 - xo), (float)( 0 - yo), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( 0), (float)( u0), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 1.4f - yo), (float)( 0), (float)( u0), (float)( v0)); + t->vertexUV((float)(0 - xo), (float)( 1.4f - yo), (float)( 0), (float)( u1), (float)( v0)); + + t->end(); + glPopMatrix(); + glEnable(GL_LIGHTING); +} \ No newline at end of file diff --git a/Minecraft.Client/FireballRenderer.h b/Minecraft.Client/FireballRenderer.h new file mode 100644 index 0000000..9b22e74 --- /dev/null +++ b/Minecraft.Client/FireballRenderer.h @@ -0,0 +1,17 @@ +#pragma once +#include "EntityRenderer.h" + +class FireballRenderer : public EntityRenderer +{ +private: + float scale; + +public: + FireballRenderer(float scale); + + virtual void render(shared_ptr _fireball, double x, double y, double z, float rot, float a); + +private: + // 4J Added override + virtual void renderFlame(shared_ptr e, double x, double y, double z, float a); +}; diff --git a/Minecraft.Client/FishingHookRenderer.cpp b/Minecraft.Client/FishingHookRenderer.cpp new file mode 100644 index 0000000..0408e96 --- /dev/null +++ b/Minecraft.Client/FishingHookRenderer.cpp @@ -0,0 +1,101 @@ +#include "stdafx.h" +#include "FishingHookRenderer.h" +#include "EntityRenderDispatcher.h" +#include "Options.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\Vec3.h" +#include "..\Minecraft.World\Mth.h" +#include "MultiPlayerLocalPlayer.h" + +void FishingHookRenderer::render(shared_ptr _hook, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr hook = dynamic_pointer_cast(_hook); + + glPushMatrix(); + + glTranslatef((float) x, (float) y, (float) z); + glEnable(GL_RESCALE_NORMAL); + glScalef(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + int xi = 1; + int yi = 2; + bindTexture(TN_PARTICLES); // 4J was L"/particles.png" + Tesselator *t = Tesselator::getInstance(); + + float u0 = ((xi) * 8 + 0) / 128.0f; + float u1 = ((xi) * 8 + 8) / 128.0f; + float v0 = ((yi) * 8 + 0) / 128.0f; + float v1 = ((yi) * 8 + 8) / 128.0f; + + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.5f; + + glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(-entityRenderDispatcher->playerRotX, 1, 0, 0); + t->begin(); + t->normal(0, 1, 0); + t->vertexUV((float)(0 - xo), (float)( 0 - yo), (float)( 0), (float)( u0), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 1 - yo), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(0 - xo), (float)( 1 - yo), (float)( 0), (float)( u0), (float)( v0)); + t->end(); + + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); + + + if (hook->owner != NULL) + { + float swing = hook->owner->getAttackAnim(a); + float swing2 = (float) Mth::sin((sqrt(swing)) * PI); + + + Vec3 *vv = Vec3::newTemp(-0.5, 0.03, 0.8); + vv->xRot(-(hook->owner->xRotO + (hook->owner->xRot - hook->owner->xRotO) * a) * PI / 180); + vv->yRot(-(hook->owner->yRotO + (hook->owner->yRot - hook->owner->yRotO) * a) * PI / 180); + vv->yRot(swing2 * 0.5f); + vv->xRot(-swing2 * 0.7f); + + double xp = hook->owner->xo + (hook->owner->x - hook->owner->xo) * a + vv->x; + double yp = hook->owner->yo + (hook->owner->y - hook->owner->yo) * a + vv->y; + double zp = hook->owner->zo + (hook->owner->z - hook->owner->zo) * a + vv->z; + double yOffset = hook->owner != dynamic_pointer_cast(Minecraft::GetInstance()->player) ? hook->owner->getHeadHeight() : 0; + + // 4J-PB - changing this to be per player + //if (this->entityRenderDispatcher->options->thirdPersonView) + if (hook->owner->ThirdPersonView() > 0) + { + float rr = (float) (hook->owner->yBodyRotO + (hook->owner->yBodyRot - hook->owner->yBodyRotO) * a) * PI / 180; + double ss = Mth::sin((float) rr); + double cc = Mth::cos((float) rr); + xp = hook->owner->xo + (hook->owner->x - hook->owner->xo) * a - cc * 0.35 - ss * 0.85; + yp = hook->owner->yo + yOffset + (hook->owner->y - hook->owner->yo) * a - 0.45; + zp = hook->owner->zo + (hook->owner->z - hook->owner->zo) * a - ss * 0.35 + cc * 0.85; + } + + double xh = hook->xo + (hook->x - hook->xo) * a; + double yh = hook->yo + (hook->y - hook->yo) * a + 4 / 16.0f; + double zh = hook->zo + (hook->z - hook->zo) * a; + + double xa = (float) (xp - xh); + double ya = (float) (yp - yh); + double za = (float) (zp - zh); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + t->begin(GL_LINE_STRIP); + t->color(0x000000); + int steps = 16; + for (int i = 0; i <= steps; i++) + { + float aa = i / (float) steps; + t->vertex((float)(x + xa * aa), (float)( y + ya * (aa * aa + aa) * 0.5 + 4 / 16.0f), (float)( z + za * aa)); + } + t->end(); + glEnable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + } +} diff --git a/Minecraft.Client/FishingHookRenderer.h b/Minecraft.Client/FishingHookRenderer.h new file mode 100644 index 0000000..2683f18 --- /dev/null +++ b/Minecraft.Client/FishingHookRenderer.h @@ -0,0 +1,8 @@ +#pragma once +#include "EntityRenderer.h" + +class FishingHookRenderer : public EntityRenderer +{ +public: + virtual void render(shared_ptr _hook, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/FlameParticle.cpp b/Minecraft.Client/FlameParticle.cpp new file mode 100644 index 0000000..eb12dbf --- /dev/null +++ b/Minecraft.Client/FlameParticle.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Random.h" +#include "FlameParticle.h" + +FlameParticle::FlameParticle(Level *level, double x, double y, double z, double xd, double yd, double zd) : Particle(level, x, y, z, xd, yd, zd) +{ + this->xd=this->xd*0.01f+xd; + this->yd=this->yd*0.01f+yd; + this->zd=this->zd*0.01f+zd; + x+=(random->nextFloat()-random->nextFloat())*0.05f; + y+=(random->nextFloat()-random->nextFloat())*0.05f; + z+=(random->nextFloat()-random->nextFloat())*0.05f; + + oSize = size; + rCol = gCol = bCol = 1.0f; + + lifetime = (int)(8/(Math::random()*0.8+0.2))+4; + noPhysics = true; + setMiscTex(48); +} + +void FlameParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float s = (age + a) / (float) lifetime; + size = oSize * (1 - s*s*0.5f); + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +// 4J - brought forward from 1.8.2 +int FlameParticle::getLightColor(float a) +{ + float l = (age + a) / lifetime; + if (l < 0) l = 0; + if (l > 1) l = 1; + int br = Particle::getLightColor(a); + + int br1 = (br) & 0xff; + int br2 = (br >> 16) & 0xff; + br1 += (int) (l * 15 * 16); + if (br1 > 15 * 16) br1 = 15 * 16; + return br1 | br2 << 16; +} + +float FlameParticle::getBrightness(float a) +{ + float l = (age+a)/lifetime; + if (l<0) l = 0; + if (l>1) l = 1; + float br = Particle::getBrightness(a); + + return br*l+(1-l); +} + +void FlameParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + xd *= 0.96f; + yd *= 0.96f; + zd *= 0.96f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } +} \ No newline at end of file diff --git a/Minecraft.Client/FlameParticle.h b/Minecraft.Client/FlameParticle.h new file mode 100644 index 0000000..7097d9d --- /dev/null +++ b/Minecraft.Client/FlameParticle.h @@ -0,0 +1,17 @@ +#pragma once +#include "Particle.h" + +class FlameParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_FLAMEPARTICLE; } +private: + float oSize; + +public: + FlameParticle(Level *level, double x, double y, double z, double xd, double yd, double zd); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual int getLightColor(float a); // 4J - brought forward from 1.8.2 + virtual float getBrightness(float a); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/FolderTexturePack.cpp b/Minecraft.Client/FolderTexturePack.cpp new file mode 100644 index 0000000..4547568 --- /dev/null +++ b/Minecraft.Client/FolderTexturePack.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" +#include "FolderTexturePack.h" + +FolderTexturePack::FolderTexturePack(DWORD id, const wstring &name, File *folder, TexturePack *fallback) : AbstractTexturePack(id, folder, name, fallback) +{ + // 4J Stu - These calls need to be in the most derived version of the class + loadIcon(); + loadName(); + loadDescription(); + + bUILoaded = false; +} + +InputStream *FolderTexturePack::getResourceImplementation(const wstring &name) //throws IOException +{ +#if 0 + final File file = new File(this.file, name.substring(1)); + if (!file.exists()) { + throw new FileNotFoundException(name); + } + + return new BufferedInputStream(new FileInputStream(file)); +#endif + + wstring wDrive = L""; + // Make the content package point to to the UPDATE: drive is needed +#ifdef _XBOX + wDrive=L"GAME:\\DummyTexturePack\\res"; +#else + wDrive = L"Common\\DummyTexturePack\\res"; +#endif + InputStream *resource = InputStream::getResourceAsStream(wDrive + name); + //InputStream *stream = DefaultTexturePack::class->getResourceAsStream(name); + //if (stream == NULL) + //{ + // throw new FileNotFoundException(name); + //} + + //return stream; + return resource; +} + +bool FolderTexturePack::hasFile(const wstring &name) +{ + File file = File( getPath() + name); + return file.exists() && file.isFile(); + //return true; +} + +bool FolderTexturePack::isTerrainUpdateCompatible() +{ +#if 0 + final File dir = new File(this.file, "textures/"); + final boolean hasTexturesFolder = dir.exists() && dir.isDirectory(); + final boolean hasOldFiles = hasFile("terrain.png") || hasFile("gui/items.png"); + return hasTexturesFolder || !hasOldFiles; +#endif + return true; +} + +wstring FolderTexturePack::getPath(bool bTitleUpdateTexture /*= false*/) +{ + wstring wDrive; +#ifdef _XBOX + wDrive=L"GAME:\\" + file->getPath() + L"\\"; +#else + wDrive=L"Common\\" + file->getPath() + L"\\"; +#endif + return wDrive; +} + +void FolderTexturePack::loadUI() +{ +#ifdef _XBOX + //"file://" + Drive + PathToXZP + "#" + PathInsideXZP + + //L"file://game:/ui.xzp#skin_default.xur" + + // Load new skin + if(hasFile(L"TexturePack.xzp")) + { + const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string + WCHAR szResourceLocator[ LOCATOR_SIZE ]; + + swprintf(szResourceLocator, LOCATOR_SIZE,L"file://%lsTexturePack.xzp#skin_Minecraft.xur",getPath().c_str()); + + XuiFreeVisuals(L""); + app.LoadSkin(szResourceLocator,NULL);//L"TexturePack"); + bUILoaded = true; + //CXuiSceneBase::GetInstance()->SetVisualPrefix(L"TexturePack"); + } + + AbstractTexturePack::loadUI(); +#endif +} + +void FolderTexturePack::unloadUI() +{ +#ifdef _XBOX + // Unload skin + if(bUILoaded) + { + XuiFreeVisuals(L"TexturePack"); + XuiFreeVisuals(L""); + CXuiSceneBase::GetInstance()->SetVisualPrefix(L""); + CXuiSceneBase::GetInstance()->SkinChanged(CXuiSceneBase::GetInstance()->m_hObj); + } + AbstractTexturePack::unloadUI(); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/FolderTexturePack.h b/Minecraft.Client/FolderTexturePack.h new file mode 100644 index 0000000..fff4abf --- /dev/null +++ b/Minecraft.Client/FolderTexturePack.h @@ -0,0 +1,26 @@ +#pragma once + +#include "AbstractTexturePack.h" + +class FolderTexturePack : public AbstractTexturePack +{ +private: + bool bUILoaded; + +public: + FolderTexturePack(DWORD id, const wstring &name, File *folder, TexturePack *fallback); + +protected: + //@Override + InputStream *getResourceImplementation(const wstring &name); //throws IOException + +public: + //@Override + bool hasFile(const wstring &name); + bool isTerrainUpdateCompatible(); + + // 4J Added + virtual wstring getPath(bool bTitleUpdateTexture = false); + virtual void loadUI(); + virtual void unloadUI(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Font.cpp b/Minecraft.Client/Font.cpp new file mode 100644 index 0000000..17f7285 --- /dev/null +++ b/Minecraft.Client/Font.cpp @@ -0,0 +1,616 @@ +#include "stdafx.h" +#include "Textures.h" +#include "Font.h" +#include "Options.h" +#include "Tesselator.h" +#include "..\Minecraft.World\IntBuffer.h" +#include "..\Minecraft.World\net.minecraft.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\Random.h" + +Font::Font(Options *options, const wstring& name, Textures* textures, bool enforceUnicode, TEXTURE_NAME textureName, int cols, int rows, int charWidth, int charHeight, unsigned short charMap[]/* = nullptr */) : textures(textures) +{ + int charC = cols * rows; // Number of characters in the font + + charWidths = new int[charC]; + + // 4J - added initialisers + memset(charWidths, 0, charC); + + enforceUnicodeSheet = false; + bidirectional = false; + xPos = yPos = 0.0f; + + // Set up member variables + m_cols = cols; + m_rows = rows; + m_charWidth = charWidth; + m_charHeight = charHeight; + m_textureName = textureName; + + // Build character map + if (charMap != NULL) + { + for(int i = 0; i < charC; i++) + { + m_charMap.insert(std::make_pair(charMap[i], i)); + } + } + + random = new Random(); + + // Load the image + BufferedImage *img = textures->readImage(m_textureName, name); + + /* - 4J - TODO + try { + img = ImageIO.read(Textures.class.getResourceAsStream(name)); + } catch (IOException e) { + throw new RuntimeException(e); + } + */ + + int w = img->getWidth(); + int h = img->getHeight(); + intArray rawPixels(w * h); + img->getRGB(0, 0, w, h, rawPixels, 0, w); + + for (int i = 0; i < charC; i++) + { + int xt = i % m_cols; + int yt = i / m_cols; + + int x = 7; + for (; x >= 0; x--) + { + int xPixel = xt * 8 + x; + bool emptyColumn = true; + for (int y = 0; y < 8 && emptyColumn; y++) + { + int yPixel = (yt * 8 + y) * w; + bool emptyPixel = (rawPixels[xPixel + yPixel] >> 24) == 0; // Check the alpha value + if (!emptyPixel) emptyColumn = false; + } + if (!emptyColumn) + { + break; + } + } + + if (i == ' ') x = 4 - 2; + charWidths[i] = x + 2; + } + + delete img; + + // calculate colors + for (int colorN = 0; colorN < 32; ++colorN) + { + int var10 = (colorN >> 3 & 1) * 85; + int red = (colorN >> 2 & 1) * 170 + var10; + int green = (colorN >> 1 & 1) * 170 + var10; + int blue = (colorN >> 0 & 1) * 170 + var10; + + if (colorN == 6) + { + red += 85; + } + + if (options->anaglyph3d) + { + int tmpRed = (red * 30 + green * 59 + blue * 11) / 100; + int tmpGreen = (red * 30 + green * 70) / 100; + int tmpBlue = (red * 30 + blue * 70) / 100; + red = tmpRed; + green = tmpGreen; + blue = tmpBlue; + } + + if (colorN >= 16) + { + red /= 4; + green /= 4; + blue /= 4; + } + + colors[colorN] = (red & 255) << 16 | (green & 255) << 8 | (blue & 255); + } +} + +#ifndef _XBOX +// 4J Stu - This dtor clashes with one in xui! We never delete these anyway so take it out for now. Can go back when we have got rid of XUI +Font::~Font() +{ + delete[] charWidths; +} +#endif + +void Font::renderCharacter(wchar_t c) +{ + float xOff = c % m_cols * m_charWidth; + float yOff = c / m_cols * m_charWidth; + + float width = charWidths[c] - .01f; + float height = m_charHeight - .01f; + + float fontWidth = m_cols * m_charWidth; + float fontHeight = m_rows * m_charHeight; + + Tesselator *t = Tesselator::getInstance(); + // 4J Stu - Changed to a quad so that we can use within a command buffer +#if 1 + t->begin(); + t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight); + t->vertex(xPos, yPos + height, 0.0f); + + t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight); + t->vertex(xPos + width, yPos + height, 0.0f); + + t->tex((xOff + width) / fontWidth, yOff / fontHeight); + t->vertex(xPos + width, yPos, 0.0f); + + t->tex(xOff / fontWidth, yOff / fontHeight); + t->vertex(xPos, yPos, 0.0f); + + t->end(); +#else + t->begin(GL_TRIANGLE_STRIP); + t->tex(xOff / 128.0F, yOff / 128.0F); + t->vertex(xPos, yPos, 0.0f); + t->tex(xOff / 128.0F, (yOff + 7.99f) / 128.0F); + t->vertex(xPos, yPos + 7.99f, 0.0f); + t->tex((xOff + width) / 128.0F, yOff / 128.0F); + t->vertex(xPos + width, yPos, 0.0f); + t->tex((xOff + width) / 128.0F, (yOff + 7.99f) / 128.0F); + t->vertex(xPos + width, yPos + 7.99f, 0.0f); + t->end(); +#endif + + xPos += (float) charWidths[c]; +} + +void Font::drawShadow(const wstring& str, int x, int y, int color) +{ + draw(str, x + 1, y + 1, color, true); + draw(str, x, y, color, false); +} + +void Font::drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h) +{ + drawWordWrapInternal(str, x + 1, y + 1, w, color, true, h); + drawWordWrapInternal(str, x, y, w, color, h); +} + +void Font::draw(const wstring& str, int x, int y, int color) +{ + draw(str, x, y, color, false); +} + +wstring Font::reorderBidi(const wstring &str) +{ + // 4J Not implemented + return str; +} + +void Font::draw(const wstring &str, bool dropShadow) +{ + // Bind the texture + textures->bindTexture(m_textureName); + + bool noise = false; + wstring cleanStr = sanitize(str); + + for (int i = 0; i < (int)cleanStr.length(); ++i) + { + // Map character + wchar_t c = cleanStr.at(i); + + if (c == 167 && i + 1 < cleanStr.length()) + { + // 4J - following block was: + // int colorN = L"0123456789abcdefk".indexOf(str.toLowerCase().charAt(i + 1)); + wchar_t ca = cleanStr[i+1]; + int colorN = 16; + if(( ca >= L'0' ) && (ca <= L'9')) colorN = ca - L'0'; + else if(( ca >= L'a' ) && (ca <= L'f')) colorN = (ca - L'a') + 10; + else if(( ca >= L'A' ) && (ca <= L'F')) colorN = (ca - L'A') + 10; + + if (colorN == 16) + { + noise = true; + } + else + { + noise = false; + if (colorN < 0 || colorN > 15) colorN = 15; + + if (dropShadow) colorN += 16; + + int color = colors[colorN]; + glColor3f((color >> 16) / 255.0F, ((color >> 8) & 255) / 255.0F, (color & 255) / 255.0F); + } + + + i += 1; + continue; + } + + // "noise" for crazy splash screen message + if (noise) + { + int newc; + do + { + newc = random->nextInt(SharedConstants::acceptableLetters.length()); + } while (charWidths[c + 32] != charWidths[newc + 32]); + c = newc; + } + + renderCharacter(c); + } +} + +void Font::draw(const wstring& str, int x, int y, int color, bool dropShadow) +{ + if (!str.empty()) + { + if ((color & 0xFC000000) == 0) color |= 0xFF000000; // force alpha + // if not set + + if (dropShadow) // divide RGB by 4, preserve alpha + color = (color & 0xfcfcfc) >> 2 | (color & (-1 << 24)); + + glColor4f((color >> 16 & 255) / 255.0F, (color >> 8 & 255) / 255.0F, (color & 255) / 255.0F, (color >> 24 & 255) / 255.0F); + + xPos = x; + yPos = y; + draw(str, dropShadow); + } +} + +int Font::width(const wstring& str) +{ + wstring cleanStr = sanitize(str); + + if (cleanStr == L"") return 0; // 4J - was NULL comparison + int len = 0; + + for (int i = 0; i < cleanStr.length(); ++i) + { + wchar_t c = cleanStr.at(i); + + if(c == 167) + { + // Ignore the character used to define coloured text + ++i; + } + else + { + len += charWidths[c]; + } + } + + return len; +} + +wstring Font::sanitize(const wstring& str) +{ + wstring sb = str; + + for (unsigned int i = 0; i < sb.length(); i++) + { + if (CharacterExists(sb[i])) + { + sb[i] = MapCharacter(sb[i]); + } + else + { + // If this character isn't supported, just show the first character (empty square box character) + sb[i] = 0; + } + } + return sb; +} + +int Font::MapCharacter(wchar_t c) +{ + if (!m_charMap.empty()) + { + // Don't map space character + return c == ' ' ? c : m_charMap[c]; + } + else + { + return c; + } +} + +bool Font::CharacterExists(wchar_t c) +{ + if (!m_charMap.empty()) + { + return m_charMap.find(c) != m_charMap.end(); + } + else + { + return c >= 0 && c <= m_rows*m_cols; + } +} + +void Font::drawWordWrap(const wstring &string, int x, int y, int w, int col, int h) +{ + //if (bidirectional) + //{ + // string = reorderBidi(string); + //} + drawWordWrapInternal(string, x, y, w, col, h); +} + +void Font::drawWordWrapInternal(const wstring &string, int x, int y, int w, int col, int h) +{ + drawWordWrapInternal(string, x, y, w, col, false, h); +} + +void Font::drawWordWrap(const wstring &string, int x, int y, int w, int col, bool darken, int h) +{ + //if (bidirectional) + //{ + // string = reorderBidi(string); + //} + drawWordWrapInternal(string, x, y, w, col, darken, h); +} + +void Font::drawWordWrapInternal(const wstring& string, int x, int y, int w, int col, bool darken, int h) +{ + vectorlines = stringSplit(string,L'\n'); + if (lines.size() > 1) + { + AUTO_VAR(itEnd, lines.end()); + for (AUTO_VAR(it, lines.begin()); it != itEnd; it++) + { + // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't + if( (y + this->wordWrapHeight(*it, w)) > h) break; + drawWordWrapInternal(*it, x, y, w, col, h); + y += this->wordWrapHeight(*it, w); + } + return; + } + vector words = stringSplit(string,L' '); + unsigned int pos = 0; + while (pos < words.size()) + { + wstring line = words[pos++] + L" "; + while (pos < words.size() && width(line + words[pos]) < w) + { + line += words[pos++] + L" "; + } + while (width(line) > w) + { + int l = 0; + while (width(line.substr(0, l + 1)) <= w) + { + l++; + } + if (trimString(line.substr(0, l)).length() > 0) + { + draw(line.substr(0, l), x, y, col); + y += 8; + } + line = line.substr(l); + + // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't + if( (y + 8) > h) break; + } + // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't + if (trimString(line).length() > 0 && !( (y + 8) > h) ) + { + draw(line, x, y, col); + y += 8; + } + } + +} + +int Font::wordWrapHeight(const wstring& string, int w) +{ + vector lines = stringSplit(string,L'\n'); + if (lines.size() > 1) + { + int h = 0; + AUTO_VAR(itEnd, lines.end()); + for (AUTO_VAR(it, lines.begin()); it != itEnd; it++) + { + h += this->wordWrapHeight(*it, w); + } + return h; + } + vector words = stringSplit(string,L' '); + unsigned int pos = 0; + int y = 0; + while (pos < words.size()) + { + wstring line = words[pos++] + L" "; + while (pos < words.size() && width(line + words[pos]) < w) + { + line += words[pos++] + L" "; + } + while (width(line) > w) + { + int l = 0; + while (width(line.substr(0, l + 1)) <= w) + { + l++; + } + if (trimString(line.substr(0, l)).length() > 0) + { + y += 8; + } + line = line.substr(l); + } + if (trimString(line).length() > 0) { + y += 8; + } + } + if (y < 8) y += 8; + return y; + +} + +void Font::setEnforceUnicodeSheet(bool enforceUnicodeSheet) +{ + this->enforceUnicodeSheet = enforceUnicodeSheet; +} + +void Font::setBidirectional(bool bidirectional) +{ + this->bidirectional = bidirectional; +} + +bool Font::AllCharactersValid(const wstring &str) +{ + for (int i = 0; i < (int)str.length(); ++i) + { + wchar_t c = str.at(i); + + if (c == 167 && i + 1 < str.length()) + { + // skip special color setting + i += 1; + continue; + } + + int index = SharedConstants::acceptableLetters.find(c); + + if ((c != ' ') && !(index > 0 && !enforceUnicodeSheet)) + { + return false; + } + } + return true; +} + +// Not in use +/*// 4J - this code is lifted from #if 0 section above, so that we can directly create what would have gone in each of our 256 + 32 command buffers +void Font::renderFakeCB(IntBuffer *ib) +{ + Tesselator *t = Tesselator::getInstance(); + + int i; + + for(unsigned int j = 0; j < ib->limit(); j++) + { + int cb = ib->get(j); + + if( cb < 256 ) + { + i = cb; + t->begin(); + int ix = i % 16 * 8; + int iy = i / 16 * 8; + // float s = 7.99f; + float s = 7.99f; + + float uo = (0.0f) / 128.0f; + float vo = (0.0f) / 128.0f; + + t->vertexUV((float)(0), (float)( 0 + s), (float)( 0), (float)( ix / 128.0f + uo), (float)( (iy + s) / 128.0f + vo)); + t->vertexUV((float)(0 + s), (float)( 0 + s), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( (iy + s) / 128.0f + vo)); + t->vertexUV((float)(0 + s), (float)( 0), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( iy / 128.0f + vo)); + t->vertexUV((float)(0), (float)( 0), (float)( 0), (float)( ix / 128.0f + uo), (float)( iy / 128.0f + vo)); + // target.colorBlit(texture, x + xo, y, color, ix, iy, + // charWidths[chars[i]], 8); + t->end(); + + glTranslatef((float)charWidths[i], 0, 0); + } + else + { + i = cb - 256; + + int br = ((i >> 3) & 1) * 0x55; + int r = ((i >> 2) & 1) * 0xaa + br; + int g = ((i >> 1) & 1) * 0xaa + br; + int b = ((i >> 0) & 1) * 0xaa + br; + if (i == 6) + { + r += 0x55; + } + bool darken = i >= 16; + + // color = r << 16 | g << 8 | b; + if (darken) + { + r /= 4; + g /= 4; + b /= 4; + } + glColor3f(r / 255.0f, g / 255.0f, b / 255.0f); + } + } +} + +void Font::loadUnicodePage(int page) +{ + wchar_t fileName[25]; + //String fileName = String.format("/1_2_2/font/glyph_%02X.png", page); + swprintf(fileName,25,L"/1_2_2/font/glyph_%02X.png",page); + BufferedImage *image = new BufferedImage(fileName); + //try + //{ + // image = ImageIO.read(Textures.class.getResourceAsStream(fileName.toString())); + //} + //catch (IOException e) + //{ + // throw new RuntimeException(e); + //} + + unicodeTexID[page] = textures->getTexture(image); + lastBoundTexture = unicodeTexID[page]; +} + +void Font::renderUnicodeCharacter(wchar_t c) +{ + if (unicodeWidth[c] == 0) + { + // System.out.println("no-width char " + c); + return; + } + + int page = c / 256; + + if (unicodeTexID[page] == 0) loadUnicodePage(page); + + if (lastBoundTexture != unicodeTexID[page]) + { + glBindTexture(GL_TEXTURE_2D, unicodeTexID[page]); + lastBoundTexture = unicodeTexID[page]; + } + + // first column with non-trans pixels + int firstLeft = unicodeWidth[c] >> 4; + // last column with non-trans pixels + int firstRight = unicodeWidth[c] & 0xF; + + float left = firstLeft; + float right = firstRight + 1; + + float xOff = c % 16 * 16 + left; + float yOff = (c & 0xFF) / 16 * 16; + float width = right - left - .02f; + + Tesselator *t = Tesselator::getInstance(); + t->begin(GL_TRIANGLE_STRIP); + t->tex(xOff / 256.0F, yOff / 256.0F); + t->vertex(xPos, yPos, 0.0f); + t->tex(xOff / 256.0F, (yOff + 15.98f) / 256.0F); + t->vertex(xPos, yPos + 7.99f, 0.0f); + t->tex((xOff + width) / 256.0F, yOff / 256.0F); + t->vertex(xPos + width / 2, yPos, 0.0f); + t->tex((xOff + width) / 256.0F, (yOff + 15.98f) / 256.0F); + t->vertex(xPos + width / 2, yPos + 7.99f, 0.0f); + t->end(); + + xPos += (right - left) / 2 + 1; +} +*/ + diff --git a/Minecraft.Client/Font.h b/Minecraft.Client/Font.h new file mode 100644 index 0000000..b6ad555 --- /dev/null +++ b/Minecraft.Client/Font.h @@ -0,0 +1,83 @@ +#pragma once +class IntBuffer; +class Options; +class Textures; + +class Font +{ +private: + int *charWidths; +public: + int fontTexture; + Random *random; + +private: + int colors[32]; // RGB colors for formatting + + Textures *textures; + + float xPos; + float yPos; + + bool enforceUnicodeSheet; // use unicode sheet for ascii + bool bidirectional; // use bidi to flip strings + + int m_cols; // Number of columns in font sheet + int m_rows; // Number of rows in font sheet + int m_charWidth; // Maximum character width + int m_charHeight; // Maximum character height + TEXTURE_NAME m_textureName; // Texture + std::map m_charMap; + +public: + Font(Options *options, const wstring& name, Textures* textures, bool enforceUnicode, TEXTURE_NAME textureName, int cols, int rows, int charWidth, int charHeight, unsigned short charMap[] = NULL); +#ifndef _XBOX + // 4J Stu - This dtor clashes with one in xui! We never delete these anyway so take it out for now. Can go back when we have got rid of XUI + ~Font(); +#endif + void renderFakeCB(IntBuffer *cb); // 4J added + +private: + void renderCharacter(wchar_t c); // 4J added + +public: + void drawShadow(const wstring& str, int x, int y, int color); + void drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h); // 4J Added h param + void draw(const wstring &str, int x, int y, int color); + /** + * Reorders the string according to bidirectional levels. A bit expensive at + * the moment. + * + * @param str + * @return + */ +private: + wstring reorderBidi(const wstring &str); + + void draw(const wstring &str, bool dropShadow); + void draw(const wstring& str, int x, int y, int color, bool dropShadow); + int MapCharacter(wchar_t c); // 4J added + bool CharacterExists(wchar_t c); // 4J added + +public: + int width(const wstring& str); + wstring sanitize(const wstring& str); + void drawWordWrap(const wstring &string, int x, int y, int w, int col, int h); // 4J Added h param + +private: + void drawWordWrapInternal(const wstring &string, int x, int y, int w, int col, int h); // 4J Added h param + +public: + void drawWordWrap(const wstring &string, int x, int y, int w, int col, bool darken, int h); // 4J Added h param + +private: + void drawWordWrapInternal(const wstring& string, int x, int y, int w, int col, bool darken, int h); // 4J Added h param + +public: + int wordWrapHeight(const wstring& string, int w); + void setEnforceUnicodeSheet(bool enforceUnicodeSheet); + void setBidirectional(bool bidirectional); + + // 4J-PB - check for invalid player name - Japanese local name + bool AllCharactersValid(const wstring &str); +}; diff --git a/Minecraft.Client/FootstepParticle.cpp b/Minecraft.Client/FootstepParticle.cpp new file mode 100644 index 0000000..18331b3 --- /dev/null +++ b/Minecraft.Client/FootstepParticle.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "FootstepParticle.h" +#include "Textures.h" +#include "Tesselator.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" + +FootstepParticle::FootstepParticle(Textures *textures, Level *level, double x, double y, double z) : Particle(level, x, y, z, 0, 0, 0) +{ + // 4J added initialisers + life = 0; + lifeTime = 0; + + this->textures = textures; + xd = yd = zd = 0; + lifeTime = 200; +} + +void FootstepParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float time = (life + a) / lifeTime; + time = time * time; + + float alpha = 2 - time * 2; + if (alpha > 1) alpha = 1; + alpha = alpha * 0.2f; + + glDisable(GL_LIGHTING); + float r = 2 / 16.0f; + + float xx = (float) (x - xOff); + float yy = (float) (y - yOff); + float zz = (float) (z - zOff); + + float br = level->getBrightness(Mth::floor(x), Mth::floor(y), Mth::floor(z)); + + textures->bindTexture(TN_MISC_FOOTSTEP);//L"/misc/footprint.png")); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + t->begin(); + t->color(br, br, br, alpha); + t->vertexUV((float)(xx - r), (float)( yy), (float)( zz + r), (float)( 0), (float)( 1)); + t->vertexUV((float)(xx + r), (float)( yy), (float)( zz + r), (float)( 1), (float)( 1)); + t->vertexUV((float)(xx + r), (float)( yy), (float)( zz - r), (float)( 1), (float)( 0)); + t->vertexUV((float)(xx - r), (float)( yy), (float)( zz - r), (float)( 0), (float)( 0)); + t->end(); + + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + +} + +void FootstepParticle::tick() +{ + life++; + if (life == lifeTime) remove(); +} + +int FootstepParticle::getParticleTexture() +{ + return ParticleEngine::ENTITY_PARTICLE_TEXTURE; +} \ No newline at end of file diff --git a/Minecraft.Client/FootstepParticle.h b/Minecraft.Client/FootstepParticle.h new file mode 100644 index 0000000..50e5e36 --- /dev/null +++ b/Minecraft.Client/FootstepParticle.h @@ -0,0 +1,19 @@ +#pragma once +#include "Particle.h" +class Textures; + +class FootstepParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_FOOTSTEPPARTICLE; } +private: + int life; + int lifeTime; + Textures *textures; + +public: + FootstepParticle(Textures *textures, Level *level, double x, double y, double z); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); + virtual int getParticleTexture(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Frustum.cpp b/Minecraft.Client/Frustum.cpp new file mode 100644 index 0000000..c40f7c6 --- /dev/null +++ b/Minecraft.Client/Frustum.cpp @@ -0,0 +1,149 @@ +#include "stdafx.h" +#include "..\Minecraft.World\FloatBuffer.h" +#include "Frustum.h" + +Frustum *Frustum::frustum = new Frustum(); + +Frustum::Frustum() +{ + _proj = MemoryTracker::createFloatBuffer(16); + _modl = MemoryTracker::createFloatBuffer(16); + _clip = MemoryTracker::createFloatBuffer(16); +} + +Frustum::~Frustum() +{ + delete _proj; + delete _modl; + delete _clip; +} + + +FrustumData *Frustum::getFrustum() +{ + frustum->calculateFrustum(); + return frustum; +} + + + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This normalizes a plane (A side) from a given frustum. + ///// + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +void Frustum::normalizePlane(float **frustum, int side) +{ + float magnitude = (float) sqrt(frustum[side][A] * frustum[side][A] + frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); + + // Then we divide the plane's values by it's magnitude. + // This makes it easier to work with. + frustum[side][A] /= magnitude; + frustum[side][B] /= magnitude; + frustum[side][C] /= magnitude; + frustum[side][D] /= magnitude; +} + +void Frustum::calculateFrustum() +{ + _proj->clear(); + _modl->clear(); + _clip->clear(); + + // glGetFloatv() is used to extract information about our OpenGL world. + // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. + // It then stores the matrix into an array of [16]. + glGetFloat(GL_PROJECTION_MATRIX, _proj); + + // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. + // This also stores it in an array of [16]. + glGetFloat(GL_MODELVIEW_MATRIX, _modl); + + _proj->flip()->limit(16); + _proj->get(&proj); + _modl->flip()->limit(16); + _modl->get(&modl); + + // Now that we have our modelview and projection matrix, if we combine these 2 matrices, + // it will give us our clipping planes. To combine 2 matrices, we multiply them. + + clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; + clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; + clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; + clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; + + clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; + clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; + clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; + clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; + + clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; + clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; + clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; + clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; + + clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; + clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; + clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; + clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; + + // Now we actually want to get the sides of the frustum. To do this we take + // the clipping planes we received above and extract the sides from them. + + // This will extract the RIGHT side of the frustum + m_Frustum[RIGHT][A] = clip[3] - clip[0]; + m_Frustum[RIGHT][B] = clip[7] - clip[4]; + m_Frustum[RIGHT][C] = clip[11] - clip[8]; + m_Frustum[RIGHT][D] = clip[15] - clip[12]; + + // Now that we have a normal (A,B,C) and a distance (D) to the plane, + // we want to normalize that normal and distance. + + // Normalize the RIGHT side + normalizePlane(m_Frustum, RIGHT); + + // This will extract the LEFT side of the frustum + m_Frustum[LEFT][A] = clip[3] + clip[0]; + m_Frustum[LEFT][B] = clip[7] + clip[4]; + m_Frustum[LEFT][C] = clip[11] + clip[8]; + m_Frustum[LEFT][D] = clip[15] + clip[12]; + + // Normalize the LEFT side + normalizePlane(m_Frustum, LEFT); + + // This will extract the BOTTOM side of the frustum + m_Frustum[BOTTOM][A] = clip[3] + clip[1]; + m_Frustum[BOTTOM][B] = clip[7] + clip[5]; + m_Frustum[BOTTOM][C] = clip[11] + clip[9]; + m_Frustum[BOTTOM][D] = clip[15] + clip[13]; + + // Normalize the BOTTOM side + normalizePlane(m_Frustum, BOTTOM); + + // This will extract the TOP side of the frustum + m_Frustum[TOP][A] = clip[3] - clip[1]; + m_Frustum[TOP][B] = clip[7] - clip[5]; + m_Frustum[TOP][C] = clip[11] - clip[9]; + m_Frustum[TOP][D] = clip[15] - clip[13]; + + // Normalize the TOP side + normalizePlane(m_Frustum, TOP); + + // This will extract the BACK side of the frustum + m_Frustum[BACK][A] = clip[3] - clip[2]; + m_Frustum[BACK][B] = clip[7] - clip[6]; + m_Frustum[BACK][C] = clip[11] - clip[10]; + m_Frustum[BACK][D] = clip[15] - clip[14]; + + // Normalize the BACK side + normalizePlane(m_Frustum, BACK); + + // This will extract the FRONT side of the frustum + m_Frustum[FRONT][A] = clip[3] + clip[2]; + m_Frustum[FRONT][B] = clip[7] + clip[6]; + m_Frustum[FRONT][C] = clip[11] + clip[10]; + m_Frustum[FRONT][D] = clip[15] + clip[14]; + + // Normalize the FRONT side + normalizePlane(m_Frustum, FRONT); +} \ No newline at end of file diff --git a/Minecraft.Client/Frustum.h b/Minecraft.Client/Frustum.h new file mode 100644 index 0000000..4ce3716 --- /dev/null +++ b/Minecraft.Client/Frustum.h @@ -0,0 +1,29 @@ +#pragma once +#include "FrustumData.h" + +class Frustum : public FrustumData +{ +private: + static Frustum *frustum; + +public: + static FrustumData *getFrustum(); + + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This normalizes a plane (A side) from a given frustum. + ///// + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + +private: + void normalizePlane(float **frustum, int side); + + FloatBuffer *_proj; + FloatBuffer *_modl; + FloatBuffer *_clip; + + void calculateFrustum(); + + Frustum(); + ~Frustum(); +}; \ No newline at end of file diff --git a/Minecraft.Client/FrustumCuller.cpp b/Minecraft.Client/FrustumCuller.cpp new file mode 100644 index 0000000..da4abd4 --- /dev/null +++ b/Minecraft.Client/FrustumCuller.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "FrustumCuller.h" + +FrustumCuller::FrustumCuller() +{ + frustum = Frustum::getFrustum(); +} + +void FrustumCuller::prepare(double xOff, double yOff, double zOff) +{ + this->xOff = xOff; + this->yOff = yOff; + this->zOff = zOff; +} + +bool FrustumCuller::cubeFullyInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return frustum->cubeFullyInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); +} + +bool FrustumCuller::cubeInFrustum(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return frustum->cubeInFrustum(x0 - xOff, y0 - yOff, z0 - zOff, x1 - xOff, y1 - yOff, z1 - zOff); +} + +bool FrustumCuller::isVisible(AABB *bb) +{ + return cubeInFrustum(bb->x0, bb->y0, bb->z0, bb->x1, bb->y1, bb->z1); +} \ No newline at end of file diff --git a/Minecraft.Client/FrustumCuller.h b/Minecraft.Client/FrustumCuller.h new file mode 100644 index 0000000..962f850 --- /dev/null +++ b/Minecraft.Client/FrustumCuller.h @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "Culler.h" +#include "Frustum.h" + +class FrustumCuller : public Culler +{ +public: + FrustumData *frustum; + FrustumCuller(); + double xOff, yOff, zOff; +public: + virtual void prepare(double xOff, double yOff, double zOff); + virtual bool cubeFullyInFrustum(double x0, double y0, double z0, double x1, double y1, double z1); + virtual bool cubeInFrustum(double x0, double y0, double z0, double x1, double y1, double z1); + virtual bool isVisible(AABB *bb); +}; diff --git a/Minecraft.Client/FrustumData.cpp b/Minecraft.Client/FrustumData.cpp new file mode 100644 index 0000000..177a51f --- /dev/null +++ b/Minecraft.Client/FrustumData.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "FrustumData.h" + +float** m_Frustum; + + +FrustumData::FrustumData() +{ + m_Frustum = new float *[16]; + for( int i = 0; i < 16; i++ ) m_Frustum[i] = new float[16]; + proj = floatArray( 16 ); + modl = floatArray( 16 ); + clip = floatArray( 16 ); +} + +FrustumData::~FrustumData() +{ + delete[] proj.data; + delete[] modl.data; + delete[] clip.data; + for( int i = 0; i < 16; i++ ) delete[] m_Frustum[i]; + delete[] m_Frustum; +} + +bool FrustumData::pointInFrustum(float x, float y, float z) +{ + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) + { + return false; + } + } + + return true; +} + +bool FrustumData::sphereInFrustum(float x, float y, float z, float radius) +{ + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) + { + return false; + } + } + + return true; +} + +bool FrustumData::cubeFullyInFrustum(double x1, double y1, double z1, double x2, double y2, double z2) +{ + for (int i = 0; i < 6; i++) + { + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + if (!(m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0)) return false; + } + + return true; +} + +bool FrustumData::cubeInFrustum(double x1, double y1, double z1, double x2, double y2, double z2) +{ + for (int i = 0; i < 6; i++) + { + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z1) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y1) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x1) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + if (m_Frustum[i][A] * (x2) + m_Frustum[i][B] * (y2) + m_Frustum[i][C] * (z2) + m_Frustum[i][D] > 0) continue; + + return false; + } + + return true; +} + +bool FrustumData::isVisible(AABB *aabb) +{ + return cubeInFrustum(aabb->x0, aabb->y0, aabb->z0, aabb->x1, aabb->y1, aabb->z1); +} \ No newline at end of file diff --git a/Minecraft.Client/FrustumData.h b/Minecraft.Client/FrustumData.h new file mode 100644 index 0000000..7285f14 --- /dev/null +++ b/Minecraft.Client/FrustumData.h @@ -0,0 +1,35 @@ +#pragma once +#include "..\Minecraft.World\AABB.h" + +class FrustumData +{ +public: + //enum FrustumSide + static const int RIGHT = 0; // The RIGHT side of the frustum + static const int LEFT = 1; // The LEFT side of the frustum + static const int BOTTOM = 2; // The BOTTOM side of the frustum + static const int TOP = 3; // The TOP side of the frustum + static const int BACK = 4; // The BACK side of the frustum + static const int FRONT = 5; // The FRONT side of the frustum + + // Like above, instead of saying a number for the ABC and D of the plane, we + // want to be more descriptive. + static const int A = 0; // The X value of the plane's normal + static const int B = 1; // The Y value of the plane's normal + static const int C = 2; // The Z value of the plane's normal + static const int D = 3; // The distance the plane is from the origin + + float** m_Frustum; + floatArray proj; + floatArray modl; + floatArray clip; + + FrustumData(); + ~FrustumData(); + + bool pointInFrustum(float x, float y, float z); + bool sphereInFrustum(float x, float y, float z, float radius); + bool cubeFullyInFrustum(double x1, double y1, double z1, double x2, double y2, double z2); + bool cubeInFrustum(double x1, double y1, double z1, double x2, double y2, double z2); + bool isVisible(AABB *aabb); +}; \ No newline at end of file diff --git a/Minecraft.Client/FurnaceScreen.cpp b/Minecraft.Client/FurnaceScreen.cpp new file mode 100644 index 0000000..20ec710 --- /dev/null +++ b/Minecraft.Client/FurnaceScreen.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "FurnaceScreen.h" +#include "Textures.h" +#include "LocalPlayer.h" +#include "Font.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\FurnaceTileEntity.h" + +FurnaceScreen::FurnaceScreen(shared_ptr inventory, shared_ptr furnace) : AbstractContainerScreen(new FurnaceMenu(inventory, furnace)) +{ + this->furnace = furnace; +} + +void FurnaceScreen::renderLabels() +{ + font->draw(L"Furnace", 16 + 4 + 40, 2 + 2 + 2, 0x404040); + font->draw(L"Inventory", 8, imageHeight - 96 + 2, 0x404040); +} + +void FurnaceScreen::renderBg(float a) +{ + // 4J Unused +#if 0 + int tex = minecraft->textures->loadTexture(L"/gui/furnace.png"); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, imageHeight); + if (furnace->isLit()) + { + int p = furnace->getLitProgress(12); + this->blit(xo + 56, yo + 36 + 12 - p, 176, 12 - p, 14, p + 2); + } + + int p = furnace->getBurnProgress(24); + this->blit(xo + 79, yo + 34, 176, 14, p + 1, 16); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/FurnaceScreen.h b/Minecraft.Client/FurnaceScreen.h new file mode 100644 index 0000000..f018093 --- /dev/null +++ b/Minecraft.Client/FurnaceScreen.h @@ -0,0 +1,17 @@ +#pragma once +#include "AbstractContainerScreen.h" + +class FurnaceTileEntity; +class Inventory; + +class FurnaceScreen : public AbstractContainerScreen +{ +private: + shared_ptr furnace; + +public: + FurnaceScreen(shared_ptr inventory, shared_ptr furnace); +protected: + virtual void renderLabels(); + virtual void renderBg(float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/GameMode.cpp b/Minecraft.Client/GameMode.cpp new file mode 100644 index 0000000..a11d6c0 --- /dev/null +++ b/Minecraft.Client/GameMode.cpp @@ -0,0 +1,182 @@ +#include "stdafx.h" +#include "GameMode.h" +#include "LocalPlayer.h" +#include "LevelRenderer.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" + +GameMode::GameMode(Minecraft *minecraft) +{ + instaBuild = false; // 4J - added + this->minecraft = minecraft; +} + +void GameMode::initLevel(Level *level) +{ +} + +bool GameMode::destroyBlock(int x, int y, int z, int face) +{ + Level *level = minecraft->level; + Tile *oldTile = Tile::tiles[level->getTile(x, y, z)]; + if (oldTile == NULL) return false; + + // 4J - Let the rendering side of thing know we are about to destroy the tile, so we can synchronise collision with async render data upates. + minecraft->levelRenderer->destroyedTileManager->destroyingTileAt(level, x, y, z); + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT)); + int data = level->getData(x, y, z); + // 4J - before we remove the tile, recalc the heightmap - setTile depends on this being valid to be able to do + // a quick update of skylighting when the block is removed, and there are cases with falling tiles where this can get out of sync + level->getChunkAt(x,z)->recalcHeightmapOnly(); + bool changed = level->setTile(x, y, z, 0); + + if (oldTile != NULL && changed) + { + oldTile->destroy(level, x, y, z, data); + } + return changed; +} + +void GameMode::render(float a) +{ +} + +bool GameMode::useItem(shared_ptr player, Level *level, shared_ptr item, bool bTestUseOnly) +{ +} + +void GameMode::initPlayer(shared_ptr player) +{ +} + +void GameMode::tick() +{ +} + +void GameMode::adjustPlayer(shared_ptr player) +{ +} + +//bool GameMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, bool bTestUseOnOnly) +//{ +// // 4J-PB - Adding a test only version to allow tooltips to be displayed +// int t = level->getTile(x, y, z); +// if (t > 0) +// { +// if(bTestUseOnOnly) +// { +// switch(t) +// { +// case Tile::recordPlayer_Id: +// case Tile::bed_Id: // special case for a bed +// if (Tile::tiles[t]->TestUse(level, x, y, z, player )) +// { +// return true; +// } +// else +// { +// // bed is too far away, or something +// return false; +// } +// break; +// default: +// if (Tile::tiles[t]->TestUse()) return true; +// break; +// } +// } +// else +// { +// if (Tile::tiles[t]->use(level, x, y, z, player )) return true; +// } +// } +// +// if (item == NULL) return false; +// return item->useOn(player, level, x, y, z, face, bTestUseOnOnly); +//} + + +shared_ptr GameMode::createPlayer(Level *level) +{ + return shared_ptr( new LocalPlayer(minecraft, level, minecraft->user, level->dimension->id) ); +} + +bool GameMode::interact(shared_ptr player, shared_ptr entity) +{ + return player->interact(entity); +} + +void GameMode::attack(shared_ptr player, shared_ptr entity) +{ + player->attack(entity); +} + +shared_ptr GameMode::handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, bool quickKeyHeld, shared_ptr player) +{ + return nullptr; +} + +void GameMode::handleCloseInventory(int containerId, shared_ptr player) +{ + player->containerMenu->removed(player); + delete player->containerMenu; + player->containerMenu = player->inventoryMenu; +} + +void GameMode::handleInventoryButtonClick(int containerId, int buttonId) +{ + +} + +bool GameMode::isCutScene() +{ + return false; +} + +void GameMode::releaseUsingItem(shared_ptr player) +{ + player->releaseUsingItem(); +} + +bool GameMode::hasExperience() +{ + return false; +} + +bool GameMode::hasMissTime() +{ + return true; +} + +bool GameMode::hasInfiniteItems() +{ + return false; +} + +bool GameMode::hasFarPickRange() +{ + return false; +} + +void GameMode::handleCreativeModeItemAdd(shared_ptr clicked, int i) +{ +} + +void GameMode::handleCreativeModeItemDrop(shared_ptr clicked) +{ +} + +bool GameMode::handleCraftItem(int recipe, shared_ptr player) +{ + return true; +} + +// 4J-PB +void GameMode::handleDebugOptions(unsigned int uiVal, shared_ptr player) +{ + player->SetDebugOptions(uiVal); +} diff --git a/Minecraft.Client/GameMode.h b/Minecraft.Client/GameMode.h new file mode 100644 index 0000000..ab9ec9d --- /dev/null +++ b/Minecraft.Client/GameMode.h @@ -0,0 +1,59 @@ +#pragma once + +class Minecraft; +class Level; +class Player; +class ItemInstance; +class Entity; + +class Tutorial; + +class GameMode +{ +protected: + Minecraft *minecraft; +public: + bool instaBuild; + + GameMode(Minecraft *minecraft); + virtual ~GameMode() {} + + virtual void initLevel(Level *level) ; + virtual void startDestroyBlock(int x, int y, int z, int face) = 0; + virtual bool destroyBlock(int x, int y, int z, int face); + virtual void continueDestroyBlock(int x, int y, int z, int face) = 0; + virtual void stopDestroyBlock() = 0; + virtual void render(float a); + virtual float getPickRange() = 0; + virtual void initPlayer(shared_ptr player); + virtual void tick(); + virtual bool canHurtPlayer() = 0; + virtual void adjustPlayer(shared_ptr player); + virtual bool useItem(shared_ptr player, Level *level, shared_ptr item, bool bTestUseOnly=false); + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, bool bTestUseOnOnly=false, bool *pbUsedItem = NULL) = 0; + + virtual shared_ptr createPlayer(Level *level); + virtual bool interact(shared_ptr player, shared_ptr entity); + virtual void attack(shared_ptr player, shared_ptr entity); + virtual shared_ptr handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, bool quickKeyHeld, shared_ptr player); + virtual void handleCloseInventory(int containerId, shared_ptr player); + virtual void handleInventoryButtonClick(int containerId, int buttonId); + + virtual bool isCutScene(); + virtual void releaseUsingItem(shared_ptr player); + virtual bool hasExperience(); + virtual bool hasMissTime(); + virtual bool hasInfiniteItems(); + virtual bool hasFarPickRange(); + virtual void handleCreativeModeItemAdd(shared_ptr clicked, int i); + virtual void handleCreativeModeItemDrop(shared_ptr clicked); + + // 4J Stu - Added so we can send packets for this in the network game + virtual bool handleCraftItem(int recipe, shared_ptr player); + virtual void handleDebugOptions(unsigned int uiVal, shared_ptr player); + + // 4J Stu - Added for tutorial checks + virtual bool isInputAllowed(int mapping) { return true; } + virtual bool isTutorial() { return false; } + virtual Tutorial *getTutorial() { return NULL; } +}; diff --git a/Minecraft.Client/GameRenderer.cpp b/Minecraft.Client/GameRenderer.cpp new file mode 100644 index 0000000..508b3a1 --- /dev/null +++ b/Minecraft.Client/GameRenderer.cpp @@ -0,0 +1,2169 @@ +#include "stdafx.h" +#include "GameRenderer.h" +#include "ItemInHandRenderer.h" +#include "LevelRenderer.h" +#include "Frustum.h" +#include "FrustumCuller.h" +#include "Textures.h" +#include "Tesselator.h" +#include "ParticleEngine.h" +#include "SmokeParticle.h" +#include "WaterDropParticle.h" +#include "GameMode.h" +#include "CreativeMode.h" +#include "Lighting.h" +#include "Options.h" +#include "MultiplayerLocalPlayer.h" +#include "GuiParticles.h" +#include "MultiPlayerLevel.h" +#include "Chunk.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\net.minecraft.world.level.material.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.level.biome.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\FloatBuffer.h" +#include "..\Minecraft.World\ThreadName.h" +#include "..\Minecraft.World\SparseLightStorage.h" +#include "..\Minecraft.World\CompressedTileStorage.h" +#include "..\Minecraft.World\SparseDataStorage.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Facing.h" +#include "..\Minecraft.World\MobEffect.h" +#include "..\Minecraft.World\IntCache.h" +#include "..\Minecraft.World\SmoothFloat.h" +#include "..\Minecraft.World\MobEffectInstance.h" +#include "..\Minecraft.World\Item.h" +#include "Camera.h" +#include "..\Minecraft.World\SoundTypes.h" +#include "HumanoidModel.h" +#include "..\Minecraft.World\Item.h" +#include "..\Minecraft.World\compression.h" +#include "PS3\PS3Extras\ShutdownManager.h" + +#include "TexturePackRepository.h" +#include "TexturePack.h" + +bool GameRenderer::anaglyph3d = false; +int GameRenderer::anaglyphPass = 0; + +#ifdef MULTITHREAD_ENABLE +C4JThread* GameRenderer::m_updateThread; +C4JThread::EventArray* GameRenderer::m_updateEvents; +bool GameRenderer::nearThingsToDo = false; +bool GameRenderer::updateRunning = false; +vector GameRenderer::m_deleteStackByte; +vector GameRenderer::m_deleteStackSparseLightStorage; +vector GameRenderer::m_deleteStackCompressedTileStorage; +vector GameRenderer::m_deleteStackSparseDataStorage; +#endif +CRITICAL_SECTION GameRenderer::m_csDeleteStack; + +GameRenderer::GameRenderer(Minecraft *mc) +{ + // 4J - added this block of initialisers + renderDistance = 0; + _tick = 0; + hovered = nullptr; + thirdDistance = 4; + thirdDistanceO = 4; + thirdRotation = 0; + thirdRotationO = 0; + thirdTilt = 0; + thirdTiltO = 0; + + accumulatedSmoothXO = 0; + accumulatedSmoothYO = 0; + tickSmoothXO = 0; + tickSmoothYO = 0; + lastTickA = 0; + + cameraPos = Vec3::newPermanent(0.0f,0.0f,0.0f); + + fovOffset = 0; + fovOffsetO = 0; + cameraRoll = 0; + cameraRollO = 0; + for( int i = 0; i < 4; i++ ) + { + fov[i] = 0.0f; + oFov[i] = 0.0f; + tFov[i] = 0.0f; + } + isInClouds = false; + zoom = 1; + zoom_x = 0; + zoom_y = 0; + rainXa = NULL; + rainZa = NULL; + lastActiveTime = Minecraft::currentTimeMillis(); + lastNsTime = 0; + random = new Random(); + rainSoundTime = 0; + xMod = 0; + yMod = 0; + lb = MemoryTracker::createFloatBuffer(16); + fr = 0.0f; + fg = 0.0f; + fb = 0.0f; + fogBrO = 0.0f; + fogBr = 0.0f; + cameraFlip = 0; + _updateLightTexture = false; + blr = 0.0f; + blrt = 0.0f; + blg = 0.0f; + blgt = 0.0f; + + + m_fov=70.0f; + + // 4J Stu - Init these so they are setup before the tick + for( int i = 0; i < 4; i++ ) + { + fov[i] = oFov[i] = 1.0f; + } + + this->mc = mc; + itemInHandRenderer = NULL; + + // 4J-PB - set up the local players iteminhand renderers here - needs to be done with lighting enabled so that the render geometry gets compiled correctly + glEnable(GL_LIGHTING); + mc->localitemInHandRenderers[0] = new ItemInHandRenderer(mc);//itemInHandRenderer; + mc->localitemInHandRenderers[1] = new ItemInHandRenderer(mc); + mc->localitemInHandRenderers[2] = new ItemInHandRenderer(mc); + mc->localitemInHandRenderers[3] = new ItemInHandRenderer(mc); + glDisable(GL_LIGHTING); + + // 4J - changes brought forward from 1.8.2 + BufferedImage *img = new BufferedImage(16, 16, BufferedImage::TYPE_INT_RGB); + for( int i = 0; i < NUM_LIGHT_TEXTURES; i++ ) + { + lightTexture[i] = mc->textures->getTexture(img); // 4J - changed to one light texture per level to support split screen + } + delete img; +#ifdef __PS3__ + // we're using the RSX now to upload textures to vram, so we need the main ram textures allocated from io space + for(int i=0;iSet(eUpdateEventIsFinished); + + InitializeCriticalSection(&m_csDeleteStack); + m_updateThread = new C4JThread(runUpdate, NULL, "Chunk update"); +#ifdef __PS3__ + m_updateThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); +#endif// __PS3__ + m_updateThread->SetProcessor(CPU_CORE_CHUNK_UPDATE); + m_updateThread->Run(); +#endif +} + +// 4J Stu Added to go with 1.8.2 change +GameRenderer::~GameRenderer() +{ + if(rainXa != NULL) delete [] rainXa; + if(rainZa != NULL) delete [] rainZa; +} + +void GameRenderer::tick(bool first) // 4J - add bFirst +{ + tickFov(); + tickLightTexture(); // 4J - change brought forward from 1.8.2 + fogBrO = fogBr; + thirdDistanceO = thirdDistance; + thirdRotationO = thirdRotation; + thirdTiltO = thirdTilt; + fovOffsetO = fovOffset; + cameraRollO = cameraRoll; + + if (ClientConstants::DEADMAU5_CAMERA_CHEATS) + { + if (mc->screen == NULL) + { + float distanceDelta = 0; + float rotationDelta = 0; + float tiltDelta = 0; + float rollDelta = 0; + + if (Keyboard::isKeyDown(Keyboard::KEY_U)) + { + distanceDelta -= .3f * mc->options->cameraSpeed; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_O)) + { + distanceDelta += .3f * mc->options->cameraSpeed; + } + if (Keyboard::isKeyDown(Keyboard::KEY_J)) + { + rotationDelta += 8.0f * mc->options->cameraSpeed; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_L)) + { + rotationDelta -= 8.0f * mc->options->cameraSpeed; + } + if (Keyboard::isKeyDown(Keyboard::KEY_I)) + { + tiltDelta += 6.0f * mc->options->cameraSpeed; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_K)) + { + tiltDelta -= 6.0f * mc->options->cameraSpeed; + } + if (Keyboard::isKeyDown(Keyboard::KEY_Y) && Keyboard::isKeyDown(Keyboard::KEY_H)) + { + fovOffset = 0; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_Y)) + { + fovOffset -= 3.0f; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_H)) + { + fovOffset += 3.0f; + } + if (Keyboard::isKeyDown(Keyboard::KEY_N) && Keyboard::isKeyDown(Keyboard::KEY_M)) + { + cameraRoll = 0; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_N)) + { + rollDelta -= 8.0f * mc->options->cameraSpeed; + } + else if (Keyboard::isKeyDown(Keyboard::KEY_M)) + { + rollDelta += 8.0f * mc->options->cameraSpeed; + } + + if (mc->options->smoothCamera) + { + distanceDelta = smoothDistance.getNewDeltaValue(distanceDelta, .5f * mc->options->sensitivity); + rotationDelta = smoothRotation.getNewDeltaValue(rotationDelta, .5f * mc->options->sensitivity); + tiltDelta = smoothTilt.getNewDeltaValue(tiltDelta, .5f * mc->options->sensitivity); + rollDelta = smoothRoll.getNewDeltaValue(rollDelta, .5f * mc->options->sensitivity); + } + thirdDistance += distanceDelta; + thirdRotation += rotationDelta; + thirdTilt += tiltDelta; + cameraRoll += rollDelta; + } + } + + if (mc->options->smoothCamera) + { + // update player view in tick() instead of render() to maintain +// camera movement regardless of FPS + float ss = mc->options->sensitivity * 0.6f + 0.2f; + float sens = (ss * ss * ss) * 8; + tickSmoothXO = smoothTurnX.getNewDeltaValue(accumulatedSmoothXO, 0.05f * sens); + tickSmoothYO = smoothTurnY.getNewDeltaValue(accumulatedSmoothYO, 0.05f * sens); + lastTickA = 0; + + accumulatedSmoothXO = 0; + accumulatedSmoothYO = 0; + } + + if (mc->cameraTargetPlayer == NULL) + { + mc->cameraTargetPlayer = dynamic_pointer_cast(mc->player); + } + + float brr = mc->level->getBrightness(Mth::floor(mc->cameraTargetPlayer->x), Mth::floor(mc->cameraTargetPlayer->y), Mth::floor(mc->cameraTargetPlayer->z)); + float whiteness = (3 - mc->options->viewDistance) / 3.0f; + float fogBrT = brr * (1 - whiteness) + whiteness; + fogBr += (fogBrT - fogBr) * 0.1f; + + itemInHandRenderer->tick(); + + PIXBeginNamedEvent(0,"Rain tick"); + tickRain(); + PIXEndNamedEvent(); + + if( mc->player != mc->localplayers[ProfileManager.GetPrimaryPad()] ) return; // 4J added for split screen - only do rest of processing for once per frame + + _tick++; +} + +void GameRenderer::pick(float a) +{ + if (mc->cameraTargetPlayer == NULL) return; + if (mc->level == NULL) return; + + double range = mc->gameMode->getPickRange(); + delete mc->hitResult; + MemSect(31); + mc->hitResult = mc->cameraTargetPlayer->pick(range, a); + MemSect(0); + + // 4J - added - stop blocks right at the edge of the world from being pickable so we shouldn't be able to directly destroy or create anything there + if( mc->hitResult ) + { + int maxxz = ( ( mc->level->chunkSource->m_XZSize / 2 ) * 16 ) - 2; + int minxz = ( -( mc->level->chunkSource->m_XZSize / 2 ) * 16 ) + 1; + + // Don't select the tops of the very edge blocks, or the sides of the next blocks in + // 4J Stu - Only block the sides that are facing an outside block + int hitx = mc->hitResult->x; + int hitz = mc->hitResult->z; + int face = mc->hitResult->f; + if( face == Facing::WEST && hitx < 0 ) hitx -= 1; + if( face == Facing::EAST && hitx > 0 ) hitx += 1; + if( face == Facing::NORTH && hitz < 0 ) hitz -= 1; + if( face == Facing::SOUTH && hitz > 0 ) hitz += 1; + + if( ( hitx < minxz ) || ( hitx > maxxz) || + ( hitz < minxz ) || ( hitz > maxxz) ) + { + delete mc->hitResult; + mc->hitResult = NULL; + } + } + + double dist = range; + Vec3 *from = mc->cameraTargetPlayer->getPos(a); + + if (mc->gameMode->hasFarPickRange()) + { + dist = range = 6; + } + else + { + if (dist > 3) dist = 3; + range = dist; + } + + if (mc->hitResult != NULL) + { + dist = mc->hitResult->pos->distanceTo(from); + } + + Vec3 *b = mc->cameraTargetPlayer->getViewVector(a); + Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); + hovered = nullptr; + float overlap = 1; + vector > *objects = mc->level->getEntities(mc->cameraTargetPlayer, mc->cameraTargetPlayer->bb->expand(b->x * (range), b->y * (range), b->z * (range))->grow(overlap, overlap, overlap)); + double nearest = dist; + + AUTO_VAR(itEnd, objects->end()); + for (AUTO_VAR(it, objects->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //objects->at(i); + if (!e->isPickable()) continue; + + float rr = e->getPickRadius(); + AABB *bb = e->bb->grow(rr, rr, rr); + HitResult *p = bb->clip(from, to); + if (bb->contains(from)) + { + if (0 < nearest || nearest == 0) + { + hovered = e; + nearest = 0; + } + } + else if (p != NULL) + { + double dd = from->distanceTo(p->pos); + if (dd < nearest || nearest == 0) + { + hovered = e; + nearest = dd; + } + } + delete p; + } + + if (hovered != NULL) + { + if (nearest < dist || (mc->hitResult == NULL)) + { + if( mc->hitResult != NULL ) + delete mc->hitResult; + mc->hitResult = new HitResult(hovered); + } + } +} + +void GameRenderer::SetFovVal(float fov) +{ + m_fov=fov; +} + +float GameRenderer::GetFovVal() +{ + return m_fov; +} + +void GameRenderer::tickFov() +{ + shared_ptrplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + + int playerIdx = player ? player->GetXboxPad() : 0; + tFov[playerIdx] = player->getFieldOfViewModifier(); + + oFov[playerIdx] = fov[playerIdx]; + fov[playerIdx] += (tFov[playerIdx] - fov[playerIdx]) * 0.5f; +} + +float GameRenderer::getFov(float a, bool applyEffects) +{ + if (cameraFlip > 0 ) return 90; + + shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); + int playerIdx = player ? player->GetXboxPad() : 0; + float fov = m_fov;//70; + if (applyEffects) + { + fov += mc->options->fov * 40; + fov *= this->oFov[playerIdx] + (this->fov[playerIdx] - this->oFov[playerIdx]) * a; + } + if (player->getHealth() <= 0) + { + float duration = player->deathTime + a; + + fov /= ((1 - 500 / (duration + 500)) * 2.0f + 1); + } + + int t = Camera::getBlockAt(mc->level, player, a); + if (t != 0 && Tile::tiles[t]->material == Material::water) fov = fov * 60 / 70; + + return fov + fovOffsetO + (fovOffset - fovOffsetO) * a; + +} + +void GameRenderer::bobHurt(float a) +{ + shared_ptr player = mc->cameraTargetPlayer; + + float hurt = player->hurtTime - a; + + if (player->getHealth() <= 0) + { + float duration = player->deathTime + a; + + glRotatef(40 - (40 * 200) / (duration + 200), 0, 0, 1); + } + + if (hurt < 0) return; + hurt /= player->hurtDuration; + hurt = (float) Mth::sin(hurt * hurt * hurt * hurt * PI); + + float rr = player->hurtDir; + + + glRotatef(-rr, 0, 1, 0); + glRotatef(-hurt * 14, 0, 0, 1); + glRotatef(+rr, 0, 1, 0); + +} + +void GameRenderer::bobView(float a) +{ + shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); + if(player==NULL) + { + return; + } + //shared_ptr player = dynamic_pointer_cast(mc->cameraTargetPlayer); + + float wda = player->walkDist - player->walkDistO; + float b = -(player->walkDist + wda * a); + float bob = player->oBob + (player->bob - player->oBob) * a; + float tilt = player->oTilt + (player->tilt - player->oTilt) * a; + glTranslatef((float) Mth::sin(b * PI) * bob * 0.5f, -(float) abs(Mth::cos(b * PI) * bob), 0); + glRotatef((float) Mth::sin(b * PI) * bob * 3, 0, 0, 1); + glRotatef((float) abs(Mth::cos(b * PI - 0.2f) * bob) * 5, 1, 0, 0); + glRotatef((float) tilt, 1, 0, 0); +} + +void GameRenderer::moveCameraToPlayer(float a) +{ + shared_ptr player = mc->cameraTargetPlayer; + shared_ptr localplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + float heightOffset = player->heightOffset - 1.62f; + + double x = player->xo + (player->x - player->xo) * a; + double y = player->yo + (player->y - player->yo) * a - heightOffset; + double z = player->zo + (player->z - player->zo) * a; + + + glRotatef(cameraRollO + (cameraRoll - cameraRollO) * a, 0, 0, 1); + + if (player->isSleeping()) + { + heightOffset += 1.0; + glTranslatef(0.0f, 0.3f, 0); + if (!mc->options->fixedCamera) + { + int t = mc->level->getTile(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + if (t == Tile::bed_Id) + { + int data = mc->level->getData(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + int direction = data & 3; + glRotatef((float)direction * 90,0.0f, 1.0f, 0.0f); + } + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, -1, 0); + glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, -1, 0, 0); + } + } + // 4J-PB - changing this to be per player + //else if (mc->options->thirdPersonView) + else if (localplayer->ThirdPersonView()) + { + double cameraDist = thirdDistanceO + (thirdDistance - thirdDistanceO) * a; + + if (mc->options->fixedCamera) + { + + float rotationY = thirdRotationO + (thirdRotation - thirdRotationO) * a; + float xRot = thirdTiltO + (thirdTilt - thirdTiltO) * a; + + glTranslatef(0, 0, (float) -cameraDist); + glRotatef(xRot, 1, 0, 0); + glRotatef(rotationY, 0, 1, 0); + } + else + { + // 4J - corrected bug where this used to just take player->xRot & yRot directly and so wasn't taking into account interpolation, allowing camera to go through walls + float yRot = player->yRotO + (player->yRot - player->yRotO) * a; + float xRot = player->xRotO + (player->xRot - player->xRotO) * a; + + // Thirdperson view values are now 0 for disabled, 1 for original mode, 2 for reversed. + if( localplayer->ThirdPersonView() == 2 ) + { + // Reverse y rotation - note that this is only used in doing collision to calculate our view + // distance, the actual rotation itself is just below this else {} block + yRot += 180.0f; + } + + double xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; + double zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI) * cameraDist; + double yd = -Mth::sin(xRot / 180 * PI) * cameraDist; + + for (int i = 0; i < 8; i++) + { + float xo = (float)((i & 1) * 2 - 1); + float yo = (float)(((i >> 1) & 1) * 2 - 1); + float zo = (float)(((i >> 2) & 1) * 2 - 1); + + xo *= 0.1f; + yo *= 0.1f; + zo *= 0.1f; + + // 4J - corrected bug here where zo was also added to x component + HitResult *hr = mc->level->clip(Vec3::newTemp(x + xo, y + yo, z + zo), Vec3::newTemp(x - xd + xo, y - yd + yo, z - zd + zo)); + if (hr != NULL) + { + double dist = hr->pos->distanceTo(Vec3::newTemp(x, y, z)); + if (dist < cameraDist) cameraDist = dist; + delete hr; + } + } + + // 4J - removed extra rotations here that aren't needed because our xRot/yRot don't ever + // deviate from the player's view direction +// glRotatef(player->xRot - xRot, 1, 0, 0); +// glRotatef(player->yRot - yRot, 0, 1, 0); + glTranslatef(0, 0, (float) -cameraDist); +// glRotatef(yRot - player->yRot, 0, 1, 0); +// glRotatef(xRot - player->xRot, 1, 0, 0); + } + } + else + { + glTranslatef(0, 0, -0.1f); + } + + if (!mc->options->fixedCamera) + { + glRotatef(player->xRotO + (player->xRot - player->xRotO) * a, 1, 0, 0); + if( localplayer->ThirdPersonView() == 2 ) + { + // Third person view is now 0 for disabled, 1 for original, 2 for flipped + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); + } + else + { + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a + 180, 0, 1, 0); + } + } + + glTranslatef(0, heightOffset, 0); + + x = player->xo + (player->x - player->xo) * a; + y = player->yo + (player->y - player->yo) * a - heightOffset; + z = player->zo + (player->z - player->zo) * a; + + isInClouds = mc->levelRenderer->isInCloud(x, y, z, a); + +} + + +void GameRenderer::zoomRegion(double zoom, double xa, double ya) +{ + this->zoom = zoom; + this->zoom_x = xa; + this->zoom_y = ya; +} + +void GameRenderer::unZoomRegion() +{ + zoom = 1; +} + +// 4J added as we have more complex adjustments to make for fov & aspect on account of viewports +void GameRenderer::getFovAndAspect(float& fov, float& aspect, float a, bool applyEffects) +{ + // 4J - split out aspect ratio and fov here so we can adjust for viewports - we might need to revisit these as + // they are maybe be too generous for performance. + aspect = mc->width / (float) mc->height; + fov = getFov(a, applyEffects); + + if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP ) || + ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM ) ) + { + aspect *= 2.0f; + fov *= 0.7f; // Reduce FOV to make things less fish-eye, at the expense of reducing vertical FOV from single player mode + } + else if( ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT ) || + ( mc->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT) ) + { + // Ideally I'd like to make the fov bigger here, but if I do then you an see that the arm isn't very long... + aspect *= 0.5f; + } +} + +void GameRenderer::setupCamera(float a, int eye) +{ + renderDistance = (float)(16 * 16 >> (mc->options->viewDistance)); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + float stereoScale = 0.07f; + if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0); + + // 4J - have split out fov & aspect calculation so we can take into account viewports + float aspect, fov; + getFovAndAspect(fov, aspect, a, true); + + if (zoom != 1) + { + glTranslatef((float) zoom_x, (float) -zoom_y, 0); + glScaled(zoom, zoom, 1); + } + gluPerspective(fov, aspect, 0.05f, renderDistance * 2); + + if (mc->gameMode->isCutScene()) + { + float s = 1 / 1.5f; + glScalef(1, s, 1); + } + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0); + + bobHurt(a); + + // 4J-PB - this is a per-player option + //if (mc->options->bobView) bobView(a); + + bool bNoLegAnim =(mc->player->getAnimOverrideBitmask()&(1<player->getAnimOverrideBitmask()&(1<player->GetXboxPad(),eGameSetting_ViewBob) && !mc->player->abilities.flying && !bNoLegAnim && !bNoBobbingAnim) bobView(a); + + float pt = mc->player->oPortalTime + (mc->player->portalTime - mc->player->oPortalTime) * a; + if (pt > 0) + { + int multiplier = 20; + if (mc->player->hasEffect(MobEffect::confusion)) + { + multiplier = 7; + } + + float skew = 5 / (pt * pt + 5) - pt * 0.04f; + skew *= skew; + glRotatef((_tick + a) * multiplier, 0, 1, 1); + glScalef(1 / skew, 1, 1); + glRotatef(-(_tick + a) * multiplier, 0, 1, 1); + } + + + moveCameraToPlayer(a); + + if (cameraFlip > 0) + { + int i = cameraFlip - 1; + if (i == 1) glRotatef(90, 0, 1, 0); + if (i == 2) glRotatef(180, 0, 1, 0); + if (i == 3) glRotatef(-90, 0, 1, 0); + if (i == 4) glRotatef(90, 1, 0, 0); + if (i == 5) glRotatef(-90, 1, 0, 0); + } +} + +void GameRenderer::renderItemInHand(float a, int eye) +{ + if (cameraFlip > 0) return; + + shared_ptr localplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + + // 4J-PB - to turn off the hand for screenshots, but not when the item held is a map + if ( localplayer!=NULL) + { + shared_ptr item = localplayer->inventory->getSelected(); + if(!(item && item->getItem()->id==Item::map_Id) && app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_DisplayHand)==0 ) return; + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + + float stereoScale = 0.07f; + if (mc->options->anaglyph3d) glTranslatef(-(eye * 2 - 1) * stereoScale, 0, 0); + + // 4J - have split out fov & aspect calculation so we can take into account viewports + float fov, aspect; + getFovAndAspect(fov, aspect, a, false); + + if (zoom != 1) + { + glTranslatef((float) zoom_x, (float) -zoom_y, 0); + glScaled(zoom, zoom, 1); + } + gluPerspective(fov, aspect, 0.05f, renderDistance * 2); + + if (mc->gameMode->isCutScene()) + { + float s = 1 / 1.5f; + glScalef(1, s, 1); + } + + glMatrixMode(GL_MODELVIEW); + + + glLoadIdentity(); + if (mc->options->anaglyph3d) glTranslatef((eye * 2 - 1) * 0.10f, 0, 0); + + glPushMatrix(); + bobHurt(a); + + // 4J-PB - changing this to be per player + //if (mc->options->bobView) bobView(a); + bool bNoLegAnim =(localplayer->getAnimOverrideBitmask()&( (1<GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a); + + // 4J-PB - changing this to be per player + //if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) + if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) + { + if (!mc->options->hideGui && !mc->gameMode->isCutScene()) + { + turnOnLightLayer(a); + PIXBeginNamedEvent(0,"Item in hand render"); + itemInHandRenderer->render(a); + PIXEndNamedEvent(); + turnOffLightLayer(a); + } + } + glPopMatrix(); + // 4J-PB - changing this to be per player + //if (!mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) + if (!localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) + { + itemInHandRenderer->renderScreenEffect(a); + bobHurt(a); + } + // 4J-PB - changing this to be per player + //if (mc->options->bobView) bobView(a); + if(app.GetGameSettings(localplayer->GetXboxPad(),eGameSetting_ViewBob) && !localplayer->abilities.flying && !bNoLegAnim) bobView(a); +} + +// 4J - change brought forward from 1.8.2 +void GameRenderer::turnOffLightLayer(double alpha) +{ // 4J - TODO +#if 0 + if (SharedConstants::TEXTURE_LIGHTING) + { + glClientActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glClientActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); + } +#endif + RenderManager.TextureBindVertex(-1); +} + +// 4J - change brought forward from 1.8.2 +void GameRenderer::turnOnLightLayer(double alpha) +{ // 4J - TODO +#if 0 + if (SharedConstants::TEXTURE_LIGHTING) + { + glClientActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE1); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); +// float s = 1 / 16f / 15.0f*16/14.0f; + float s = 1 / 16.0f / 15.0f * 15 / 16; + glScalef(s, s, s); + glTranslatef(8f, 8f, 8f); + glMatrixMode(GL_MODELVIEW); + + mc->textures->bind(lightTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glColor4f(1, 1, 1, 1); + glEnable(GL_TEXTURE_2D); + glClientActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); + } +#endif + RenderManager.TextureBindVertex(getLightTexture(mc->player->GetXboxPad(), mc->level)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); +} + +// 4J - change brought forward from 1.8.2 +void GameRenderer::tickLightTexture() +{ + blrt += (float)((Math::random() - Math::random()) * Math::random() * Math::random()); + blgt += (float)((Math::random() - Math::random()) * Math::random() * Math::random()); + blrt *= 0.9; + blgt *= 0.9; + blr += (blrt - blr) * 1; + blg += (blgt - blg) * 1; + _updateLightTexture = true; +} + +void GameRenderer::updateLightTexture(float a) +{ + // 4J-JEV: Now doing light textures on PER PLAYER basis. + // 4J - we *had* added separate light textures for all dimensions, and this loop to update them all here + for(int j = 0; j < XUSER_MAX_COUNT; j++ ) + { + // Loop over all the players + shared_ptr player = Minecraft::GetInstance()->localplayers[j]; + if (player == NULL) continue; + + Level *level = player->level; // 4J - was mc->level when it was just to update the one light texture + + float skyDarken1 = level->getSkyDarken((float) 1); + for (int i = 0; i < 256; i++) + { + float darken = skyDarken1 * 0.95f + 0.05f; + float sky = level->dimension->brightnessRamp[i / 16] * darken; + float block = level->dimension->brightnessRamp[i % 16] * (blr * 0.1f + 1.5f); + + if (level->lightningBoltTime > 0) + { + sky = level->dimension->brightnessRamp[i / 16]; + } + + float rs = sky * (skyDarken1 * 0.65f + 0.35f); + float gs = sky * (skyDarken1 * 0.65f + 0.35f); + float bs = sky; + + float rb = block; + float gb = block * ((block * 0.6f + 0.4f) * 0.6f + 0.4f); + float bb = block * ((block * block) * 0.6f + 0.4f); + + float _r = (rs + rb); + float _g = (gs + gb); + float _b = (bs + bb); + + _r = _r * 0.96f + 0.03f; + _g = _g * 0.96f + 0.03f; + _b = _b * 0.96f + 0.03f; + + if (level->dimension->id == 1) + { + _r = (0.22f + rb * 0.75f); + _g = (0.28f + gb * 0.75f); + _b = (0.25f + bb * 0.75f); + } + + if (player->hasEffect(MobEffect::nightVision)) + { + float scale = getNightVisionScale(player, a); + { + float dist = 1.0f / _r; + if (dist > (1.0f / _g)) + { + dist = (1.0f / _g); + } + if (dist > (1.0f / _b)) + { + dist = (1.0f / _b); + } + _r = _r * (1.0f - scale) + (_r * dist) * scale; + _g = _g * (1.0f - scale) + (_g * dist) * scale; + _b = _b * (1.0f - scale) + (_b * dist) * scale; + } + } + + float brightness = 0.0f; // 4J - TODO - was mc->options->gamma; + if (_r > 1) _r = 1; + if (_g > 1) _g = 1; + if (_b > 1) _b = 1; + + float ir = 1 - _r; + float ig = 1 - _g; + float ib = 1 - _b; + ir = 1 - (ir * ir * ir * ir); + ig = 1 - (ig * ig * ig * ig); + ib = 1 - (ib * ib * ib * ib); + _r = _r * (1 - brightness) + ir * brightness; + _g = _g * (1 - brightness) + ig * brightness; + _b = _b * (1 - brightness) + ib * brightness; + + + _r = _r * 0.96f + 0.03f; + _g = _g * 0.96f + 0.03f; + _b = _b * 0.96f + 0.03f; + + + if (_r > 1) _r = 1; + if (_g > 1) _g = 1; + if (_b > 1) _b = 1; + if (_r < 0) _r = 0; + if (_g < 0) _g = 0; + if (_b < 0) _b = 0; + + int a = 255; + int r = (int) (_r * 255); + int g = (int) (_g * 255); + int b = (int) (_b * 255); + +#if ( defined _DURANGO || defined _WIN64 || __PSVITA__ ) + lightPixels[j][i] = a << 24 | b << 16 | g << 8 | r; +#elif ( defined _XBOX || defined __ORBIS__ ) + lightPixels[j][i] = a << 24 | r << 16 | g << 8 | b; +#else + lightPixels[j][i] = r << 24 | g << 16 | b << 8 | a; +#endif + } + + mc->textures->replaceTextureDirect( lightPixels[j], 16, 16, getLightTexture(j,level) ); + } +} + +float GameRenderer::getNightVisionScale(shared_ptr player, float a) +{ + int duration = player->getEffect(MobEffect::nightVision)->getDuration(); + if (duration > (SharedConstants::TICKS_PER_SECOND * 10)) + { + return 1.0f; + } + else + { + float flash = max(0.0f, (float)duration - a); + return .7f + Mth::sin(flash * PI * .05f) * .3f; // was: .7 + sin(flash*pi*0.2) * .3 + } +} + +// 4J added, so we can have a light texture for each player to support split screen +int GameRenderer::getLightTexture(int iPad, Level *level) +{ + // Turn the current dimenions id into an index from 0 to 2 + // int idx = level->dimension->id; + // if( idx == -1 ) idx = 2; + + return lightTexture[iPad]; // 4J-JEV: Changing to Per Player lighting textures. +} + +void GameRenderer::render(float a, bool bFirst) +{ + if( _updateLightTexture && bFirst) updateLightTexture(a); + if (Display::isActive()) + { + lastActiveTime = System::currentTimeMillis(); + } + else + { + if (System::currentTimeMillis() - lastActiveTime > 500) + { + mc->pauseGame(); + } + } + +#if 0 // 4J - TODO + if (mc->mouseGrabbed) { + mc->mouseHandler.poll(); + + float ss = mc->options->sensitivity * 0.6f + 0.2f; + float sens = (ss * ss * ss) * 8; + float xo = mc->mouseHandler.xd * sens; + float yo = mc->mouseHandler.yd * sens; + + int yAxis = 1; + if (mc->options->invertYMouse) yAxis = -1; + + if (Minecraft.DEADMAU5_CAMERA_CHEATS) { + if (!mc->options->fixedCamera) { + if (Keyboard.isKeyDown(Keyboard.KEY_J)) { + xo = -12f * mc->options->sensitivity; + } else if (Keyboard.isKeyDown(Keyboard.KEY_L)) { + xo = 12f * mc->options->sensitivity; + } + if (Keyboard.isKeyDown(Keyboard.KEY_I)) { + yo = 12f * mc->options->sensitivity; + } else if (Keyboard.isKeyDown(Keyboard.KEY_K)) { + yo = -12f * mc->options->sensitivity; + } + } + } + + if (mc->options->smoothCamera) { + + xo = smoothTurnX.getNewDeltaValue(xo, .05f * sens); + yo = smoothTurnY.getNewDeltaValue(yo, .05f * sens); + + } + + mc->player.turn(xo, yo * yAxis); + } +#endif + + if (mc->noRender) return; + GameRenderer::anaglyph3d = mc->options->anaglyph3d; + + glViewport(0, 0, mc->width, mc->height); // 4J - added + ScreenSizeCalculator ssc(mc->options, mc->width, mc->height); + int screenWidth = ssc.getWidth(); + int screenHeight = ssc.getHeight(); + int xMouse = Mouse::getX() * screenWidth / mc->width; + int yMouse = screenHeight - Mouse::getY() * screenHeight / mc->height - 1; + + int maxFps = getFpsCap(mc->options->framerateLimit); + + if (mc->level != NULL) + { + if (mc->options->framerateLimit == 0) + { + renderLevel(a, 0); + } + else + { + renderLevel(a, lastNsTime + 1000000000 / maxFps); + } + + lastNsTime = System::nanoTime(); + + + if (!mc->options->hideGui || mc->screen != NULL) + { + mc->gui->render(a, mc->screen != NULL, xMouse, yMouse); + } + } + else + { + glViewport(0, 0, mc->width, mc->height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + setupGuiScreen(); + + lastNsTime = System::nanoTime(); + } + + + if (mc->screen != NULL) + { + glClear(GL_DEPTH_BUFFER_BIT); + mc->screen->render(xMouse, yMouse, a); + if (mc->screen != NULL && mc->screen->particles != NULL) mc->screen->particles->render(a); + } + +} + +void GameRenderer::renderLevel(float a) +{ + renderLevel(a, 0); +} + +#ifdef MULTITHREAD_ENABLE +// Request that an item be deleted, when it is safe to do so +void GameRenderer::AddForDelete(byte *deleteThis) +{ + EnterCriticalSection(&m_csDeleteStack); + m_deleteStackByte.push_back(deleteThis); +} + +void GameRenderer::AddForDelete(SparseLightStorage *deleteThis) +{ + EnterCriticalSection(&m_csDeleteStack); + m_deleteStackSparseLightStorage.push_back(deleteThis); +} + +void GameRenderer::AddForDelete(CompressedTileStorage *deleteThis) +{ + EnterCriticalSection(&m_csDeleteStack); + m_deleteStackCompressedTileStorage.push_back(deleteThis); +} + +void GameRenderer::AddForDelete(SparseDataStorage *deleteThis) +{ + EnterCriticalSection(&m_csDeleteStack); + m_deleteStackSparseDataStorage.push_back(deleteThis); +} + +void GameRenderer::FinishedReassigning() +{ + LeaveCriticalSection(&m_csDeleteStack); +} + +int GameRenderer::runUpdate(LPVOID lpParam) +{ + Minecraft *minecraft = Minecraft::GetInstance(); + Vec3::CreateNewThreadStorage(); + AABB::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Tesselator::CreateNewThreadStorage(1024*1024); + Compression::UseDefaultThreadStorage(); + RenderManager.InitialiseContext(); +#ifdef _LARGE_WORLDS + Chunk::CreateNewThreadStorage(); +#endif + Tile::CreateNewThreadStorage(); + + ShutdownManager::HasStarted(ShutdownManager::eRenderChunkUpdateThread,m_updateEvents); + while(ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread)) + { + //m_updateEvents->Clear(eUpdateEventIsFinished); + //m_updateEvents->WaitForSingle(eUpdateCanRun,INFINITE); + // 4J Stu - We Need to have this happen atomically to avoid deadlocks + m_updateEvents->WaitForAll(INFINITE); + + if( !ShutdownManager::ShouldRun(ShutdownManager::eRenderChunkUpdateThread) ) + { + break; + } + + m_updateEvents->Set(eUpdateCanRun); + +// PIXBeginNamedEvent(0,"Updating dirty chunks %d",(count++)&7); + + // Update chunks atomically until there aren't any very near ones left - they will be deferred for rendering + // until the call to CBuffDeferredModeEnd if we have anything near to render here + // Now limiting maximum number of updates that can be deferred as have noticed that with redstone clock circuits, it is possible to create + // things that need constant updating, so if you stand near them, the render data Never gets updated and the game just keeps going until it runs out of render memory... + int count = 0; + static const int MAX_DEFERRED_UPDATES = 10; + bool shouldContinue = false; + do + { + shouldContinue = minecraft->levelRenderer->updateDirtyChunks(); + count++; + } while ( shouldContinue && count < MAX_DEFERRED_UPDATES ); + +// while( minecraft->levelRenderer->updateDirtyChunks() ) +// ; + RenderManager.CBuffDeferredModeEnd(); + + // If any renderable tile entities were flagged in this last block of chunk(s) that were udpated, then change their + // flags to say that this deferred chunk is over and they are actually safe to be removed now + minecraft->levelRenderer->fullyFlagRenderableTileEntitiesToBeRemoved(); + + // We've got stacks for things that can only safely be deleted whilst this thread isn't updating things - delete those things now + EnterCriticalSection(&m_csDeleteStack); + for(unsigned int i = 0; i < m_deleteStackByte.size(); i++ ) + { + delete m_deleteStackByte[i]; + } + m_deleteStackByte.clear(); + for(unsigned int i = 0; i < m_deleteStackSparseLightStorage.size(); i++ ) + { + delete m_deleteStackSparseLightStorage[i]; + } + m_deleteStackSparseLightStorage.clear(); + for(unsigned int i = 0; i < m_deleteStackCompressedTileStorage.size(); i++ ) + { + delete m_deleteStackCompressedTileStorage[i]; + } + m_deleteStackCompressedTileStorage.clear(); + for(unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); i++ ) + { + delete m_deleteStackSparseDataStorage[i]; + } + m_deleteStackSparseDataStorage.clear(); + LeaveCriticalSection(&m_csDeleteStack); + +// PIXEndNamedEvent(); + + AABB::resetPool(); + Vec3::resetPool(); + IntCache::Reset(); + m_updateEvents->Set(eUpdateEventIsFinished); + } + + ShutdownManager::HasFinished(ShutdownManager::eRenderChunkUpdateThread); + return 0; +} +#endif + +void GameRenderer::EnableUpdateThread() +{ +// #ifdef __PS3__ // MGH - disable the update on PS3 for now +// return; +// #endif +#ifdef MULTITHREAD_ENABLE + if( updateRunning) return; + app.DebugPrintf("------------------EnableUpdateThread--------------------\n"); + updateRunning = true; + m_updateEvents->Set(eUpdateCanRun); + m_updateEvents->Set(eUpdateEventIsFinished); +#endif +} + +void GameRenderer::DisableUpdateThread() +{ +// #ifdef __PS3__ // MGH - disable the update on PS3 for now +// return; +// #endif +#ifdef MULTITHREAD_ENABLE + if( !updateRunning) return; + app.DebugPrintf("------------------DisableUpdateThread--------------------\n"); + updateRunning = false; + m_updateEvents->Clear(eUpdateCanRun); + m_updateEvents->WaitForSingle(eUpdateEventIsFinished,INFINITE); +#endif +} + +void GameRenderer::renderLevel(float a, __int64 until) +{ +// if (updateLightTexture) updateLightTexture(); // 4J - TODO - Java 1.0.1 has this line enabled, should check why - don't want to put it in now in case it breaks split-screen + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + // Is this the primary player? Only do the updating of chunks if it is. This controls the creation of render data for each chunk - all of this we are only + // going to do for the primary player, and the other players can just view whatever they have loaded in - we're sharing render data between players. + bool updateChunks = ( mc->player == mc->localplayers[ProfileManager.GetPrimaryPad()] ); + +// if (mc->cameraTargetPlayer == NULL) // 4J - removed condition as we want to update this is mc->player changes for different local players + { + mc->cameraTargetPlayer = mc->player; + } + pick(a); + + shared_ptr cameraEntity = mc->cameraTargetPlayer; + LevelRenderer *levelRenderer = mc->levelRenderer; + ParticleEngine *particleEngine = mc->particleEngine; + double xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * a; + double yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * a; + double zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * a; + + for (int i = 0; i < 2; i++) + { + if (mc->options->anaglyph3d) + { + GameRenderer::anaglyphPass = i; + if (GameRenderer::anaglyphPass == 0) glColorMask(false, true, true, false); + else glColorMask(true, false, false, false); + } + + + glViewport(0, 0, mc->width, mc->height); + setupClearColor(a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_CULL_FACE); + + setupCamera(a, i); + Camera::prepare(mc->player, mc->player->ThirdPersonView() == 2); + + Frustum::getFrustum(); + if (mc->options->viewDistance < 2) + { + setupFog(-1, a); + levelRenderer->renderSky(a); + if(mc->skins->getSelected()->getId() == 1026 ) levelRenderer->renderHaloRing(a); + } + glEnable(GL_FOG); + setupFog(1, a); + + if (mc->options->ambientOcclusion) + { + GL11::glShadeModel(GL11::GL_SMOOTH); + } + + PIXBeginNamedEvent(0,"Culling"); + MemSect(31); +// Culler *frustum = new FrustumCuller(); + FrustumCuller frustObj; + Culler *frustum = &frustObj; + MemSect(0); + frustum->prepare(xOff, yOff, zOff); + + mc->levelRenderer->cull(frustum, a); + PIXEndNamedEvent(); + +#ifndef MULTITHREAD_ENABLE + if ( (i == 0) && updateChunks ) // 4J - added updateChunks condition + { + int PIXPass = 0; + PIXBeginNamedEvent(0,"Updating dirty chunks"); + do + { + PIXBeginNamedEvent(0,"Updating dirty chunks pass %d",PIXPass++); + bool retval = mc->levelRenderer->updateDirtyChunks(cameraEntity, false); + PIXEndNamedEvent(); + if( retval ) break; + + + if (until == 0) break; + + __int64 diff = until - System::nanoTime(); + if (diff < 0) break; + if (diff > 1000000000) break; + } while (true); + PIXEndNamedEvent(); + } +#endif + setupFog(0, a); + glEnable(GL_FOG); + MemSect(31); + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + Lighting::turnOff(); + PIXBeginNamedEvent(0,"Level render"); + levelRenderer->render(cameraEntity, 0, a, updateChunks); + PIXEndNamedEvent(); + + GL11::glShadeModel(GL11::GL_FLAT); + + if (cameraFlip == 0 ) + { + Lighting::turnOn(); + PIXBeginNamedEvent(0,"Entity render"); + // 4J - for entities, don't include the "a" factor that interpolates from the old to new position, as the AABBs for the entities are already fully at the new position + // This fixes flickering minecarts, and pigs that you are riding on + frustum->prepare(cameraEntity->x,cameraEntity->y,cameraEntity->z); + // 4J Stu - When rendering entities, in the end if the dragon is hurt or we have a lot of entities we can end up wrapping + // our index into the temp Vec3 cache and overwrite the one that was storing the camera position + // Fix for #77745 - TU9: Content: Gameplay: Items and mobs not belonging to end world are disappearing when Enderdragon is damaged. + Vec3 *cameraPosTemp = cameraEntity->getPos(a); + cameraPos->x = cameraPosTemp->x; + cameraPos->y = cameraPosTemp->y; + cameraPos->z = cameraPosTemp->z; + levelRenderer->renderEntities(cameraPos, frustum, a); +#ifdef __PSVITA__ + // AP - make sure we're using the Alpha cut out effect for particles + glEnable(GL_ALPHA_TEST); +#endif + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Particle render"); + turnOnLightLayer(a); // 4J - brought forward from 1.8.2 + particleEngine->renderLit(cameraEntity, a); + Lighting::turnOff(); + setupFog(0, a); + particleEngine->render(cameraEntity, a); + PIXEndNamedEvent(); + turnOffLightLayer(a); // 4J - brought forward from 1.8.2 + + shared_ptr player = dynamic_pointer_cast(cameraEntity); + if (mc->hitResult != NULL && cameraEntity->isUnderLiquid(Material::water) && player!=NULL) //&& !mc->options.hideGui) + { + //shared_ptr player = dynamic_pointer_cast(cameraEntity); + glDisable(GL_ALPHA_TEST); + levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a); + levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a); + glEnable(GL_ALPHA_TEST); + } + } + + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(true); + setupFog(0, a); + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + MemSect(31); + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + // 4J - have changed this fancy rendering option to work with our command buffers. The original used to use frame buffer flags to disable + // writing to colour when doing the z-only pass, but that value gets obliterated by our command buffers. Using alpha blend function instead + // to achieve the same effect. + if (true) // (mc->options->fancyGraphics) + { + if (mc->options->ambientOcclusion) + { + GL11::glShadeModel(GL11::GL_SMOOTH); + } + + glBlendFunc(GL_ZERO, GL_ONE); + PIXBeginNamedEvent(0,"Fancy second pass - writing z"); + int visibleWaterChunks = levelRenderer->render(cameraEntity, 1, a, updateChunks); + PIXEndNamedEvent(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (visibleWaterChunks > 0) + { + PIXBeginNamedEvent(0,"Fancy second pass - actual rendering"); + levelRenderer->render(cameraEntity, 1, a, updateChunks); // 4J - chanaged, used to be renderSameAsLast but we don't support that anymore + PIXEndNamedEvent(); + } + + GL11::glShadeModel(GL11::GL_FLAT); + } + else + { + PIXBeginNamedEvent(0,"Second pass level render"); + levelRenderer->render(cameraEntity, 1, a, updateChunks); + PIXEndNamedEvent(); + } + + + glDepthMask(true); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); + + if (zoom == 1 && (dynamic_pointer_cast(cameraEntity)!=NULL)) //&& !mc->options.hideGui) + { + if (mc->hitResult != NULL && !cameraEntity->isUnderLiquid(Material::water)) + { + shared_ptr player = dynamic_pointer_cast(cameraEntity); + glDisable(GL_ALPHA_TEST); + levelRenderer->renderHit(player, mc->hitResult, 0, player->inventory->getSelected(), a); + levelRenderer->renderHitOutline(player, mc->hitResult, 0, player->inventory->getSelected(), a); + glEnable(GL_ALPHA_TEST); + } + } + + /* 4J - moved rain rendering to after clouds so that it alpha blends onto them properly + PIXBeginNamedEvent(0,"Rendering snow and rain"); + renderSnowAndRain(a); + PIXEndNamedEvent(); + glDisable(GL_FOG); + */ + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + levelRenderer->renderDestroyAnimation(Tesselator::getInstance(), dynamic_pointer_cast(cameraEntity), a); + glDisable(GL_BLEND); + + if (mc->options->isCloudsOn()) + { + glPushMatrix(); + setupFog(0, a); + glEnable(GL_FOG); + PIXBeginNamedEvent(0,"Rendering clouds"); + levelRenderer->renderClouds(a); + PIXEndNamedEvent(); + glDisable(GL_FOG); + setupFog(1, a); + glPopMatrix(); + } + + // 4J - rain rendering moved here so that it renders after clouds & can blend properly onto them + setupFog(0, a); + glEnable(GL_FOG); + PIXBeginNamedEvent(0,"Rendering snow and rain"); + renderSnowAndRain(a); + PIXEndNamedEvent(); + glDisable(GL_FOG); + + + if (zoom == 1) + { + glClear(GL_DEPTH_BUFFER_BIT); + renderItemInHand(a, i); + } + + + if (!mc->options->anaglyph3d) + { + return; + } + } + glColorMask(true, true, true, false); +} + +void GameRenderer::tickRain() +{ + float rainLevel = mc->level->getRainLevel(1); + + if (!mc->options->fancyGraphics) rainLevel /= 2; + if (rainLevel == 0) return; + + rainLevel /= ( mc->levelRenderer->activePlayers() + 1 ); + + random->setSeed(_tick * 312987231l); + shared_ptr player = mc->cameraTargetPlayer; + Level *level = mc->level; + + int x0 = Mth::floor(player->x); + int y0 = Mth::floor(player->y); + int z0 = Mth::floor(player->z); + + int r = 10; + + double rainPosX = 0; + double rainPosY = 0; + double rainPosZ = 0; + int rainPosSamples = 0; + + int rainCount = (int) (100 * rainLevel * rainLevel); + if (mc->options->particles == 1) + { + rainCount >>= 1; + } else if (mc->options->particles == 2) + { + rainCount = 0; + } + for (int i = 0; i < rainCount; i++) + { + int x = x0 + random->nextInt(r) - random->nextInt(r); + int z = z0 + random->nextInt(r) - random->nextInt(r); + int y = level->getTopRainBlock(x, z); + int t = level->getTile(x, y - 1, z); + Biome *biome = level->getBiome(x,z); + if (y <= y0 + r && y >= y0 - r && biome->hasRain() && biome->getTemperature() >= 0.2f) + { + float xa = random->nextFloat(); + float za = random->nextFloat(); + if (t > 0) + { + if (Tile::tiles[t]->material == Material::lava) + { + mc->particleEngine->add( shared_ptr( new SmokeParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za, 0, 0, 0) ) ); + } + else + { + if (random->nextInt(++rainPosSamples) == 0) + { + rainPosX = x + xa; + rainPosY = y + 0.1f - Tile::tiles[t]->getShapeY0(); + rainPosZ = z + za; + } + mc->particleEngine->add( shared_ptr( new WaterDropParticle(level, x + xa, y + 0.1f - Tile::tiles[t]->getShapeY0(), z + za) ) ); + } + } + } + } + + + if (rainPosSamples > 0 && random->nextInt(3) < rainSoundTime++) + { + rainSoundTime = 0; + MemSect(24); + if (rainPosY > player->y + 1 && level->getTopRainBlock(Mth::floor(player->x), Mth::floor(player->z)) > Mth::floor(player->y)) + { + mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.1f, 0.5f); + } + else + { + mc->level->playLocalSound(rainPosX, rainPosY, rainPosZ, eSoundType_AMBIENT_WEATHER_RAIN, 0.2f, 1.0f); + } + MemSect(0); + } + +} + +// 4J - this whole function updated from 1.8.2 +void GameRenderer::renderSnowAndRain(float a) +{ + float rainLevel = mc->level->getRainLevel(a); + if (rainLevel <= 0) return; + + // 4J - rain is relatively low poly, but high fill-rate - better to clip it + RenderManager.StateSetEnableViewportClipPlanes(true); + + this->turnOnLightLayer(a); + + if (rainXa == NULL) + { + rainXa = new float[32 * 32]; + rainZa = new float[32 * 32]; + + for (int z = 0; z < 32; z++) + { + for (int x = 0; x < 32; x++) + { + float xa = x - 16; + float za = z - 16; + float d = Mth::sqrt(xa * xa + za * za); + rainXa[z << 5 | x] = -za / d; + rainZa[z << 5 | x] = xa / d; + } + } + } + + shared_ptr player = mc->cameraTargetPlayer; + Level *level = mc->level; + + int x0 = Mth::floor(player->x); + int y0 = Mth::floor(player->y); + int z0 = Mth::floor(player->z); + + Tesselator *t = Tesselator::getInstance(); + glDisable(GL_CULL_FACE); + glNormal3f(0, 1, 0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GREATER, 0.01f); + + MemSect(31); + mc->textures->bindTexture(TN_ENVIRONMENT_SNOW); // 4J was L"/environment/snow.png" + MemSect(0); + + double xo = player->xOld + (player->x - player->xOld) * a; + double yo = player->yOld + (player->y - player->yOld) * a; + double zo = player->zOld + (player->z - player->zOld) * a; + + int yMin = Mth::floor(yo); + + + int r = 5; + // 4J - was if(mc.options.fancyGraphics) r = 10; + switch( mc->levelRenderer->activePlayers() ) + { + case 1: + default: + r = 9; + break; + case 2: + r = 7; + break; + case 3: + r = 5; + break; + case 4: + r = 5; + break; + } + + // 4J - some changes made here to access biome through new interface that caches results in levelchunk flags, as an optimisation + + int mode = -1; + float time = _tick + a; + + glColor4f(1, 1, 1, 1); + + for (int x = x0 - r; x <= x0 + r; x++) + for (int z = z0 - r; z <= z0 + r; z++) + { + int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); + float xa = rainXa[rainSlot] * 0.5f; + float za = rainZa[rainSlot] * 0.5f; + + // 4J - changes here brought forward from 1.8.2 + Biome *b = level->getBiome(x, z); + if (!b->hasRain() && !b->hasSnow()) continue; + + int floor = level->getTopRainBlock(x, z); + + int yy0 = y0 - r; + int yy1 = y0 + r; + + if (yy0 < floor) yy0 = floor; + if (yy1 < floor) yy1 = floor; + float s = 1; + + int yl = floor; + if (yl < yMin) yl = yMin; + + if (yy0 != yy1) + { + random->setSeed((x * x * 3121 + x * 45238971) ^ (z * z * 418711 + z * 13761)); + + // 4J - changes here brought forward from 1.8.2 + float temp = b->getTemperature(); + if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) + { + if (mode != 0) + { + if (mode >= 0) t->end(); + mode = 0; + mc->textures->bindTexture(TN_ENVIRONMENT_RAIN); + t->begin(); + } + + float ra = (((_tick + x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761) & 31) + a) / 32.0f * (3 + random->nextFloat()); + + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float) Mth::sqrt(xd * xd + zd * zd) / r; + + float br = 1; + t->offset(-xo * 1, -yo * 1, -zo * 1); +#ifdef __PSVITA__ + // AP - this will set up the 4 vertices in half the time + float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, + br, br, br, Alpha, br, br, br, 0, tex2); +#else + t->tex2(level->getLightColor(x, yl, z, 0)); + t->color(br, br, br, ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s); + t->color(br, br, br, 0.0f); // 4J - added to soften the top visible edge of the rain + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s); +#endif + t->offset(0, 0, 0); + t->end(); + } + else + { + if (mode != 1) + { + if (mode >= 0) t->end(); + mode = 1; + mc->textures->bindTexture(TN_ENVIRONMENT_SNOW); + t->begin(); + } + float ra = (((_tick) & 511) + a) / 512.0f; + float uo = random->nextFloat() + time * 0.01f * (float) random->nextGaussian(); + float vo = random->nextFloat() + time * (float) random->nextGaussian() * 0.001f; + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float) sqrt(xd * xd + zd * zd) / r; + float br = 1; + t->offset(-xo * 1, -yo * 1, -zo * 1); +#ifdef __PSVITA__ + // AP - this will set up the 4 vertices in half the time + float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, yy0 * s / 4.0f + ra * s + vo, + x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, + x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo, + br, br, br, Alpha, br, br, br, Alpha, tex2); +#else + t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4); + t->color(br, br, br, ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo); +#endif + t->offset(0, 0, 0); + } + } + } + + if( mode >= 0 ) t->end(); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); + glAlphaFunc(GL_GREATER, 0.1f); + this->turnOffLightLayer(a); + + RenderManager.StateSetEnableViewportClipPlanes(false); +} + +// 4J - added forceScale parameter +void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) +{ + ScreenSizeCalculator ssc(mc->options, mc->width, mc->height, forceScale); + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, (float)ssc.rawWidth, (float)ssc.rawHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); +} + +void GameRenderer::setupClearColor(float a) +{ + Level *level = mc->level; + shared_ptr player = mc->cameraTargetPlayer; + + float whiteness = 1.0f / (4 - mc->options->viewDistance); + whiteness = 1 - (float) pow((double)whiteness, 0.25); + + Vec3 *skyColor = level->getSkyColor(mc->cameraTargetPlayer, a); + float sr = (float) skyColor->x; + float sg = (float) skyColor->y; + float sb = (float) skyColor->z; + + Vec3 *fogColor = level->getFogColor(a); + fr = (float) fogColor->x; + fg = (float) fogColor->y; + fb = (float) fogColor->z; + + if (mc->options->viewDistance < 2) + { + Vec3 *sunAngle = Mth::sin(level->getSunAngle(a)) > 0 ? Vec3::newTemp(-1, 0, 0) : Vec3::newTemp(1, 0, 0); + float d = (float) player->getViewVector(a)->dot(sunAngle); + if (d < 0) d = 0; + if (d > 0) + { + float *c = level->dimension->getSunriseColor(level->getTimeOfDay(a), a); + if (c != NULL) + { + d *= c[3]; + fr = fr * (1 - d) + c[0] * d; + fg = fg * (1 - d) + c[1] * d; + fb = fb * (1 - d) + c[2] * d; + } + } + } + + fr += (sr - fr) * whiteness; + fg += (sg - fg) * whiteness; + fb += (sb - fb) * whiteness; + + float rainLevel = level->getRainLevel(a); + if (rainLevel > 0) + { + float ba = 1 - rainLevel * 0.5f; + float bb = 1 - rainLevel * 0.4f; + fr *= ba; + fg *= ba; + fb *= bb; + } + float thunderLevel = level->getThunderLevel(a); + if (thunderLevel > 0) + { + float ba = 1 - thunderLevel * 0.5f; + fr *= ba; + fg *= ba; + fb *= ba; + } + + int t = Camera::getBlockAt(mc->level, player, a); + if (isInClouds) + { + Vec3 *cc = level->getCloudColor(a); + fr = (float) cc->x; + fg = (float) cc->y; + fb = (float) cc->z; + } + else if (t != 0 && Tile::tiles[t]->material == Material::water) + { + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Clear_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + fr = (float)redComponent/256;//0.02f; + fg = (float)greenComponent/256;//0.02f; + fb = (float)blueComponent/256;//0.2f; + } + else if (t != 0 && Tile::tiles[t]->material == Material::lava) + { + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Clear_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + fr = (float)redComponent/256;//0.6f; + fg = (float)greenComponent/256;//0.1f; + fb = (float)blueComponent/256;//0.00f; + } + + float brr = fogBrO + (fogBr - fogBrO) * a; + fr *= brr; + fg *= brr; + fb *= brr; + + double yy = (player->yOld + (player->y - player->yOld) * a) * level->dimension->getClearColorScale(); // 4J - getClearColorScale brought forward from 1.2.3 + + if (player->hasEffect(MobEffect::blindness)) + { + int duration = player->getEffect(MobEffect::blindness)->getDuration(); + if (duration < 20) + { + yy = yy * (1.0f - (float) duration / 20.0f); + } + else + { + yy = 0; + } + } + + if (yy < 1) + { + if (yy < 0) yy = 0; + yy = yy * yy; + fr *= yy; + fg *= yy; + fb *= yy; + } + + if (player->hasEffect(MobEffect::nightVision)) + { + float scale = getNightVisionScale(mc->player, a); + { + float dist = FLT_MAX; // MGH - changed this to avoid divide by zero + if ( (fr > 0) && (dist > (1.0f / fr)) ) + { + dist = (1.0f / fr); + } + if ( (fg > 0) && (dist > (1.0f / fg)) ) + { + dist = (1.0f / fg); + } + if ( (fb > 0) && (dist > (1.0f / fb)) ) + { + dist = (1.0f / fb); + } + fr = fr * (1.0f - scale) + (fr * dist) * scale; + fg = fg * (1.0f - scale) + (fg * dist) * scale; + fb = fb * (1.0f - scale) + (fb * dist) * scale; + } + } + + if (mc->options->anaglyph3d) + { + float frr = (fr * 30 + fg * 59 + fb * 11) / 100; + float fgg = (fr * 30 + fg * 70) / (100); + float fbb = (fr * 30 + fb * 70) / (100); + + fr = frr; + fg = fgg; + fb = fbb; + } + + glClearColor(fr, fg, fb, 0.0f); + +} + +void GameRenderer::setupFog(int i, float alpha) +{ + shared_ptr player = mc->cameraTargetPlayer; + + // 4J - check for creative mode brought forward from 1.2.3 + bool creative = false; + if (dynamic_pointer_cast(player) ) + { + creative = (dynamic_pointer_cast(player))->abilities.instabuild; + } + + if (i == 999) + { + __debugbreak(); + // 4J TODO + /* + glFog(GL_FOG_COLOR, getBuffer(0, 0, 0, 1)); + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_START, 0); + glFogf(GL_FOG_END, 8); + + if (GLContext.getCapabilities().GL_NV_fog_distance) { + glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); + } + + glFogf(GL_FOG_START, 0); + */ + return; + } + + glFog(GL_FOG_COLOR, getBuffer(fr, fg, fb, 1)); + glNormal3f(0, -1, 0); + glColor4f(1, 1, 1, 1); + + int t = Camera::getBlockAt(mc->level, player, alpha); + + if (player->hasEffect(MobEffect::blindness)) + { + float distance = 5.0f; + int duration = player->getEffect(MobEffect::blindness)->getDuration(); + if (duration < 20) + { + distance = 5.0f + (renderDistance - 5.0f) * (1.0f - (float) duration / 20.0f); + } + + glFogi(GL_FOG_MODE, GL_LINEAR); + if (i < 0) + { + glFogf(GL_FOG_START, 0); + glFogf(GL_FOG_END, distance * 0.8f); + } + else + { + glFogf(GL_FOG_START, distance * 0.25f); + glFogf(GL_FOG_END, distance); + } + // 4J - TODO investigate implementing this +// if (GLContext.getCapabilities().GL_NV_fog_distance) +// { +// glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); +// } + } + else if (isInClouds) + { + glFogi(GL_FOG_MODE, GL_EXP); + glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_In_Cloud_Fog_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + float rr = (float)redComponent/256;//1.0f; + float gg = (float)greenComponent/256;//1.0f; + float bb = (float)blueComponent/256;//1.0f; + + if (mc->options->anaglyph3d) + { + float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; + float ggg = (rr * 30 + gg * 70) / (100); + float bbb = (rr * 30 + bb * 70) / (100); + + rr = rrr; + gg = ggg; + bb = bbb; + } + } + else if (t > 0 && Tile::tiles[t]->material == Material::water) + { + glFogi(GL_FOG_MODE, GL_EXP); + if (player->hasEffect(MobEffect::waterBreathing)) + { + glFogf(GL_FOG_DENSITY, 0.05f); // was 0.06 + } + else + { + glFogf(GL_FOG_DENSITY, 0.1f); // was 0.06 + } + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Water_Fog_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + float rr = (float)redComponent/256;//0.4f; + float gg = (float)greenComponent/256;//0.4f; + float bb = (float)blueComponent/256;//0.9f; + + if (mc->options->anaglyph3d) + { + float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; + float ggg = (rr * 30 + gg * 70) / (100); + float bbb = (rr * 30 + bb * 70) / (100); + + rr = rrr; + gg = ggg; + bb = bbb; + } + } + else if (t > 0 && Tile::tiles[t]->material == Material::lava) + { + glFogi(GL_FOG_MODE, GL_EXP); + glFogf(GL_FOG_DENSITY, 2.0f); // was 0.06 + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Under_Lava_Fog_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + float rr = (float)redComponent/256;//0.4f; + float gg = (float)greenComponent/256;//0.3f; + float bb = (float)blueComponent/256;//0.3f; + + if (mc->options->anaglyph3d) + { + float rrr = (rr * 30 + gg * 59 + bb * 11) / 100; + float ggg = (rr * 30 + gg * 70) / (100); + float bbb = (rr * 30 + bb * 70) / (100); + + rr = rrr; + gg = ggg; + bb = bbb; + } + } + else + { + float distance = renderDistance; + if (!mc->level->dimension->hasCeiling) + { + // 4J - test for doing bedrockfog brought forward from 1.2.3 + if (mc->level->dimension->hasBedrockFog() && !creative) + { + double yy = ((player->getLightColor(alpha) & 0xf00000) >> 20) / 16.0 + (player->yOld + (player->y - player->yOld) * alpha + 4) / 32; + if (yy < 1) + { + if (yy < 0) yy = 0; + yy = yy * yy; + float dist = 100 * (float) yy; + if (dist < 5) dist = 5; + if (distance > dist) distance = dist; + } + } + } + + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_START, distance * 0.25f); + glFogf(GL_FOG_END, distance); + if (i < 0) + { + glFogf(GL_FOG_START, 0); + glFogf(GL_FOG_END, distance * 0.8f); + } + else + { + glFogf(GL_FOG_START, distance * 0.25f); + glFogf(GL_FOG_END, distance); + } + /* 4J - removed - TODO investigate + if (GLContext.getCapabilities().GL_NV_fog_distance) + { + glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); + } + */ + + if (mc->level->dimension->isFoggyAt((int) player->x, (int) player->z)) + { + glFogf(GL_FOG_START, distance * 0.05f); + glFogf(GL_FOG_END, min(distance, 16 * 16 * .75f) * .5f); + } + } + + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT, GL_AMBIENT); + +} + +FloatBuffer *GameRenderer::getBuffer(float a, float b, float c, float d) +{ + lb->clear(); + lb->put(a)->put(b)->put(c)->put(d); + lb->flip(); + return lb; +} + +int GameRenderer::getFpsCap(int option) +{ + int maxFps = 200; + if (option == 1) maxFps = 120; + if (option == 2) maxFps = 35; + return maxFps; +} + +void GameRenderer::updateAllChunks() +{ +// mc->levelRenderer->updateDirtyChunks(mc->cameraTargetPlayer, true); +} diff --git a/Minecraft.Client/GameRenderer.h b/Minecraft.Client/GameRenderer.h new file mode 100644 index 0000000..3729726 --- /dev/null +++ b/Minecraft.Client/GameRenderer.h @@ -0,0 +1,171 @@ +#pragma once +class Minecraft; +class Entity; +class Random; +class FloatBuffer; +class ItemInHandRenderer; +class DataLayer; +class SparseLightStorage; +class CompressedTileStorage; +class SparseDataStorage; + +#include "..\Minecraft.World\SmoothFloat.h" +#include "..\Minecraft.World\C4JThread.h" + +class GameRenderer +{ +public: + static bool anaglyph3d; + static int anaglyphPass; + +private: + Minecraft *mc; + float renderDistance; +public: + ItemInHandRenderer *itemInHandRenderer; +private: + int _tick; + shared_ptr hovered; + + // smooth camera movement + SmoothFloat smoothTurnX; + SmoothFloat smoothTurnY; + + // third-person distance etc + SmoothFloat smoothDistance; + SmoothFloat smoothRotation; + SmoothFloat smoothTilt; + SmoothFloat smoothRoll; + float thirdDistance; + float thirdDistanceO; + float thirdRotation; + float thirdRotationO; + float thirdTilt; + float thirdTiltO; + float accumulatedSmoothXO, accumulatedSmoothYO; + float tickSmoothXO, tickSmoothYO, lastTickA; + Vec3 *cameraPos; // 4J added + + // fov modification + float fovOffset; + float fovOffsetO; + + // roll modification + float cameraRoll; + float cameraRollO; + + // 4J - changes brought forward from 1.8.2 + static const int NUM_LIGHT_TEXTURES = 4;// * 3; + int lightTexture[NUM_LIGHT_TEXTURES]; // 4J - changed so that we have one lightTexture per level, to support split screen + int getLightTexture(int iPad, Level *level); // 4J added + intArray lightPixels[NUM_LIGHT_TEXTURES]; + + float fov[4]; + float oFov[4]; + float tFov[4]; + + bool isInClouds; + + float m_fov; +public: + GameRenderer(Minecraft *mc); + ~GameRenderer(); + void SetFovVal(float fov); + float GetFovVal(); + +public: + void tick(bool bFirst); + void pick(float a); +private: + void tickFov(); + float getFov(float a, bool applyEffects); + void bobHurt(float a); + void bobView(float a); + void moveCameraToPlayer(float a); + double zoom; + double zoom_x; + double zoom_y; +public: + void zoomRegion(double zoom, double xa, double ya); + void unZoomRegion(); +private: + void getFovAndAspect(float& fov, float& aspect, float a, bool applyEffects); // 4J added +public: + void setupCamera(float a, int eye); +private: + void renderItemInHand(float a, int eye); + __int64 lastActiveTime; + __int64 lastNsTime; + // 4J - changes brought forward from 1.8.2 + bool _updateLightTexture; +public: + float blr; + float blrt; + float blg; + float blgt; + void turnOffLightLayer(double alpha); + void turnOnLightLayer(double alpha); +private: + void tickLightTexture(); + void updateLightTexture(float a); + float getNightVisionScale(shared_ptr player, float a); +public: + void render(float a, bool bFirst); // 4J added bFirst + void renderLevel(float a); + void renderLevel(float a, __int64 until); +private: + Random *random; + int rainSoundTime; + void tickRain(); +private: + // 4J - brought forward from 1.8.2 + float *rainXa; + float *rainZa; +protected: + void renderSnowAndRain(float a); + volatile int xMod; + volatile int yMod; +public: + void setupGuiScreen(int forceScale=-1); // 4J - added forceScale parameter + + FloatBuffer *lb; + float fr; + float fg; + float fb; +private: + void setupClearColor(float a); + float fogBrO, fogBr; + int cameraFlip; + + void setupFog(int i, float alpha); + FloatBuffer *getBuffer(float a, float b, float c, float d); + static int getFpsCap(int option); +public: + void updateAllChunks(); + +#ifdef MULTITHREAD_ENABLE + static C4JThread* m_updateThread; + static int runUpdate(LPVOID lpParam); + static C4JThread::EventArray* m_updateEvents; + enum EUpdateEvents + { + eUpdateCanRun, + eUpdateEventIsFinished, + eUpdateEventCount, + }; + static bool nearThingsToDo; + static bool updateRunning; +#endif + static vector m_deleteStackByte; + static vector m_deleteStackSparseLightStorage; + static vector m_deleteStackCompressedTileStorage; + static vector m_deleteStackSparseDataStorage; + static CRITICAL_SECTION m_csDeleteStack; + static void AddForDelete(byte *deleteThis); + static void AddForDelete(SparseLightStorage *deleteThis); + static void AddForDelete(CompressedTileStorage *deleteThis); + static void AddForDelete(SparseDataStorage *deleteThis); + static void FinishedReassigning(); + void EnableUpdateThread(); + void DisableUpdateThread(); +}; diff --git a/Minecraft.Client/GhastModel.cpp b/Minecraft.Client/GhastModel.cpp new file mode 100644 index 0000000..460c28f --- /dev/null +++ b/Minecraft.Client/GhastModel.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\Mth.h" +#include "GhastModel.h" +#include "ModelPart.h" + +GhastModel::GhastModel() : Model() +{ + int yoffs = -16; + body = new ModelPart(this, 0, 0); + body->addBox(-8, -8, -8, 16, 16, 16); + body->y += (8 + 16) + yoffs; + + Random *random = new Random(1660); + for (int i = 0; i < TENTACLESLENGTH; i++) // 4J - 9 was tentacles.length + { + tentacles[i] = new ModelPart(this, 0, 0); + + float xo = (((i % 3 - (i / 3 % 2) * 0.5f + 0.25f) / 2.0f * 2 - 1) * 5); + float yo = (((i / 3) / 2.0f * 2 - 1) * 5); + int len = random->nextInt(7) + 8; + tentacles[i]->addBox(-1, 0, -1, 2, len, 2); + + tentacles[i]->x = xo; + tentacles[i]->z = yo; + tentacles[i]->y = (float)(31 + yoffs); + } + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + body->compile(1.0f/16.0f); + for( int i = 0; i < TENTACLESLENGTH; i++ ) + { + tentacles[i]->compile(1.0f/16.0f); + } +} + +void GhastModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + for (int i = 0; i < TENTACLESLENGTH; i++) // 4J - 9 was tentacles.length + { + tentacles[i]->xRot = 0.2f * Mth::sin(bob * 0.3f + i) + 0.4f; + } +} + +void GhastModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + glPushMatrix(); + glTranslatef(0, .6f, 0); + + body->render(scale, usecompiled); + for (int i = 0; i < TENTACLESLENGTH; i++) // 4J - 9 was tentacles.length + { + tentacles[i]->render(scale, usecompiled); + } + + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/GhastModel.h b/Minecraft.Client/GhastModel.h new file mode 100644 index 0000000..22e9023 --- /dev/null +++ b/Minecraft.Client/GhastModel.h @@ -0,0 +1,14 @@ +#pragma once +#include "Model.h" + +class GhastModel : public Model +{ +public: + static const int TENTACLESLENGTH=9; + ModelPart *body; + ModelPart *tentacles[TENTACLESLENGTH]; + + GhastModel(); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +}; \ No newline at end of file diff --git a/Minecraft.Client/GhastRenderer.cpp b/Minecraft.Client/GhastRenderer.cpp new file mode 100644 index 0000000..1d8a283 --- /dev/null +++ b/Minecraft.Client/GhastRenderer.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "GhastRenderer.h" +#include "GhastModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" + +GhastRenderer::GhastRenderer() : MobRenderer(new GhastModel(), 0.5f) +{ +} + +void GhastRenderer::scale(shared_ptr mob, float a) +{ + shared_ptr ghast = dynamic_pointer_cast(mob); + + float ss = (ghast->oCharge+(ghast->charge-ghast->oCharge)*a)/20.0f; + if (ss<0) ss = 0; + ss = 1/(ss*ss*ss*ss*ss*2+1); + float s = (8+ss)/2; + float hs = (8+1/ss)/2; + glScalef(hs, s, hs); + glColor4f(1, 1, 1, 1); +} \ No newline at end of file diff --git a/Minecraft.Client/GhastRenderer.h b/Minecraft.Client/GhastRenderer.h new file mode 100644 index 0000000..6778b9a --- /dev/null +++ b/Minecraft.Client/GhastRenderer.h @@ -0,0 +1,11 @@ +#pragma once +#include "MobRenderer.h" + +class GhastRenderer : public MobRenderer +{ +public: + GhastRenderer(); + +protected: + virtual void scale(shared_ptr mob, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/GiantMobRenderer.cpp b/Minecraft.Client/GiantMobRenderer.cpp new file mode 100644 index 0000000..6dabf7c --- /dev/null +++ b/Minecraft.Client/GiantMobRenderer.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "GiantMobRenderer.h" + +GiantMobRenderer::GiantMobRenderer(Model *model, float shadow, float _scale) : MobRenderer(model, shadow *_scale) +{ + this->_scale = _scale; +} + +void GiantMobRenderer::scale(shared_ptr mob, float a) +{ + glScalef(_scale, _scale, _scale); +} \ No newline at end of file diff --git a/Minecraft.Client/GiantMobRenderer.h b/Minecraft.Client/GiantMobRenderer.h new file mode 100644 index 0000000..5b1cce1 --- /dev/null +++ b/Minecraft.Client/GiantMobRenderer.h @@ -0,0 +1,14 @@ +#pragma once +#include "MobRenderer.h" + +class GiantMobRenderer : public MobRenderer +{ +private: + float _scale; + +public: + GiantMobRenderer(Model *model, float shadow, float scale); + +protected: + virtual void scale(shared_ptr mob, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp new file mode 100644 index 0000000..e876579 --- /dev/null +++ b/Minecraft.Client/Gui.cpp @@ -0,0 +1,1530 @@ +#include "stdafx.h" +#include "Gui.h" +#include "ItemRenderer.h" +#include "GameRenderer.h" +#include "Options.h" +#include "MultiplayerLocalPlayer.h" +#include "Textures.h" +#include "GameMode.h" +#include "Lighting.h" +#include "ChatScreen.h" +#include "MultiPlayerLevel.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\Minecraft.World\net.minecraft.world.food.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\Language.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\Dimension.h" +#include "..\Minecraft.World\net.minecraft.world.entity.boss.enderdragon.h" +#include "EnderDragonRenderer.h" +#include "..\Minecraft.World\net.minecraft.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "..\Minecraft.World\LevelChunk.h" +#include "..\Minecraft.World\Biome.h" + +#define RENDER_HUD 0 +//#ifndef _XBOX +//#undef RENDER_HUD +//#define RENDER_HUD 1 +//#endif + +float Gui::currentGuiBlendFactor = 1.0f; // 4J added +float Gui::currentGuiScaleFactor = 1.0f; // 4J added +ItemRenderer *Gui::itemRenderer = new ItemRenderer(); + +Gui::Gui(Minecraft *minecraft) +{ + // 4J - initialisers added + random = new Random(); + tickCount = 0; + overlayMessageTime = 0; + animateOverlayMessageColor = false; + progress = 0.0f; + tbr = 1.0f; + fAlphaIncrementPerCent=255.0f/100.0f; + + this->minecraft = minecraft; + + lastTickA = 0.0f; +} + +void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) +{ + // 4J Stu - I have copied this code for XUI_BaseScene. If/when it gets changed it should be broken out + // 4J - altered to force full screen mode to 3X scaling, and any split screen modes to 2X scaling. This is so that the further scaling by 0.5 that + // happens in split screen modes results in a final scaling of 1 rather than 1.5. + int splitYOffset;// = 20; // This offset is applied when doing the 2X scaling above to move the gui out of the way of the tool tips + int guiScale;// = ( minecraft->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN ? 3 : 2 ); + int iPad=minecraft->player->GetXboxPad(); + int iWidthOffset=0,iHeightOffset=0; // used to get the interface looking right on a 2 player split screen game + + // 4J-PB - selected the gui scale based on the slider settings + if(minecraft->player->m_iScreenSection == C4JRender::VIEWPORT_TYPE_FULLSCREEN) + { + guiScale=app.GetGameSettings(iPad,eGameSetting_UISize) + 2; + } + else + { + guiScale=app.GetGameSettings(iPad,eGameSetting_UISizeSplitscreen) + 2; + } + + + ScreenSizeCalculator ssc(minecraft->options, minecraft->width, minecraft->height, guiScale ); + int screenWidth = ssc.getWidth(); + int screenHeight = ssc.getHeight(); + int iSafezoneXHalf=0,iSafezoneYHalf=0; + int iTooltipsYOffset=0; + int quickSelectWidth=182; + int quickSelectHeight=22; + float fScaleFactorWidth=1.0f,fScaleFactorHeight=1.0f; + bool bTwoPlayerSplitscreen=false; + currentGuiScaleFactor = (float) guiScale; // Keep static copy of scale so we know how gui coordinates map to physical pixels - this is also affected by the viewport + + switch(guiScale) + { + case 3: + splitYOffset = 0; + break; + case 4: + splitYOffset = -5; + break; + default: // 2 + splitYOffset = 10; + break; + } + + // Check which screen section this player is in + switch(minecraft->player->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + // single player + iSafezoneXHalf = screenWidth/20; // 5% + iSafezoneYHalf = screenHeight/20; // 5% + iTooltipsYOffset=40+splitYOffset; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + iSafezoneXHalf = screenWidth/10; // 5% (need to treat the whole screen is 2x this screen) + iSafezoneYHalf = splitYOffset; + fScaleFactorWidth=0.5f; + iWidthOffset=(int)((float)screenWidth*(1.0f - fScaleFactorWidth)); + iTooltipsYOffset=44; + bTwoPlayerSplitscreen=true; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + iSafezoneXHalf = screenWidth/10; // 5% (need to treat the whole screen is 2x this screen) + iSafezoneYHalf = splitYOffset + screenHeight/10;// 5% (need to treat the whole screen is 2x this screen) + fScaleFactorWidth=0.5f; + iWidthOffset=(int)((float)screenWidth*(1.0f - fScaleFactorWidth)); + iTooltipsYOffset=44; + bTwoPlayerSplitscreen=true; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + iSafezoneXHalf = screenWidth/10; // 5% (the whole screen is 2x this screen) + iSafezoneYHalf = splitYOffset + screenHeight/10;// 5% (need to treat the whole screen is 2x this screen) + fScaleFactorHeight=0.5f; + iHeightOffset=screenHeight; + iTooltipsYOffset=44; + bTwoPlayerSplitscreen=true; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + iSafezoneXHalf = 0; + iSafezoneYHalf = splitYOffset + screenHeight/10;// 5% (need to treat the whole screen is 2x this screen) + fScaleFactorHeight=0.5f; + iHeightOffset=screenHeight; + iTooltipsYOffset=44; + bTwoPlayerSplitscreen=true; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + iSafezoneXHalf = screenWidth/10; // 5% (the whole screen is 2x this screen) + iSafezoneYHalf = splitYOffset; + iTooltipsYOffset=44; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + iSafezoneXHalf = 0; + iSafezoneYHalf = splitYOffset; // 5% + iTooltipsYOffset=44; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + iSafezoneXHalf = screenWidth/10; // 5% (the whole screen is 2x this screen) + iSafezoneYHalf = splitYOffset + screenHeight/10; // 5% (the whole screen is 2x this screen) + iTooltipsYOffset=44; + currentGuiScaleFactor *= 0.5f; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + iSafezoneXHalf = 0; + iSafezoneYHalf = splitYOffset + screenHeight/10; // 5% (the whole screen is 2x this screen) + iTooltipsYOffset=44; + currentGuiScaleFactor *= 0.5f; + break; + + } + + // 4J-PB - turn off the slot display if a xui menu is up, or if we're autosaving + bool bDisplayGui=!ui.GetMenuDisplayed(iPad) && !(app.GetXuiAction(iPad)==eAppAction_AutosaveSaveGameCapturedThumbnail); + + // if tooltips are off, set the y offset to zero + if(app.GetGameSettings(iPad,eGameSetting_Tooltips)==0 && bDisplayGui) + { + switch(minecraft->player->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + iTooltipsYOffset=screenHeight/10; + break; + default: + //iTooltipsYOffset=screenHeight/10; + switch(guiScale) + { + case 3: + iTooltipsYOffset=28;//screenHeight/10; + break; + case 4: + iTooltipsYOffset=28;//screenHeight/10; + break; + default: // 2 + iTooltipsYOffset=14;//screenHeight/10; + break; + } + break; + } + } + + // 4J-PB - Turn off interface if eGameSetting_DisplayHUD is off - for screen shots/videos. + if ( app.GetGameSettings(iPad,eGameSetting_DisplayHUD)==0 ) + { + bDisplayGui = false; + } + + Font *font = minecraft->font; + + + minecraft->gameRenderer->setupGuiScreen(guiScale); + + + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 4J - added - this did actually get set in renderVignette but that code is currently commented out + + if (Minecraft::useFancyGraphics()) + { + renderVignette(minecraft->player->getBrightness(a), screenWidth, screenHeight); + } + + ///////////////////////////////////////////////////////////////////////////////////// + // Display the pumpkin screen effect + ///////////////////////////////////////////////////////////////////////////////////// + + shared_ptr headGear = minecraft->player->inventory->getArmor(3); + + // 4J-PB - changing this to be per player + //if (!minecraft->options->thirdPersonView && headGear != NULL && headGear->id == Tile::pumpkin_Id) renderPumpkin(screenWidth, screenHeight); + if ((minecraft->player->ThirdPersonView()==0) && headGear != NULL && headGear->id == Tile::pumpkin_Id) renderPumpkin(screenWidth, screenHeight); + if (!minecraft->player->hasEffect(MobEffect::confusion)) + { + float pt = minecraft->player->oPortalTime + (minecraft->player->portalTime - minecraft->player->oPortalTime) * a; + if (pt > 0) + { + renderTp(pt, screenWidth, screenHeight); + } + } + + if (!minecraft->gameMode->isCutScene()) + { + if(bDisplayGui && bTwoPlayerSplitscreen) + { + // need to apply scale factors depending on the mode + glPushMatrix(); + glScalef(fScaleFactorWidth, fScaleFactorHeight, fScaleFactorWidth); + } +#if RENDER_HUD + ///////////////////////////////////////////////////////////////////////////////////// + // Display the quick select background, the quick select selection, and the crosshair + ///////////////////////////////////////////////////////////////////////////////////// + + glColor4f(1, 1, 1, 1); + + // 4J - this is where to set the blend factor for gui things + // use the primary player's settings + unsigned char ucAlpha=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_InterfaceOpacity); + + // If the user has started to navigate their quickselect bar, ignore the alpha setting, and display at default value + float fVal=fAlphaIncrementPerCent*(float)ucAlpha; + if(ucAlpha<80) + { + // check if we have the timer running for the opacity + unsigned int uiOpacityTimer=app.GetOpacityTimer(iPad); + if(uiOpacityTimer!=0) + { + if(uiOpacityTimer<10) + { + float fStep=(80.0f-(float)ucAlpha)/10.0f; + fVal=fAlphaIncrementPerCent*(80.0f-((10.0f-(float)uiOpacityTimer)*fStep)); + } + else + { + fVal=fAlphaIncrementPerCent*80.0f; + } + } + else + { + fVal=fAlphaIncrementPerCent*(float)ucAlpha; + } + } + else + { + fVal=fAlphaIncrementPerCent*(float)ucAlpha; + } + + RenderManager.StateSetBlendFactor(0xffffff |(((unsigned int)fVal)<<24)); + currentGuiBlendFactor = fVal / 255.0f; + // RenderManager.StateSetBlendFactor(0x40ffffff); + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + + blitOffset = -90; + + ///////////////////////////////////////////////////////////////////////////////////// + // Display the quick select background, the quick select selection, and the crosshair + ///////////////////////////////////////////////////////////////////////////////////// + if(bDisplayGui) + { + MemSect(31); + minecraft->textures->bindTexture(TN_GUI_GUI); // 4J was L"/gui/gui.png" + MemSect(0); + + shared_ptr inventory = minecraft->player->inventory; + if(bTwoPlayerSplitscreen) + { + // need to apply scale factors depending on the mode + + // 4J Stu - Moved this push and scale further up as we still need to do it for the few HUD components not replaced by xui + //glPushMatrix(); + //glScalef(fScaleFactorWidth, fScaleFactorHeight, fScaleFactorWidth); + + // 4J-PB - move into the safe zone, and account for 2 player splitscreen + blit(iWidthOffset + (screenWidth - quickSelectWidth)/2, iHeightOffset + screenHeight - iSafezoneYHalf - iTooltipsYOffset , 0, 0, 182, 22); + blit(iWidthOffset + (screenWidth - quickSelectWidth)/2 - 1 + inventory->selected * 20, iHeightOffset + screenHeight - iSafezoneYHalf - iTooltipsYOffset - 1, 0, 22, 24, 22); + } + else + { + blit(iWidthOffset + screenWidth / 2 - quickSelectWidth / 2, iHeightOffset + screenHeight - iSafezoneYHalf - iTooltipsYOffset , 0, 0, 182, 22); + blit(iWidthOffset + screenWidth / 2 - quickSelectWidth / 2 - 1 + inventory->selected * 20, iHeightOffset + screenHeight - iSafezoneYHalf - iTooltipsYOffset - 1, 0, 22, 24, 22); + } + + + MemSect(31); + minecraft->textures->bindTexture(TN_GUI_ICONS);//L"/gui/icons.png")); + MemSect(0); + glEnable(GL_BLEND); + RenderManager.StateSetBlendFactor(0xffffff |(((unsigned int)fVal)<<24)); + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + //glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); + // 4J Stu - We don't want to adjust the cursor by the safezone, we want it centred + if(bTwoPlayerSplitscreen) + { + blit(iWidthOffset + screenWidth / 2 - 7, (iHeightOffset + screenHeight) / 2 - 7, 0, 0, 16, 16); + } + else + { + blit(screenWidth / 2 - 7, screenHeight / 2 - 7, 0, 0, 16, 16); + } + glDisable(GL_BLEND); + + // if(bTwoPlayerSplitscreen) + // { + // glPopMatrix(); + // } + + } + + bool blink = minecraft->player->invulnerableTime / 3 % 2 == 1; + if (minecraft->player->invulnerableTime < 10) blink = false; + int iHealth = minecraft->player->getHealth(); + int iLastHealth = minecraft->player->lastHealth; + random->setSeed(tickCount * 312871); + + bool foodBlink = false; + FoodData *foodData = minecraft->player->getFoodData(); + int food = foodData->getFoodLevel(); + int oldFood = foodData->getLastFoodLevel(); + +// if (false) //(true) +// { +// renderBossHealth(); +// } + + ///////////////////////////////////////////////////////////////////////////////////// + // Display the experience, food, armour, health and the air bubbles + ///////////////////////////////////////////////////////////////////////////////////// + if(bDisplayGui) + { + // 4J - added blend for fading gui + glEnable(GL_BLEND); + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + + if (minecraft->gameMode->canHurtPlayer()) + { + int xLeft, xRight; + // 4J Stu - TODO Work out proper positioning for splitscreen + if(bTwoPlayerSplitscreen) + { + xLeft = iWidthOffset + (screenWidth - quickSelectWidth)/2; + xRight = iWidthOffset + (screenWidth + quickSelectWidth)/2; + } + else + { + xLeft = (screenWidth - quickSelectWidth)/2; + xRight = (screenWidth + quickSelectWidth) / 2; + } + + // render experience bar + int xpNeededForNextLevel = minecraft->player->getXpNeededForNextLevel(); + if (xpNeededForNextLevel > 0) + { + int w = 182; + + int progress = (int) (minecraft->player->experienceProgress * (float) (w + 1)); + + int yo = screenHeight - iSafezoneYHalf - iTooltipsYOffset - 8; + if(bTwoPlayerSplitscreen) + { + yo+=iHeightOffset; + } + blit(xLeft, yo, 0, 64, w, 5); + if (progress > 0) + { + blit(xLeft, yo, 0, 69, progress, 5); + } + } + + int yLine1, yLine2; + if(bTwoPlayerSplitscreen) + { + //yo = iHeightOffset + screenHeight - 10 - iSafezoneYHalf - iTooltipsYOffset; + yLine1 = iHeightOffset + screenHeight - 18 - iSafezoneYHalf - iTooltipsYOffset; + yLine2 = yLine1 - 10; + } + else + { + //yo = screenHeight - 10 - iSafezoneYHalf - iTooltipsYOffset; + yLine1 = screenHeight - 18 - iSafezoneYHalf - iTooltipsYOffset; + yLine2 = yLine1 - 10; + } + + int armor = minecraft->player->getArmorValue(); + int heartOffsetIndex = -1; + if (minecraft->player->hasEffect(MobEffect::regeneration)) + { + heartOffsetIndex = tickCount % 25; + } + + // render health and armor + for (int i = 0; i < Player::MAX_HEALTH / 2; i++) + { + if (armor > 0) + { + int xo = xLeft + i * 8; + + // HEALTH + if (i * 2 + 1 < armor) blit(xo, yLine2, 16 + 2 * 9, 9 * 1, 9, 9); + if (i * 2 + 1 == armor) blit(xo, yLine2, 16 + 1 * 9, 9 * 1, 9, 9); + if (i * 2 + 1 > armor) blit(xo, yLine2, 16 + 0 * 9, 9 * 1, 9, 9); + } + + int healthTexBaseX = 16; + if (minecraft->player->hasEffect(MobEffect::poison)) + { + healthTexBaseX += 4 * 9; + } + + int bg = 0; + if (blink) bg = 1; + int xo = xLeft + i * 8; + int yo = yLine1; + + if (iHealth <= 4) + { + yo += random->nextInt(2); + } + if (i == heartOffsetIndex) + { + yo -= 2; + } + + int y0 = 0; + // 4J-PB - no hardcore in xbox +// if (minecraft.level.getLevelData().isHardcore()) { +// y0 = 5; +// } + blit(xo, yo, 16 + bg * 9, 9 * 0, 9, 9); + if (blink) + { + if (i * 2 + 1 < iLastHealth) blit(xo, yo, healthTexBaseX + 6 * 9, 9 * y0, 9, 9); + if (i * 2 + 1 == iLastHealth) blit(xo, yo, healthTexBaseX + 7 * 9, 9 * y0, 9, 9); + } + if (i * 2 + 1 < iHealth) blit(xo, yo, healthTexBaseX + 4 * 9, 9 * y0, 9, 9); + if (i * 2 + 1 == iHealth) blit(xo, yo, healthTexBaseX + 5 * 9, 9 * y0, 9, 9); + } + + // render food + for (int i = 0; i < FoodConstants::MAX_FOOD / 2; i++) + { + int yo = yLine1; + + + int texBaseX = 16; + int bg = 0; + if (minecraft->player->hasEffect(MobEffect::hunger)) + { + texBaseX += 4 * 9; + bg = 13; + } + + if (minecraft->player->getFoodData()->getSaturationLevel() <= 0) + { + if ((tickCount % (food * 3 + 1)) == 0) + { + yo += random->nextInt(3) - 1; + } + } + + if (foodBlink) bg = 1; + int xo = xRight - i * 8 - 9; + blit(xo, yo, 16 + bg * 9, 9 * 3, 9, 9); + if (foodBlink) + { + if (i * 2 + 1 < oldFood) blit(xo, yo, texBaseX + 6 * 9, 9 * 3, 9, 9); + if (i * 2 + 1 == oldFood) blit(xo, yo, texBaseX + 7 * 9, 9 * 3, 9, 9); + } + if (i * 2 + 1 < food) blit(xo, yo, texBaseX + 4 * 9, 9 * 3, 9, 9); + if (i * 2 + 1 == food) blit(xo, yo, texBaseX + 5 * 9, 9 * 3, 9, 9); + } + + // render air bubbles + if (minecraft->player->isUnderLiquid(Material::water)) + { + int count = (int) ceil((minecraft->player->getAirSupply() - 2) * 10.0f / Player::TOTAL_AIR_SUPPLY); + int extra = (int) ceil((minecraft->player->getAirSupply()) * 10.0f / Player::TOTAL_AIR_SUPPLY) - count; + for (int i = 0; i < count + extra; i++) + { + // Air bubbles + if (i < count) blit(xRight - i * 8 - 9, yLine2, 16, 9 * 2, 9, 9); + else blit(xRight - i * 8 - 9, yLine2, 16 + 9, 9 * 2, 9, 9); + } + } + } + + } + + // 4J-PB - turn off the slot display if a xui menu is up + + //////////////////////////// + // render the slot contents + //////////////////////////// + if(bDisplayGui) + { + // glDisable(GL_BLEND); 4J - removed - we want to be able to fade our gui + + glEnable(GL_RESCALE_NORMAL); + + Lighting::turnOnGui(); + + + int x,y; + + for (int i = 0; i < 9; i++) + { + if(bTwoPlayerSplitscreen) + { + x = iWidthOffset + screenWidth / 2 - 9 * 10 + i * 20 + 2; + y = iHeightOffset + screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22; + } + else + { + x = screenWidth / 2 - 9 * 10 + i * 20 + 2; + y = screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22; + } + this->renderSlot(i, x, y, a); + } + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + } +#endif // RENDER_HUD + + // 4J - do render of crouched player. This code is largely taken from the inventory render of the player, with some special hard-coded positions + // worked out by hand from the xui implementation of the crouch icon + + if(app.GetGameSettings(iPad,eGameSetting_AnimatedCharacter)) + { + //int playerIdx = minecraft->player->GetXboxPad(); + + static int characterDisplayTimer[4] = {0}; + if( !bDisplayGui ) + { + characterDisplayTimer[iPad] = 0; + } + else if( minecraft->player->isSneaking() ) + { + characterDisplayTimer[iPad] = 30; + } + else if( minecraft->player->isSprinting() ) + { + characterDisplayTimer[iPad] = 30; + } + else if( minecraft->player->abilities.flying) + { + characterDisplayTimer[iPad] = 5; // quickly get rid of the player display if they stop flying + } + else if( characterDisplayTimer[iPad] > 0 ) + { + --characterDisplayTimer[iPad]; + } + bool displayCrouch = minecraft->player->isSneaking() || ( characterDisplayTimer[iPad] > 0 ); + bool displaySprint = minecraft->player->isSprinting() || ( characterDisplayTimer[iPad] > 0 ); + bool displayFlying = minecraft->player->abilities.flying || ( characterDisplayTimer[iPad] > 0 ); + + if( bDisplayGui && (displayCrouch || displaySprint || displayFlying) ) + { + EntityRenderDispatcher::instance->prepare(minecraft->level, minecraft->textures, minecraft->font, minecraft->cameraTargetPlayer, minecraft->options, a); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + int xo = 0; + int yo = 0; + switch( minecraft->player->m_iScreenSection ) + { + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + default: + if(RenderManager.IsHiDef()) xo = -22; + yo = -36; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + xo = 0; yo = -25; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + xo = 0; yo = -48; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT: + xo = 0; yo = -25; + break; + case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT: + xo = -43; yo = -25; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT: + xo = 0; yo = -25; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT: + xo = -43; yo = -25; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT: + xo = 0; yo = -48; + break; + case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT: + xo = -43; yo = -48; + break; + } + glPushMatrix(); + glTranslatef((float)xo + 51, (float)yo + 75, 50); + float ss = 12; + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + float oyr = minecraft->player->yRot; + float oyrO = minecraft->player->yRotO; + float oxr = minecraft->player->xRot; + int ofire = minecraft->player->onFire; + bool ofireflag = minecraft->player->getSharedFlag(Entity::FLAG_ONFIRE); + + float xd = -40; + float yd = 10; + + // 4J Stu - This is all based on the inventory player renderer, with changes to ensure that capes render correctly + // by minimising the changes to member variables of the player which are all related + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float) atan(yd / 40.0f ) * 20, 1, 0, 0); + float bodyRot = (minecraft->player->yBodyRotO + (minecraft->player->yBodyRot - minecraft->player->yBodyRotO)); + // Fixed rotation angle of degrees, adjusted by bodyRot to negate the rotation that occurs in the renderer + // bodyRot in the rotation below is a simplification of "180 - (180 - bodyRot)" where the first 180 is EntityRenderDispatcher::instance->playerRotY that we set below + // and (180 - bodyRot) is the angle of rotation that is performed within the mob renderer + glRotatef( bodyRot - ( (float) atan(xd / 40.0f) * 20), 0, 1, 0); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + // Set head rotation to body rotation to make head static + minecraft->player->yRot = bodyRot; + minecraft->player->yRotO = minecraft->player->yRot; + minecraft->player->xRot = -(float) atan(yd / 40.0f) * 20; + + minecraft->player->onFire = 0; + minecraft->player->setSharedFlag(Entity::FLAG_ONFIRE, false); + + glTranslatef(0, minecraft->player->heightOffset, 0); + EntityRenderDispatcher::instance->playerRotY = 180; + EntityRenderDispatcher::instance->isGuiRender = true; + EntityRenderDispatcher::instance->render(minecraft->player, 0, 0, 0, 0, 1); + EntityRenderDispatcher::instance->isGuiRender = false; + + minecraft->player->yRot = oyr; + minecraft->player->yRotO = oyrO; + minecraft->player->xRot = oxr; + minecraft->player->onFire = ofire; + minecraft->player->setSharedFlag(Entity::FLAG_ONFIRE,ofireflag); + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); + } + } + } + +#if RENDER_HUD + // Moved so the opacity blend is applied to it + if (bDisplayGui && minecraft->gameMode->hasExperience() && minecraft->player->experienceLevel > 0) + { + if (true) + { + bool blink = false; + int col = blink ? 0xffffff : 0x80ff20; + wchar_t formatted[10]; + swprintf(formatted, 10, L"%d",minecraft->player->experienceLevel); + + wstring str = formatted; + int x = iWidthOffset + (screenWidth - font->width(str)) / 2; + int y = screenHeight - iSafezoneYHalf - iTooltipsYOffset; + // If we're in creative mode, we don't need to offset the XP display so much + if (minecraft->gameMode->canHurtPlayer()) + { + y-=18; + } + else + { + y-=13; + } + + if(bTwoPlayerSplitscreen) + { + y+=iHeightOffset; + } + //int y = screenHeight - 31 - 4; + font->draw(str, x + 1, y, 0x000000); + font->draw(str, x - 1, y, 0x000000); + font->draw(str, x, y + 1, 0x000000); + font->draw(str, x, y - 1, 0x000000); + // font->draw(str, x + 1, y + 1, 0x000000); + // font->draw(str, x - 1, y + 1, 0x000000); + // font->draw(str, x + 1, y - 1, 0x000000); + // font->draw(str, x - 1, y - 1, 0x000000); + font->draw(str, x, y, col); + } + } +#endif // RENDER_HUD + + // 4J - added to disable blends, which we have enabled previously to allow gui fading + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // if the player is falling asleep we render a dark overlay + if (minecraft->player->getSleepTimer() > 0) + { + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + int timer = minecraft->player->getSleepTimer(); + float amount = (float) timer / (float) Player::SLEEP_DURATION; + if (amount > 1) + { + // waking up + amount = 1.0f - ((float) (timer - Player::SLEEP_DURATION) / (float) Player::WAKE_UP_DURATION); + } + + int color = (int) (220.0f * amount) << 24 | (0x101020); + fill(0, 0, screenWidth/fScaleFactorWidth, screenHeight/fScaleFactorHeight, color); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + } + + // 4J-PB - Request from Mojang to have a red death screen + if (!minecraft->player->isAlive()) + { + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + int timer = minecraft->player->getDeathFadeTimer(); + float amount = (float) timer / (float) Player::DEATHFADE_DURATION; + + int color = (int) (220.0f * amount) << 24 | (0x200000); + fill(0, 0, screenWidth/fScaleFactorWidth, screenHeight/fScaleFactorHeight, color); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + + } + + + // { + // String str = "" + minecraft.player.getFoodData().getExhaustionLevel() + ", " + minecraft.player.getFoodData().getSaturationLevel(); + // int x = (screenWidth - font.width(str)) / 2; + // int y = screenHeight - 64; + // font.draw(str, x + 1, y, 0xffffff); + // } + +#ifndef _FINAL_BUILD + MemSect(31); + if (minecraft->options->renderDebug) + { + glPushMatrix(); + if (Minecraft::warezTime > 0) glTranslatef(0, 32, 0); + font->drawShadow(ClientConstants::VERSION_STRING + L" (" + minecraft->fpsString + L")", iSafezoneXHalf+2, 20, 0xffffff); + font->drawShadow(L"Seed: " + _toString<__int64>(minecraft->level->getLevelData()->getSeed() ), iSafezoneXHalf+2, 32 + 00, 0xffffff); + font->drawShadow(minecraft->gatherStats1(), iSafezoneXHalf+2, 32 + 10, 0xffffff); + font->drawShadow(minecraft->gatherStats2(), iSafezoneXHalf+2, 32 + 20, 0xffffff); + font->drawShadow(minecraft->gatherStats3(), iSafezoneXHalf+2, 32 + 30, 0xffffff); + font->drawShadow(minecraft->gatherStats4(), iSafezoneXHalf+2, 32 + 40, 0xffffff); + + // TERRAIN FEATURES + int iYPos=82; + + if(minecraft->level->dimension->id==0) + { + wstring wfeature[eTerrainFeature_Count]; + + wfeature[eTerrainFeature_Stronghold] = L"Stronghold: "; + wfeature[eTerrainFeature_Mineshaft] = L"Mineshaft: "; + wfeature[eTerrainFeature_Village] = L"Village: "; + wfeature[eTerrainFeature_Ravine] = L"Ravine: "; + + for(int i=0;i( pFeatureData->x*16 ) + L", " + _toString( pFeatureData->z*16 ) + L"] "; + wfeature[pFeatureData->eTerrainFeature] += itemInfo; + } + + for( int i = eTerrainFeature_Stronghold; i < (int) eTerrainFeature_Count; i++ ) + { + font->drawShadow(wfeature[i], iSafezoneXHalf + 2, iYPos, 0xffffff); + iYPos+=10; + } + } + + //font->drawShadow(minecraft->gatherStats5(), iSafezoneXHalf+2, 32 + 10, 0xffffff); + { + /* 4J - removed + long max = Runtime.getRuntime().maxMemory(); + long total = Runtime.getRuntime().totalMemory(); + long free = Runtime.getRuntime().freeMemory(); + long used = total - free; + String msg = "Used memory: " + (used * 100 / max) + "% (" + (used / 1024 / 1024) + "MB) of " + (max / 1024 / 1024) + "MB"; + drawString(font, msg, screenWidth - font.width(msg) - 2, 2, 0xe0e0e0); + msg = "Allocated memory: " + (total * 100 / max) + "% (" + (total / 1024 / 1024) + "MB)"; + drawString(font, msg, screenWidth - font.width(msg) - 2, 12, 0xe0e0e0); + */ + } + // 4J Stu - Moved these so that they don't overlap + double xBlockPos = floor(minecraft->player->x); + double yBlockPos = floor(minecraft->player->y); + double zBlockPos = floor(minecraft->player->z); + drawString(font, L"x: " + _toString(minecraft->player->x) + L"/ Head: " + _toString(xBlockPos) + L"/ Chunk: " + _toString(minecraft->player->xChunk), iSafezoneXHalf+2, iYPos + 8 * 0, 0xe0e0e0); + drawString(font, L"y: " + _toString(minecraft->player->y) + L"/ Head: " + _toString(yBlockPos), iSafezoneXHalf+2, iYPos + 8 * 1, 0xe0e0e0); + drawString(font, L"z: " + _toString(minecraft->player->z) + L"/ Head: " + _toString(zBlockPos) + L"/ Chunk: " + _toString(minecraft->player->zChunk), iSafezoneXHalf+2, iYPos + 8 * 2, 0xe0e0e0); + drawString(font, L"f: " + _toString(Mth::floor(minecraft->player->yRot * 4.0f / 360.0f + 0.5) & 0x3) + L"/ yRot: " + _toString(minecraft->player->yRot), iSafezoneXHalf+2, iYPos + 8 * 3, 0xe0e0e0); + iYPos += 8*4; + + int px = Mth::floor(minecraft->player->x); + int py = Mth::floor(minecraft->player->y); + int pz = Mth::floor(minecraft->player->z); + if (minecraft->level != NULL && minecraft->level->hasChunkAt(px, py, pz)) + { + LevelChunk *chunkAt = minecraft->level->getChunkAt(px, pz); + Biome *biome = chunkAt->getBiome(px & 15, pz & 15, minecraft->level->getBiomeSource()); + drawString( + font, + L"b: " + biome->m_name + L" (" + _toString(biome->id) + L")", iSafezoneXHalf+2, iYPos, 0xe0e0e0); + } + + glPopMatrix(); + } + MemSect(0); +#endif + + lastTickA = a; + // 4J Stu - This is now displayed in a xui scene +#if 0 + // Jukebox CD message + if (overlayMessageTime > 0) + { + float t = overlayMessageTime - a; + int alpha = (int) (t * 256 / 20); + if (alpha > 255) alpha = 255; + if (alpha > 0) + { + glPushMatrix(); + + if(bTwoPlayerSplitscreen) + { + glTranslatef((float)((screenWidth / 2)+iWidthOffset), ((float)(screenHeight+iHeightOffset)) - iTooltipsYOffset -12 -iSafezoneYHalf, 0); + } + else + { + glTranslatef(((float)screenWidth) / 2, ((float)screenHeight) - iTooltipsYOffset - 12 -iSafezoneYHalf, 0); + } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + int col = 0xffffff; + if (animateOverlayMessageColor) + { + col = Color::HSBtoRGB(t / 50.0f, 0.7f, 0.6f) & 0xffffff; + } + // 4J-PB - this is the string displayed when cds are placed in a jukebox + font->draw(overlayMessageString,-font->width(overlayMessageString) / 2, -20, col + (alpha << 24)); + glDisable(GL_BLEND); + glPopMatrix(); + } + } +#endif + + unsigned int max = 10; + bool isChatting = false; + if (dynamic_cast(minecraft->screen) != NULL) + { + max = 20; + isChatting = true; + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_ALPHA_TEST); + +// 4J Stu - We have moved the chat text to a xui +#if 0 + glPushMatrix(); + // 4J-PB we need to move this up a bit because we've moved the quick select + //glTranslatef(0, ((float)screenHeight) - 48, 0); + glTranslatef(0.0f, (float)(screenHeight - iSafezoneYHalf - iTooltipsYOffset - 16 - 3 + 22) - 24.0f, 0.0f); + // glScalef(1.0f / ssc.scale, 1.0f / ssc.scale, 1); + + // 4J-PB - we need gui messages for each of the possible 4 splitscreen players + if(bDisplayGui) + { + int iPad=minecraft->player->GetXboxPad(); + for (unsigned int i = 0; i < guiMessages[iPad].size() && i < max; i++) + { + if (guiMessages[iPad][i].ticks < 20 * 10 || isChatting) + { + double t = guiMessages[iPad][i].ticks / (20 * 10.0); + t = 1 - t; + t = t * 10; + if (t < 0) t = 0; + if (t > 1) t = 1; + t = t * t; + int alpha = (int) (255 * t); + if (isChatting) alpha = 255; + + if (alpha > 0) + { + int x = iSafezoneXHalf+2; + int y = -((int)i) * 9; + if(bTwoPlayerSplitscreen) + { + y+= iHeightOffset; + } + + wstring msg = guiMessages[iPad][i].string; + // 4J-PB - fill the black bar across the whole screen, otherwise it looks odd due to the safe area + this->fill(0, y - 1, screenWidth/fScaleFactorWidth, y + 8, (alpha / 2) << 24); + glEnable(GL_BLEND); + + font->drawShadow(msg, iSafezoneXHalf+4, y, 0xffffff + (alpha << 24)); + } + } + } + } + glPopMatrix(); +#endif + + // 4J Stu - Copied over but not used +#if 0 + if (minecraft.player instanceof MultiplayerLocalPlayer && minecraft.options.keyPlayerList.isDown) + { + ClientConnection connection = ((MultiplayerLocalPlayer) minecraft.player).connection; + List playerInfos = connection.playerInfos; + int slots = connection.maxPlayers; + + int rows = slots; + int cols = 1; + while (rows > 20) { + cols++; + rows = (slots + cols - 1) / cols; + } + + /* + * int fakeCount = 39; while (playerInfos.size() > fakeCount) + * playerInfos.remove(playerInfos.size() - 1); while (playerInfos.size() < + * fakeCount) playerInfos.add(new PlayerInfo("fiddle")); + */ + + int slotWidth = 300 / cols; + if (slotWidth > 150) slotWidth = 150; + + int xxo = (screenWidth - cols * slotWidth) / 2; + int yyo = 10; + fill(xxo - 1, yyo - 1, xxo + slotWidth * cols, yyo + 9 * rows, 0x80000000); + for (int i = 0; i < slots; i++) { + int xo = xxo + i % cols * slotWidth; + int yo = yyo + i / cols * 9; + + fill(xo, yo, xo + slotWidth - 1, yo + 8, 0x20ffffff); + glColor4f(1, 1, 1, 1); + glEnable(GL_ALPHA_TEST); + + if (i < playerInfos.size()) { + PlayerInfo pl = playerInfos.get(i); + font.drawShadow(pl.name, xo, yo, 0xffffff); + minecraft.textures.bind(minecraft.textures.loadTexture("/gui/icons.png")); + int xt = 0; + int yt = 0; + xt = 0; + yt = 0; + if (pl.latency < 0) yt = 5; + else if (pl.latency < 150) yt = 0; + else if (pl.latency < 300) yt = 1; + else if (pl.latency < 600) yt = 2; + else if (pl.latency < 1000) yt = 3; + else yt = 4; + + blitOffset += 100; + blit(xo + slotWidth - 12, yo, 0 + xt * 10, 176 + yt * 8, 10, 8); + blitOffset -= 100; + } + } + } +#endif + + if(bDisplayGui && bTwoPlayerSplitscreen) + { + // pop the scaled matrix + glPopMatrix(); + } + + if (ClientConstants::SHOW_LCEMP_WATERMARK) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + font->drawShadow(L"LCEMP by notpies", 2, screenHeight - 10, 0x55ffffff); + } + + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); +} + +// Moved to the xui base scene +// void Gui::renderBossHealth(void) +// { +// if (EnderDragonRenderer::bossInstance == NULL) return; +// +// shared_ptr boss = EnderDragonRenderer::bossInstance; +// EnderDragonRenderer::bossInstance = NULL; +// +// Minecraft *pMinecraft=Minecraft::GetInstance(); +// +// Font *font = pMinecraft->font; +// +// ScreenSizeCalculator ssc(pMinecraft->options, pMinecraft->width_phys, pMinecraft->height_phys); +// int screenWidth = ssc.getWidth(); +// +// int w = 182; +// int xLeft = screenWidth / 2 - w / 2; +// +// int progress = (int) (boss->getSynchedHealth() / (float) boss->getMaxHealth() * (float) (w + 1)); +// +// int yo = 12; +// blit(xLeft, yo, 0, 74, w, 5); +// blit(xLeft, yo, 0, 74, w, 5); +// if (progress > 0) +// { +// blit(xLeft, yo, 0, 79, progress, 5); +// } +// +// wstring msg = L"Boss health - NON LOCALISED"; +// font->drawShadow(msg, screenWidth / 2 - font->width(msg) / 2, yo - 10, 0xff00ff); +// glColor4f(1, 1, 1, 1); +// glBindTexture(GL_TEXTURE_2D, pMinecraft->textures->loadTexture(TN_GUI_ICONS) );//"/gui/icons.png")); +// +// } + +void Gui::renderPumpkin(int w, int h) +{ + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1, 1, 1, 1); + glDisable(GL_ALPHA_TEST); + + MemSect(31); + minecraft->textures->bindTexture(TN__BLUR__MISC_PUMPKINBLUR);//L"%blur%/misc/pumpkinblur.png")); + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertexUV((float)(0), (float)( h), (float)( -90), (float)( 0), (float)( 1)); + t->vertexUV((float)(w), (float)( h), (float)( -90), (float)( 1), (float)( 1)); + t->vertexUV((float)(w), (float)( 0), (float)( -90), (float)( 1), (float)( 0)); + t->vertexUV((float)(0), (float)( 0), (float)( -90), (float)( 0), (float)( 0)); + t->end(); + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + glEnable(GL_ALPHA_TEST); + glColor4f(1, 1, 1, 1); + +} + +void Gui::renderVignette(float br, int w, int h) +{ + br = 1 - br; + if (br < 0) br = 0; + if (br > 1) br = 1; + tbr += (br - tbr) * 0.01f; + +#if 0 // 4J - removed - TODO put back when we have blend functions implemented + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glColor4f(tbr, tbr, tbr, 1); + glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture(TN__BLUR__MISC_VIGNETTE));//L"%blur%/misc/vignette.png")); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertexUV((float)(0), (float)( h), (float)( -90), (float)( 0), (float)( 1)); + t->vertexUV((float)(w), (float)( h), (float)( -90), (float)( 1), (float)( 1)); + t->vertexUV((float)(w), (float)( 0), (float)( -90), (float)( 1), (float)( 0)); + t->vertexUV((float)(0), (float)( 0), (float)( -90), (float)( 0), (float)( 0)); + t->end(); + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + glColor4f(1, 1, 1, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +#endif +} + +void Gui::renderTp(float br, int w, int h) +{ + if (br < 1) + { + br = br * br; + br = br * br; + br = br * 0.8f + 0.2f; + } + + glDisable(GL_ALPHA_TEST); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1, 1, 1, br); + MemSect(31); + minecraft->textures->bindTexture(TN_TERRAIN);//L"/terrain.png")); + MemSect(0); + + Icon *slot = Tile::portalTile->getTexture(Facing::UP); + float u0 = slot->getU0(); + float v0 = slot->getV0(); + float u1 = slot->getU1(); + float v1 = slot->getV1(); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertexUV((float)(0), (float)( h), (float)( -90), (float)( u0), (float)( v1)); + t->vertexUV((float)(w), (float)( h), (float)( -90), (float)( u1), (float)( v1)); + t->vertexUV((float)(w), (float)( 0), (float)( -90), (float)( u1), (float)( v0)); + t->vertexUV((float)(0), (float)( 0), (float)( -90), (float)( u0), (float)( v0)); + t->end(); + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + glEnable(GL_ALPHA_TEST); + glColor4f(1, 1, 1, 1); + +} + +void Gui::renderSlot(int slot, int x, int y, float a) +{ + shared_ptr item = minecraft->player->inventory->items[slot]; + if (item == NULL) return; + + float pop = item->popTime - a; + if (pop > 0) + { + glPushMatrix(); + float squeeze = 1 + pop / (float) Inventory::POP_TIME_DURATION; + glTranslatef((float)(x + 8), (float)(y + 12), 0); + glScalef(1 / squeeze, (squeeze + 1) / 2, 1); + glTranslatef((float)-(x + 8), (float)-(y + 12), 0); + } + + itemRenderer->renderAndDecorateItem(minecraft->font, minecraft->textures, item, x, y); + + if (pop > 0) + { + glPopMatrix(); + } + + itemRenderer->renderGuiItemDecorations(minecraft->font, minecraft->textures, item, x, y); + +} + +void Gui::tick() +{ + if (overlayMessageTime > 0) overlayMessageTime--; + tickCount++; + + for(int iPad=0;iPadlocalplayers[i]) + { + guiMessages[i].clear(); + } + } + } + else + { + guiMessages[iPad].clear(); + } +} + + +void Gui::addMessage(const wstring& _string,int iPad,bool bIsDeathMessage) +{ + wstring string = _string; // 4J - Take copy of input as it is const + //int iScale=1; + + //if((minecraft->player->m_iScreenSection==C4JRender::VIEWPORT_TYPE_SPLIT_TOP) || + // (minecraft->player->m_iScreenSection==C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM)) + //{ + // iScale=2; + //} + + // while (minecraft->font->width(string) > (m_iMaxMessageWidth*iScale)) + //{ + // unsigned int i = 1; + // while (i < string.length() && minecraft->font->width(string.substr(0, i + 1)) <= (m_iMaxMessageWidth*iScale)) + // { + // i++; + // } + // int iLast=string.find_last_of(L" ",i); + + // // if a space was found, include the space on this line + // if(iLast!=i) + // { + // iLast++; + // } + // addMessage(string.substr(0, iLast), iPad); + // string = string.substr(iLast); + // } + + int maximumChars; + + switch(minecraft->player->m_iScreenSection) + { + case C4JRender::VIEWPORT_TYPE_SPLIT_TOP: + case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM: + case C4JRender::VIEWPORT_TYPE_FULLSCREEN: + if(RenderManager.IsHiDef()) + { + maximumChars = 105; + } + else + { + maximumChars = 55; + } +#ifdef __PSVITA__ + maximumChars = 90; +#endif + switch(XGetLanguage()) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + case XC_LANGUAGE_KOREAN: + if(RenderManager.IsHiDef()) + { + maximumChars = 70; + } + else + { + maximumChars = 35; + } +#ifdef __PSVITA__ + maximumChars = 55; +#endif + break; + } + break; + default: + maximumChars = 55; + switch(XGetLanguage()) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + case XC_LANGUAGE_KOREAN: + maximumChars = 35; + break; + } + break; + } + + + while (string.length() > maximumChars) + { + unsigned int i = 1; + while (i < string.length() && (i + 1) <= maximumChars) + { + i++; + } + int iLast=(int)string.find_last_of(L" ",i); + switch(XGetLanguage()) + { + case XC_LANGUAGE_JAPANESE: + case XC_LANGUAGE_TCHINESE: + case XC_LANGUAGE_KOREAN: + iLast = maximumChars; + break; + default: + iLast=(int)string.find_last_of(L" ",i); + break; + } + + // if a space was found, include the space on this line + if(iLast!=i) + { + iLast++; + } + addMessage(string.substr(0, iLast), iPad, bIsDeathMessage); + string = string.substr(iLast); + } + + if(iPad==-1) + { + // add to all + for(int i=0;ilocalplayers[i] && !(bIsDeathMessage && app.GetGameSettings(i,eGameSetting_DeathMessages)==0)) + { + guiMessages[i].insert(guiMessages[i].begin(), GuiMessage(string)); + while (guiMessages[i].size() > 50) + { + guiMessages[i].pop_back(); + } + } + } + } + else if(!(bIsDeathMessage && app.GetGameSettings(iPad,eGameSetting_DeathMessages)==0)) + { + guiMessages[iPad].insert(guiMessages[iPad].begin(), GuiMessage(string)); + while (guiMessages[iPad].size() > 50) + { + guiMessages[iPad].pop_back(); + } + } + + +} + +// 4J Added +float Gui::getOpacity(int iPad, DWORD index) +{ + float opacityPercentage = 0; + if (guiMessages[iPad].size() > index && guiMessages[iPad][index].ticks < 20 * 10) + { + double t = guiMessages[iPad][index].ticks / (20 * 10.0); + t = 1 - t; + t = t * 10; + if (t < 0) t = 0; + if (t > 1) t = 1; + t = t * t; + opacityPercentage = t; + } + return opacityPercentage; +} + +float Gui::getJukeboxOpacity(int iPad) +{ + float t = overlayMessageTime - lastTickA; + int alpha = (int) (t * 256 / 20); + if (alpha > 255) alpha = 255; + alpha /= 255; + + return alpha; +} + +void Gui::setNowPlaying(const wstring& string) +{ +// overlayMessageString = L"Now playing: " + string; + overlayMessageString = app.GetString(IDS_NOWPLAYING) + string; + overlayMessageTime = 20 * 3; + animateOverlayMessageColor = true; +} + +void Gui::displayClientMessage(int messageId, int iPad) +{ + //Language *language = Language::getInstance(); + wstring languageString = app.GetString(messageId);//language->getElement(messageId); + + addMessage(languageString, iPad); +} + +// 4J Added +void Gui::renderGraph(int dataLength, int dataPos, __int64 *dataA, float dataAScale, int dataAWarning, __int64 *dataB, float dataBScale, int dataBWarning) +{ + int height = minecraft->height; + // This causes us to cover xScale*dataLength pixels in the horizontal + int xScale = 1; + if(dataA != NULL && dataB != NULL) xScale = 2; + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, (float)minecraft->width, (float)height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glLineWidth(1); + glDisable(GL_TEXTURE_2D); + Tesselator *t = Tesselator::getInstance(); + + t->begin(GL_LINES); + for (int i = 0; i < dataLength; i++) + { + int col = ((i - dataPos) & (dataLength - 1)) * 255 / dataLength; + int cc = col * col / 255; + cc = cc * cc / 255; + int cc2 = cc * cc / 255; + cc2 = cc2 * cc2 / 255; + + if( dataA != NULL ) + { + if (dataA[i] > dataAWarning) + { + t->color(0xff000000 + cc * 65536); + } + else + { + t->color(0xff000000 + cc * 256); + } + + __int64 aVal = dataA[i] / dataAScale; + + t->vertex((float)(xScale*i + 0.5f), (float)( height - aVal + 0.5f), (float)( 0)); + t->vertex((float)(xScale*i + 0.5f), (float)( height + 0.5f), (float)( 0)); + } + + if( dataB != NULL ) + { + if (dataB[i]>dataBWarning) + { + t->color(0xff000000 + cc * 65536 + cc * 256 + cc * 1); + } + else + { + t->color(0xff808080 + cc/2 * 256); + } + + __int64 bVal = dataB[i] / dataBScale; + + t->vertex((float)(xScale*i + (xScale - 1) + 0.5f), (float)( height - bVal + 0.5f), (float)( 0)); + t->vertex((float)(xScale*i + (xScale - 1) + 0.5f), (float)( height + 0.5f), (float)( 0)); + } + } + t->end(); + + glEnable(GL_TEXTURE_2D); +} + +void Gui::renderStackedGraph(int dataPos, int dataLength, int dataSources, __int64 (*func)(unsigned int dataPos, unsigned int dataSource) ) +{ + int height = minecraft->height; + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, (float)minecraft->width, (float)height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glLineWidth(1); + glDisable(GL_TEXTURE_2D); + Tesselator *t = Tesselator::getInstance(); + + t->begin(GL_LINES); + __int64 thisVal = 0; + __int64 topVal = 0; + for (int i = 0; i < dataLength; i++) + { + thisVal = 0; + topVal = 0; + int col = ((i - dataPos) & (dataLength - 1)) * 255 / dataLength; + int cc = col * col / 255; + cc = cc * cc / 255; + int cc2 = cc * cc / 255; + cc2 = cc2 * cc2 / 255; + + + for(unsigned int source = 0; source < dataSources; ++source ) + { + thisVal = func( i, source ); + + if( thisVal > 0 ) + { + float vary = (float)source/dataSources; + int fColour = floor(vary * 0xffffff); + + int colour = 0xff000000 + fColour; + //printf("Colour is %x\n", colour); + t->color(colour); + + t->vertex((float)(i + 0.5f), (float)( height - topVal - thisVal + 0.5f), (float)( 0)); + t->vertex((float)(i + 0.5f), (float)( height - topVal + 0.5f), (float)( 0)); + + topVal += thisVal; + } + } + + // Draw some horizontals + for(unsigned int horiz = 1; horiz < 7; ++horiz ) + { + t->color(0xff000000); + + t->vertex((float)(0 + 0.5f), (float)( height - (horiz*100) + 0.5f), (float)( 0)); + t->vertex((float)(dataLength + 0.5f), (float)( height - (horiz*100) + 0.5f), (float)( 0)); + } + } + t->end(); + + glEnable(GL_TEXTURE_2D); +} diff --git a/Minecraft.Client/Gui.h b/Minecraft.Client/Gui.h new file mode 100644 index 0000000..4176201 --- /dev/null +++ b/Minecraft.Client/Gui.h @@ -0,0 +1,68 @@ +#pragma once +#include "GuiComponent.h" +#include "GuiMessage.h" +class Random; +class Minecraft; +class ItemRenderer; + +class Gui : public GuiComponent +{ +private: + // 4J-PB - this doesn't account for the safe zone, and the indent applied to messages + //static const int MAX_MESSAGE_WIDTH = 320; + static const int m_iMaxMessageWidth = 280; + static ItemRenderer *itemRenderer; + vector guiMessages[XUSER_MAX_COUNT]; + Random *random; + + Minecraft *minecraft; +public: + wstring selectedName; +private: + int tickCount; + wstring overlayMessageString; + int overlayMessageTime; + bool animateOverlayMessageColor; + + // 4J Added + float lastTickA; + float fAlphaIncrementPerCent; +public: + static float currentGuiBlendFactor; // 4J added + static float currentGuiScaleFactor; // 4J added + + float progress; + +// private DecimalFormat df = new DecimalFormat("##.00"); + +public: + Gui(Minecraft *minecraft); + + void render(float a, bool mouseFree, int xMouse, int yMouse); + float tbr; + +private: + //void renderBossHealth(void); + void renderPumpkin(int w, int h); + void renderVignette(float br, int w, int h); + void renderTp(float br, int w, int h); + void renderSlot(int slot, int x, int y, float a); +public: + void tick(); + void clearMessages(int iPad=-1); + void addMessage(const wstring& string, int iPad,bool bIsDeathMessage=false); + void setNowPlaying(const wstring& string); + void displayClientMessage(int messageId, int iPad); + + // 4J Added + DWORD getMessagesCount(int iPad) { return (int)guiMessages[iPad].size(); } + wstring getMessage(int iPad, DWORD index) { return guiMessages[iPad].at(index).string; } + float getOpacity(int iPad, DWORD index); + + wstring getJukeboxMessage(int iPad) { return overlayMessageString; } + float getJukeboxOpacity(int iPad); + + // 4J Added + void renderGraph(int dataLength, int dataPos, __int64 *dataA, float dataAScale, int dataAWarning, __int64 *dataB, float dataBScale, int dataBWarning); + void renderStackedGraph(int dataPos, int dataLength, int dataSources, __int64 (*func)(unsigned int dataPos, unsigned int dataSource) ); +}; diff --git a/Minecraft.Client/GuiComponent.cpp b/Minecraft.Client/GuiComponent.cpp new file mode 100644 index 0000000..92abf36 --- /dev/null +++ b/Minecraft.Client/GuiComponent.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "GuiComponent.h" +#include "Tesselator.h" + +void GuiComponent::hLine(int x0, int x1, int y, int col) +{ + if (x1 < x0) + { + int tmp = x0; + x0 = x1; + x1 = tmp; + } + fill(x0, y, x1 + 1, y + 1, col); +} + +void GuiComponent::vLine(int x, int y0, int y1, int col) +{ + if (y1 < y0) + { + int tmp = y0; + y0 = y1; + y1 = tmp; + } + fill(x, y0 + 1, x + 1, y1, col); +} + +void GuiComponent::fill(int x0, int y0, int x1, int y1, int col) +{ + if (x0 < x1) + { + int tmp = x0; + x0 = x1; + x1 = tmp; + } + if (y0 < y1) + { + int tmp = y0; + y0 = y1; + y1 = tmp; + } + float a = ((col >> 24) & 0xff) / 255.0f; + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + Tesselator *t = Tesselator::getInstance(); + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(r, g, b, a); + t->begin(); + t->vertex((float)(x0), (float)( y1), (float)( 0)); + t->vertex((float)(x1), (float)( y1), (float)( 0)); + t->vertex((float)(x1), (float)( y0), (float)( 0)); + t->vertex((float)(x0), (float)( y0), (float)( 0)); + t->end(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +} + +void GuiComponent::fillGradient(int x0, int y0, int x1, int y1, int col1, int col2) +{ + float a1 = ((col1 >> 24) & 0xff) / 255.0f; + float r1 = ((col1 >> 16) & 0xff) / 255.0f; + float g1 = ((col1 >> 8) & 0xff) / 255.0f; + float b1 = ((col1) & 0xff) / 255.0f; + + float a2 = ((col2 >> 24) & 0xff) / 255.0f; + float r2 = ((col2 >> 16) & 0xff) / 255.0f; + float g2 = ((col2 >> 8) & 0xff) / 255.0f; + float b2 = ((col2) & 0xff) / 255.0f; + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glShadeModel(GL_SMOOTH); + + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->color(r1, g1, b1, a1); + t->vertex((float)(x1), (float)( y0), blitOffset); + t->vertex((float)(x0), (float)( y0), blitOffset); + t->color(r2, g2, b2, a2); + t->vertex((float)(x0), (float)( y1), blitOffset); + t->vertex((float)(x1), (float)( y1), blitOffset); + t->end(); + + glShadeModel(GL_FLAT); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_TEXTURE_2D); +} + +GuiComponent::GuiComponent() +{ + blitOffset = 0; +} + +void GuiComponent::drawCenteredString(Font *font, const wstring& str, int x, int y, int color) +{ + font->drawShadow(str, x - (font->width(str)) / 2, y, color); +} + +void GuiComponent::drawString(Font *font, const wstring& str, int x, int y, int color) +{ + font->drawShadow(str, x, y, color); +} + +void GuiComponent::blit(int x, int y, int sx, int sy, int w, int h) +{ + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + + // This is a bit of a mystery. In general this ought to be 0.5 to match the centre of texels & pixels in the DX9 version of things. However, when scaling the GUI by a factor of 1.5, I'm + // really not sure how exactly point sampled rasterisation works, but when shifting by 0.5 we get a discontinuity down the diagonal of quads. Setting this shift to 0.75 in all cases seems to work fine. + const float extraShift = 0.75f; + + // 4J - subtracting extraShift (actual screen pixels, so need to compensate for physical & game width) from each x & y coordinate to compensate for centre of pixels in directx vs openGL + float dx = ( extraShift * (float)Minecraft::GetInstance()->width ) / (float)Minecraft::GetInstance()->width_phys; + // 4J - Also factor in the scaling from gui coordinate space to the screen. This varies based on user-selected gui scale, and whether we are in a viewport mode or not + dx /= Gui::currentGuiScaleFactor; + float dy = extraShift / Gui::currentGuiScaleFactor; + // Ensure that the x/y, width and height are actually pixel aligned at our current scale factor - in particular, for split screen mode with the default (3X) + // scale, we have an overall scale factor of 3 * 0.5 = 1.5, and so any odd pixels won't align + float fx = (floorf((float)x * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor; + float fy = (floorf((float)y * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor; + float fw = (floorf((float)w * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor; + float fh = (floorf((float)h * Gui::currentGuiScaleFactor)) / Gui::currentGuiScaleFactor; + + t->vertexUV(fx + 0 - dx, fy + fh - dy, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + h) * vs)); + t->vertexUV(fx + fw - dx, fy + fh - dy, (float)( blitOffset), (float)( (sx + w) * us), (float)( (sy + h) * vs)); + t->vertexUV(fx + fw - dx, fy + 0 - dy, (float)( blitOffset), (float)( (sx + w) * us), (float)( (sy + 0) * vs)); + t->vertexUV(fx + 0 - dx, fy + 0 - dy, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + 0) * vs)); + t->end(); +} \ No newline at end of file diff --git a/Minecraft.Client/GuiComponent.h b/Minecraft.Client/GuiComponent.h new file mode 100644 index 0000000..7073db4 --- /dev/null +++ b/Minecraft.Client/GuiComponent.h @@ -0,0 +1,19 @@ +#pragma once +class Font; +using namespace std; + +class GuiComponent +{ +protected: + float blitOffset; +protected: + void hLine(int x0, int x1, int y, int col); + void vLine(int x, int y0, int y1, int col); + void fill(int x0, int y0, int x1, int y1, int col); + void fillGradient(int x0, int y0, int x1, int y1, int col1, int col2); +public: + GuiComponent(); // 4J added + void drawCenteredString(Font *font, const wstring& str, int x, int y, int color); + void drawString(Font *font, const wstring& str, int x, int y, int color); + void blit(int x, int y, int sx, int sy, int w, int h); +}; diff --git a/Minecraft.Client/GuiMessage.cpp b/Minecraft.Client/GuiMessage.cpp new file mode 100644 index 0000000..6a7fa7e --- /dev/null +++ b/Minecraft.Client/GuiMessage.cpp @@ -0,0 +1,8 @@ +#include "stdafx.h" +#include "GuiMessage.h" + +GuiMessage::GuiMessage(const wstring& string) +{ + this->string = string; + ticks = 0; +} \ No newline at end of file diff --git a/Minecraft.Client/GuiMessage.h b/Minecraft.Client/GuiMessage.h new file mode 100644 index 0000000..dac9a9e --- /dev/null +++ b/Minecraft.Client/GuiMessage.h @@ -0,0 +1,10 @@ +#pragma once +using namespace std; + +class GuiMessage +{ +public: + wstring string; + int ticks; + GuiMessage(const wstring& string); +}; \ No newline at end of file diff --git a/Minecraft.Client/GuiParticle.cpp b/Minecraft.Client/GuiParticle.cpp new file mode 100644 index 0000000..25859c7 --- /dev/null +++ b/Minecraft.Client/GuiParticle.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "GuiParticle.h" +#include "..\Minecraft.World\Random.h" + +Random *GuiParticle::random = new Random(); + +GuiParticle::GuiParticle(double x, double y, double xa, double ya) +{ + // 4J - added initialisation block + removed = false; + life = 0; + a = 1; + oR = oG = oB = oA = 0; + + this->xo = this->x = x; + this->yo = this->y = y; + this->xa = xa; + this->ya = ya; + + int col = Color::HSBtoRGB(random->nextFloat(), 0.5f, 1); + r = ((col >> 16) & 0xff) / 255.0; + g = ((col >> 8) & 0xff) / 255.0; + b = ((col) & 0xff) / 255.0; + + friction = 1.0 / (random->nextDouble() * 0.05 + 1.01); + + lifeTime = (int) (10.0 / (random->nextDouble() * 2 + 0.1)); +} + +void GuiParticle::tick(GuiParticles *guiParticles) +{ + x += xa; + y += ya; + + xa *= friction; + ya *= friction; + + ya += 0.1; + if (++life > lifeTime) remove(); + a = 2 - (life / (double) lifeTime) * 2; + if (a > 1) a = 1; + a = a * a; + a *= 0.5; +} + +void GuiParticle::preTick() +{ + oR = r; + oG = g; + oB = b; + oA = a; + + xo = x; + yo = y; +} + +void GuiParticle::remove() +{ + removed = true; +} \ No newline at end of file diff --git a/Minecraft.Client/GuiParticle.h b/Minecraft.Client/GuiParticle.h new file mode 100644 index 0000000..27b14a6 --- /dev/null +++ b/Minecraft.Client/GuiParticle.h @@ -0,0 +1,25 @@ +#pragma once +class GuiParticles; +class Random; + +class GuiParticle +{ +private: + static Random *random; + +public: + double x, y; + double xo, yo; + double xa, ya; + double friction; + bool removed; + int life, lifeTime; + + double r, g, b, a; + double oR, oG, oB, oA; // MGH - remaned these, as PS3 complained about "or" var name + + GuiParticle(double x, double y, double xa, double ya); + void tick(GuiParticles *guiParticles); + void preTick(); + void remove(); +}; \ No newline at end of file diff --git a/Minecraft.Client/GuiParticles.cpp b/Minecraft.Client/GuiParticles.cpp new file mode 100644 index 0000000..6716de7 --- /dev/null +++ b/Minecraft.Client/GuiParticles.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "GuiParticles.h" +#include "GuiParticle.h" +#include "Textures.h" + +GuiParticles::GuiParticles(Minecraft *mc) +{ + this->mc = mc; +} + +void GuiParticles::tick() +{ + for (unsigned int i = 0; i < particles.size(); i++) + { + GuiParticle *gp = particles[i]; + + gp->preTick(); + gp->tick(this); + + if (gp->removed) + { + particles.erase(particles.begin()+i); + i--; + } + } +} + +void GuiParticles::add(GuiParticle *guiParticle) +{ + particles.push_back(guiParticle); + guiParticle->preTick(); +} + +void GuiParticles::render(float a) +{ + // 4J Stu - Never used +#if 0 + mc->textures->bindTexture(L"/gui/particles.png"); + + AUTO_VAR(itEnd, particles.end()); + for (AUTO_VAR(it, particles.begin()); it != itEnd; it++) + { + GuiParticle *gp = *it; //particles[i]; + int xx = (int) (gp->xo + (gp->x - gp->xo) * a - 4); + int yy = (int) (gp->yo + (gp->y - gp->yo) * a - 4); + + float alpha = ((float) (gp->oA + (gp->a - gp->oA) * a)); + float r = ((float) (gp->oR + (gp->r - gp->oR) * a)); + float g = ((float) (gp->oG + (gp->g - gp->oG) * a)); + float b = ((float) (gp->oB + (gp->b - gp->oB) * a)); + + glColor4f(r, g, b, alpha); + blit(xx, yy, 8 * 5, 0, 8, 8); + } +#endif +} diff --git a/Minecraft.Client/GuiParticles.h b/Minecraft.Client/GuiParticles.h new file mode 100644 index 0000000..0fe3181 --- /dev/null +++ b/Minecraft.Client/GuiParticles.h @@ -0,0 +1,19 @@ +#pragma once +#include "GuiComponent.h" + +class GuiParticle; +class Minecraft; +using namespace std; + +class GuiParticles : public GuiComponent +{ +private: + vector particles; + Minecraft *mc; + +public: + GuiParticles(Minecraft *mc); + void tick(); + void add(GuiParticle *guiParticle); + void render(float a); +}; diff --git a/Minecraft.Client/HeartParticle.cpp b/Minecraft.Client/HeartParticle.cpp new file mode 100644 index 0000000..24576c6 --- /dev/null +++ b/Minecraft.Client/HeartParticle.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "HeartParticle.h" + +// 4J - added +void HeartParticle::init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) +{ + xd *= 0.01f; + yd *= 0.01f; + zd *= 0.01f; + yd += 0.1; + + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = 16; + noPhysics = false; + + + setMiscTex(16 * 5); +} + +HeartParticle::HeartParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level, x, y, z, xa, ya, za, 2); +} + +HeartParticle::HeartParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level,x,y,z,xa,ya,za,scale); +} + +void HeartParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void HeartParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + if (y == yo) + { + xd *= 1.1; + zd *= 1.1; + } + xd *= 0.86f; + yd *= 0.86f; + zd *= 0.86f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } +} \ No newline at end of file diff --git a/Minecraft.Client/HeartParticle.h b/Minecraft.Client/HeartParticle.h new file mode 100644 index 0000000..f249325 --- /dev/null +++ b/Minecraft.Client/HeartParticle.h @@ -0,0 +1,19 @@ +#pragma once +#include "Particle.h" + +class HeartParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_HEARTPARTICLE; } +private: + void init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); // 4J added +public: + HeartParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + + float oSize; + + HeartParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); + + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); +}; diff --git a/Minecraft.Client/HttpTexture.cpp b/Minecraft.Client/HttpTexture.cpp new file mode 100644 index 0000000..0f452e8 --- /dev/null +++ b/Minecraft.Client/HttpTexture.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "HttpTexture.h" + +HttpTexture::HttpTexture(const wstring& _url, HttpTextureProcessor *processor) +{ + // 4J - added + count = 1; + id = -1; + isLoaded = false; + + // 4J - TODO - actually implement +} \ No newline at end of file diff --git a/Minecraft.Client/HttpTexture.h b/Minecraft.Client/HttpTexture.h new file mode 100644 index 0000000..469ef6c --- /dev/null +++ b/Minecraft.Client/HttpTexture.h @@ -0,0 +1,14 @@ +#pragma once +class BufferedImage; +class HttpTextureProcessor; +using namespace std; + +class HttpTexture { +public: + BufferedImage *loadedImage; + int count; + int id; + bool isLoaded; + + HttpTexture(const wstring& _url, HttpTextureProcessor *processor); +}; \ No newline at end of file diff --git a/Minecraft.Client/HttpTextureProcessor.h b/Minecraft.Client/HttpTextureProcessor.h new file mode 100644 index 0000000..a585a03 --- /dev/null +++ b/Minecraft.Client/HttpTextureProcessor.h @@ -0,0 +1,8 @@ +#pragma once +class BufferedImage; + +class HttpTextureProcessor +{ +public: + virtual BufferedImage *process(BufferedImage *read) = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/HugeExplosionParticle.cpp b/Minecraft.Client/HugeExplosionParticle.cpp new file mode 100644 index 0000000..273eed2 --- /dev/null +++ b/Minecraft.Client/HugeExplosionParticle.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "HugeExplosionParticle.h" +#include "..\Minecraft.World\Random.h" +#include "Textures.h" +#include "Tesselator.h" +#include "Lighting.h" + +HugeExplosionParticle::HugeExplosionParticle(Textures *textures, Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level,x,y,z,0,0,0) +{ + life = 0; + + this->textures = textures; + lifeTime = 6 + random->nextInt(4); + + // rCol = gCol = bCol = random->nextFloat() * 0.6f + 0.4f; + + unsigned int clr = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_HugeExplosion ); //0x999999 + double r = ( (clr>>16)&0xFF )/255.0f, g = ( (clr>>8)&0xFF )/255.0, b = ( clr&0xFF )/255.0; + + double br = random->nextFloat() * 0.6 + 0.4; + rCol = r * br; + gCol = g * br; + bCol = b * br; + + size = 1 - (float) xa * 0.5f; +} + +void HugeExplosionParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + int tex = (int) ((life + a) * 15 / lifeTime); + if (tex > 15) return; + textures->bindTexture(TN_MISC_EXPLOSION); // 4J was "/misc/explosion.png" + + float u0 = (tex % 4) / 4.0f; + float u1 = u0 + 0.999f / 4.0f; + float v0 = (tex / 4) / 4.0f; + float v1 = v0 + 0.999f / 4.0f; + + float r = 2.0f * size; + + float x = (float) (xo + (this->x - xo) * a - xOff); + float y = (float) (yo + (this->y - yo) * a - yOff); + float z = (float) (zo + (this->z - zo) * a - zOff); + + // 4J - don't render explosion particles that are less than 3 metres away, to try and avoid large particles that are causing us problems with photosensitivity testing + float distSq = (x*x + y*y + z*z); + if( distSq < ( 3.0f * 3.0f )) return; + + glColor4f(1, 1, 1, 1); + glDisable(GL_LIGHTING); + Lighting::turnOff(); + t->begin(); + t->color(rCol, gCol, bCol, 1.0f); + t->normal(0, 1, 0); + t->tex2(0x00f0); + t->vertexUV(x - xa * r - xa2 * r, y - ya * r, z - za * r - za2 * r, u1, v1); + t->vertexUV(x - xa * r + xa2 * r, y + ya * r, z - za * r + za2 * r, u1, v0); + t->vertexUV(x + xa * r + xa2 * r, y + ya * r, z + za * r + za2 * r, u0, v0); + t->vertexUV(x + xa * r - xa2 * r, y - ya * r, z + za * r - za2 * r, u0, v1); + t->end(); + glPolygonOffset(0, 0.0f); + glEnable(GL_LIGHTING); +} + +int HugeExplosionParticle::getLightColor(float a) +{ + return 0xf0f0; +} + +void HugeExplosionParticle::tick() +{ + xo = x; + yo = y; + zo = z; + life++; + if (life == lifeTime) remove(); +} + +int HugeExplosionParticle::getParticleTexture() +{ + return ParticleEngine::ENTITY_PARTICLE_TEXTURE; +} \ No newline at end of file diff --git a/Minecraft.Client/HugeExplosionParticle.h b/Minecraft.Client/HugeExplosionParticle.h new file mode 100644 index 0000000..0f02d12 --- /dev/null +++ b/Minecraft.Client/HugeExplosionParticle.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Particle.h" + +class HugeExplosionParticle : public Particle +{ +private: + int life; + int lifeTime; + Textures *textures; + float size; + +public: + virtual eINSTANCEOF GetType() { return eType_HUGEEXPLOSIONPARTICLE; } + HugeExplosionParticle(Textures *textures, Level *level, double x, double y, double z, double xa, double ya, double za); + void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + int getLightColor(float a); + void tick(); + int getParticleTexture(); +}; \ No newline at end of file diff --git a/Minecraft.Client/HugeExplosionSeedParticle.cpp b/Minecraft.Client/HugeExplosionSeedParticle.cpp new file mode 100644 index 0000000..7514cc4 --- /dev/null +++ b/Minecraft.Client/HugeExplosionSeedParticle.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "HugeExplosionSeedParticle.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" + +HugeExplosionSeedParticle::HugeExplosionSeedParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level,x,y,z,0,0,0) +{ + life = 0; + + lifeTime = 8; +} + +void HugeExplosionSeedParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ +} + +void HugeExplosionSeedParticle::tick() +{ + // Horrible hack to communicate with the level renderer, which is just attached as a listener to this level. This let's the particle + // rendering know to use this level (rather than try to work it out from the current player), and to not bother distance clipping particles + // which would again be based on the current player. + Minecraft::GetInstance()->animateTickLevel = level; + for (int i = 0; i < 6; i++) { + double xx = x + (random->nextDouble() - random->nextDouble()) * 4; + double yy = y + (random->nextDouble() - random->nextDouble()) * 4; + double zz = z + (random->nextDouble() - random->nextDouble()) * 4; + level->addParticle(eParticleType_largeexplode, xx, yy, zz, life / (float) lifeTime, 0, 0); + } + Minecraft::GetInstance()->animateTickLevel = NULL; + life++; + if (life == lifeTime) remove(); +} + +int HugeExplosionSeedParticle::getParticleTexture() +{ + return ParticleEngine::TERRAIN_TEXTURE; +} \ No newline at end of file diff --git a/Minecraft.Client/HugeExplosionSeedParticle.h b/Minecraft.Client/HugeExplosionSeedParticle.h new file mode 100644 index 0000000..445c8a3 --- /dev/null +++ b/Minecraft.Client/HugeExplosionSeedParticle.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Particle.h" + +class HugeExplosionSeedParticle : public Particle +{ +private: + int life; + int lifeTime; + +public: + virtual eINSTANCEOF GetType() { return eType_HUGEEXPLOSIONSEEDPARTICLE; } + HugeExplosionSeedParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + void tick(); + int getParticleTexture(); +}; \ No newline at end of file diff --git a/Minecraft.Client/HumanoidMobRenderer.cpp b/Minecraft.Client/HumanoidMobRenderer.cpp new file mode 100644 index 0000000..389116e --- /dev/null +++ b/Minecraft.Client/HumanoidMobRenderer.cpp @@ -0,0 +1,150 @@ +#include "stdafx.h" +#include "HumanoidMobRenderer.h" +#include "SkullTileRenderer.h" +#include "HumanoidModel.h" +#include "ModelPart.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\net.minecraft.h" + +void HumanoidMobRenderer::_init(HumanoidModel *humanoidModel, float scale) +{ + this->humanoidModel = humanoidModel; + this->_scale = scale; + armorParts1 = NULL; + armorParts2 = NULL; +} + +HumanoidMobRenderer::HumanoidMobRenderer(HumanoidModel *humanoidModel, float shadow) : MobRenderer(humanoidModel, shadow) +{ + _init(humanoidModel, 1.0f); +} + +HumanoidMobRenderer::HumanoidMobRenderer(HumanoidModel *humanoidModel, float shadow, float scale) : MobRenderer(humanoidModel, shadow) +{ + _init(humanoidModel, scale); + + createArmorParts(); +} + +void HumanoidMobRenderer::createArmorParts() +{ + armorParts1 = new HumanoidModel(1.0f); + armorParts2 = new HumanoidModel(0.5f); +} + +void HumanoidMobRenderer::additionalRendering(shared_ptr mob, float a) +{ + float brightness = SharedConstants::TEXTURE_LIGHTING ? 1 : mob->getBrightness(a); + glColor3f(brightness, brightness, brightness); + shared_ptr item = mob->getCarriedItem(); + shared_ptr headGear = mob->getArmor(3); + + if (headGear != NULL) + { + // don't render the pumpkin of skulls for the skins with that disabled + // 4J-PB - need to disable rendering armour/skulls/pumpkins for some special skins (Daleks) + + if((mob->getAnimOverrideBitmask()&(1<head->translateTo(1 / 16.0f); + + if (headGear->getItem()->id < 256) + { + if (Tile::tiles[headGear->id] != NULL && TileRenderer::canRender(Tile::tiles[headGear->id]->getRenderShape())) + { + float s = 10 / 16.0f; + glTranslatef(-0 / 16.0f, -4 / 16.0f, 0 / 16.0f); + glRotatef(90, 0, 1, 0); + glScalef(s, -s, -s); + } + + this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, headGear, 0); + } + else if (headGear->getItem()->id == Item::skull_Id) + { + float s = 17 / 16.0f; + glScalef(s, -s, -s); + + wstring extra = L""; + if (headGear->hasTag() && headGear->getTag()->contains(L"SkullOwner")) + { + extra = headGear->getTag()->getString(L"SkullOwner"); + } + SkullTileRenderer::instance->renderSkull(-0.5f, 0, -0.5f, Facing::UP, 180, headGear->getAuxValue(), extra); + } + + glPopMatrix(); + } + } + + if (item != NULL) + { + glPushMatrix(); + + if (model->young) + { + float s = 0.5f; + glTranslatef(0 / 16.0f, 10 / 16.0f, 0 / 16.0f); + glRotatef(-20, -1, 0, 0); + glScalef(s, s, s); + } + + humanoidModel->arm0->translateTo(1 / 16.0f); + glTranslatef(-1 / 16.0f, 7 / 16.0f, 1 / 16.0f); + + if (item->id < 256 && TileRenderer::canRender(Tile::tiles[item->id]->getRenderShape())) + { + float s = 8 / 16.0f; + glTranslatef(-0 / 16.0f, 3 / 16.0f, -5 / 16.0f); + s *= 0.75f; + glRotatef(20, 1, 0, 0); + glRotatef(45, 0, 1, 0); + glScalef(-s, -s, s); + } + else if (item->id == Item::bow_Id) + { + float s = 10 / 16.0f; + glTranslatef(0/16.0f, 2 / 16.0f, 5 / 16.0f); + glRotatef(-20, 0, 1, 0); + glScalef(s, -s, s); + glRotatef(-100, 1, 0, 0); + glRotatef(45, 0, 1, 0); + } + else if (Item::items[item->id]->isHandEquipped()) + { + float s = 10 / 16.0f; + glTranslatef(0, 3 / 16.0f, 0); + glScalef(s, -s, s); + glRotatef(-100, 1, 0, 0); + glRotatef(45, 0, 1, 0); + } + else + { + float s = 6 / 16.0f; + glTranslatef(+4 / 16.0f, +3 / 16.0f, -3 / 16.0f); + glScalef(s, s, s); + glRotatef(60, 0, 0, 1); + glRotatef(-90, 1, 0, 0); + glRotatef(20, 0, 0, 1); + } + + this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 0); + if (item->getItem()->hasMultipleSpriteLayers()) + { + this->entityRenderDispatcher->itemInHandRenderer->renderItem(mob, item, 1); + } + + glPopMatrix(); + } + +} + +void HumanoidMobRenderer::scale(shared_ptr mob, float a) +{ + glScalef(_scale, _scale, _scale); +} \ No newline at end of file diff --git a/Minecraft.Client/HumanoidMobRenderer.h b/Minecraft.Client/HumanoidMobRenderer.h new file mode 100644 index 0000000..6c718fb --- /dev/null +++ b/Minecraft.Client/HumanoidMobRenderer.h @@ -0,0 +1,22 @@ +#pragma once +#include "MobRenderer.h" +class HumanoidModel; +class Giant; + +class HumanoidMobRenderer : public MobRenderer +{ +protected: + HumanoidModel *humanoidModel; + float _scale; + HumanoidModel *armorParts1; + HumanoidModel *armorParts2; + + void _init(HumanoidModel *humanoidModel, float scale); +public: + HumanoidMobRenderer(HumanoidModel *humanoidModel, float shadow); + HumanoidMobRenderer(HumanoidModel *humanoidModel, float shadow, float scale); +protected: + virtual void createArmorParts(); + virtual void additionalRendering(shared_ptr mob, float a); + void scale(shared_ptr mob, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/HumanoidModel.cpp b/Minecraft.Client/HumanoidModel.cpp new file mode 100644 index 0000000..444d9c9 --- /dev/null +++ b/Minecraft.Client/HumanoidModel.cpp @@ -0,0 +1,463 @@ +#include "stdafx.h" +#include "HumanoidModel.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\Entity.h" +#include "ModelPart.h" + +// 4J added + +ModelPart * HumanoidModel::AddOrRetrievePart(SKIN_BOX *pBox) +{ + ModelPart *pAttachTo=NULL; + + switch(pBox->ePart) + { + case eBodyPart_Head: + pAttachTo=head; + break; + case eBodyPart_Body: + pAttachTo=body; + break; + case eBodyPart_Arm0: + pAttachTo=arm0; + break; + case eBodyPart_Arm1: + pAttachTo=arm1; + break; + case eBodyPart_Leg0: + pAttachTo=leg0; + break; + case eBodyPart_Leg1: + pAttachTo=leg1; + break; + } + + // first check this box doesn't already exist + ModelPart *pNewBox = pAttachTo->retrieveChild(pBox); + + if(pNewBox) + { + if((pNewBox->getfU()!=(int)pBox->fU) || (pNewBox->getfV()!=(int)pBox->fV)) + { + app.DebugPrintf("HumanoidModel::AddOrRetrievePart - Box geometry was found, but with different uvs\n"); + pNewBox=NULL; + } + } + if(pNewBox==NULL) + { + //app.DebugPrintf("HumanoidModel::AddOrRetrievePart - Adding box to model part\n"); + + pNewBox = new ModelPart(this, (int)pBox->fU, (int)pBox->fV); + pNewBox->visible=false; + pNewBox->addHumanoidBox(pBox->fX, pBox->fY, pBox->fZ, pBox->fW, pBox->fH, pBox->fD, 0); + // 4J-PB - don't compile here, since the lighting isn't set up. It'll be compiled on first use. + //pNewBox->compile(1.0f/16.0f); + pAttachTo->addChild(pNewBox); + } + + return pNewBox; +} + +void HumanoidModel::_init(float g, float yOffset, int texWidth, int texHeight) +{ + this->texWidth = texWidth; + this->texHeight = texHeight; + + m_fYOffset=yOffset; + cloak = new ModelPart(this, 0, 0); + cloak->addHumanoidBox(-5, -0, -1, 10, 16, 1, g); // Cloak + + ear = new ModelPart(this, 24, 0); + ear->addHumanoidBox(-3, -6, -1, 6, 6, 1, g); // Ear + + head = new ModelPart(this, 0, 0); + head->addHumanoidBox(-4, -8, -4, 8, 8, 8, g); // Head + head->setPos(0, 0 + yOffset, 0); + + hair = new ModelPart(this, 32, 0); + hair->addHumanoidBox(-4, -8, -4, 8, 8, 8, g + 0.5f); // Head + hair->setPos(0, 0 + yOffset, 0); + + body = new ModelPart(this, 16, 16); + body->addHumanoidBox(-4, 0, -2, 8, 12, 4, g); // Body + body->setPos(0, 0 + yOffset, 0); + + arm0 = new ModelPart(this, 24 + 16, 16); + arm0->addHumanoidBox(-3, -2, -2, 4, 12, 4, g); // Arm0 + arm0->setPos(-5, 2 + yOffset, 0); + + arm1 = new ModelPart(this, 24 + 16, 16); + arm1->bMirror = true; + arm1->addHumanoidBox(-1, -2, -2, 4, 12, 4, g); // Arm1 + arm1->setPos(5, 2 + yOffset, 0); + + leg0 = new ModelPart(this, 0, 16); + leg0->addHumanoidBox(-2, 0, -2, 4, 12, 4, g); // Leg0 + leg0->setPos(-1.9, 12 + yOffset, 0); + + leg1 = new ModelPart(this, 0, 16); + leg1->bMirror = true; + leg1->addHumanoidBox(-2, 0, -2, 4, 12, 4, g); // Leg1 + leg1->setPos(1.9, 12 + yOffset, 0); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + // 4J Stu - Not just performance, but alpha+depth tests don't work right unless we compile here + cloak->compile(1.0f/16.0f); + ear->compile(1.0f/16.0f); + head->compile(1.0f/16.0f); + body->compile(1.0f/16.0f); + arm0->compile(1.0f/16.0f); + arm1->compile(1.0f/16.0f); + leg0->compile(1.0f/16.0f); + leg1->compile(1.0f/16.0f); + hair->compile(1.0f/16.0f); + + holdingLeftHand=0; + holdingRightHand=0; + sneaking=false; + idle=false; + bowAndArrow=false; + + // 4J added + eating = false; + eating_t = 0.0f; + eating_swing = 0.0f; + m_uiAnimOverrideBitmask = 0L; +} + +HumanoidModel::HumanoidModel() : Model() +{ + _init(0, 0, 64, 32); +} + +HumanoidModel::HumanoidModel(float g) : Model() +{ + _init(g, 0, 64, 32); +} + +HumanoidModel::HumanoidModel(float g, float yOffset, int texWidth, int texHeight) : Model() +{ + _init(g,yOffset,texWidth,texHeight); +} + +void HumanoidModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + if(entity!=NULL) + { + m_uiAnimOverrideBitmask=entity->getAnimOverrideBitmask(); + } + + setupAnim(time, r, bob, yRot, xRot, scale, m_uiAnimOverrideBitmask); + + if (young) + { + float ss = 2.0f; + glPushMatrix(); + glScalef(1.5f / ss, 1.5f / ss, 1.5f / ss); + glTranslatef(0, 16 * scale, 0); + head->render(scale, usecompiled); + glPopMatrix(); + glPushMatrix(); + glScalef(1 / ss, 1 / ss, 1 / ss); + glTranslatef(0, 24 * scale, 0); + body->render(scale, usecompiled); + arm0->render(scale, usecompiled); + arm1->render(scale, usecompiled); + leg0->render(scale, usecompiled); + leg1->render(scale, usecompiled); + hair->render(scale, usecompiled); + glPopMatrix(); + } + else + { + head->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + body->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + arm0->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + arm1->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + leg0->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + leg1->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + hair->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + } +} + +void HumanoidModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) +{ + //bool bIsAttacking = (attackTime > -9990.0f); + + { + head->yRot = yRot / (float) (180.0f / PI); + head->xRot = xRot / (float) (180.0f / PI); + hair->yRot = head->yRot; + hair->xRot = head->xRot; + + // Does the skin have an override for anim? + + if(uiBitmaskOverrideAnim&(1<xRot=0.0f; + arm1->xRot=0.0f; + arm0->zRot = 0.0f; + arm1->zRot = 0.0f; + + } + else if(uiBitmaskOverrideAnim&(1<xRot=-HALF_PI; + arm1->xRot=-HALF_PI; + arm0->zRot = 0.0f; + arm1->zRot = 0.0f; + } + else if(uiBitmaskOverrideAnim&(1<xRot = (Mth::cos(time * 0.6662f + PI) * 2.0f) * r * 0.5f; + arm1->xRot = (Mth::cos(time * 0.6662f + PI) * 2.0f) * r * 0.5f; + arm0->zRot = 0.0f; + arm1->zRot = 0.0f; + } + // 4J-PB - Weeping Angel - does't look good holding something in the arm that's up + else if((uiBitmaskOverrideAnim&(1<xRot = -PI; + arm0->zRot = -0.3f; + arm1->xRot = ( Mth::cos(time * 0.6662f) * 2.0f) * r * 0.5f; + arm1->zRot = 0.0f; + } + else + { + arm0->xRot = (Mth::cos(time * 0.6662f + PI) * 2.0f) * r * 0.5f; + arm1->xRot = ( Mth::cos(time * 0.6662f) * 2.0f) * r * 0.5f; + arm0->zRot = 0.0f; + arm1->zRot = 0.0f; + } + // arm0.zRot = ((float) (util.Mth.cos(time * 0.2312f) + 1) * 1) * r; + + + // arm1.zRot = ((float) (util.Mth.cos(time * 0.2812f) - 1) * 1) * r; + + + leg0->yRot = 0.0f; + leg1->yRot = 0.0f; + + if (riding) + { + arm0->xRot += -HALF_PI * 0.4f; + arm1->xRot += -HALF_PI * 0.4f; + leg0->xRot = -HALF_PI * 0.8f; + leg1->xRot = -HALF_PI * 0.8f; + leg0->yRot = HALF_PI * 0.2f; + leg1->yRot = -HALF_PI * 0.2f; + } + else if(idle && !sneaking ) + { + leg0->xRot = -HALF_PI; + leg1->xRot = -HALF_PI; + leg0->yRot = HALF_PI * 0.2f; + leg1->yRot = -HALF_PI * 0.2f; + } + else if(uiBitmaskOverrideAnim&(1<xRot=0.0f; + leg0->zRot=0.0f; + leg1->xRot=0.0f; + leg1->zRot=0.0f; + leg0->yRot = 0.0f; + leg1->yRot = 0.0f; + } + else if(uiBitmaskOverrideAnim&(1<xRot = ( Mth::cos(time * 0.6662f) * 1.4f) * r; + leg1->xRot = ( Mth::cos(time * 0.6662f) * 1.4f) * r; + } + else + { + leg0->xRot = ( Mth::cos(time * 0.6662f) * 1.4f) * r; + leg1->xRot = ( Mth::cos(time * 0.6662f + PI) * 1.4f) * r; + } + + + if (holdingLeftHand != 0) + { + arm1->xRot = arm1->xRot * 0.5f - HALF_PI * 0.2f * holdingLeftHand; + } + if (holdingRightHand != 0) + { + arm0->xRot = arm0->xRot * 0.5f - HALF_PI * 0.2f * holdingRightHand; + } + + arm0->yRot = 0.0f; + arm1->yRot = 0.0f; + if (attackTime > -9990.0f) + { + float swing = attackTime; + body->yRot = Mth::sin(sqrt(swing) * PI * 2.0f) * 0.2f; + arm0->z = Mth::sin(body->yRot) * 5.0f; + arm0->x = -Mth::cos(body->yRot) * 5.0f; + arm1->z = -Mth::sin(body->yRot) * 5.0f; + arm1->x = Mth::cos(body->yRot) * 5.0f; + arm0->yRot += body->yRot; + arm1->yRot += body->yRot; + arm1->xRot += body->yRot; + + swing = 1.0f - attackTime; + swing *= swing; + swing *= swing; + swing = 1.0f - swing; + float aa = Mth::sin(swing * PI); + float bb = Mth::sin(attackTime * PI) * -(head->xRot - 0.7f) * 0.75f; + arm0->xRot -= aa * 1.2f + bb; // 4J - changed 1.2 -> 1.2f + arm0->yRot += body->yRot * 2.0f; + + if((uiBitmaskOverrideAnim&(1<zRot -= Mth::sin(attackTime * PI) * -0.4f; + } + else + { + arm0->zRot = Mth::sin(attackTime * PI) * -0.4f; + } + } + + // 4J added + if( eating ) + { + // These factors are largely lifted from ItemInHandRenderer to try and keep the 3rd person eating animation as similar as possible + float is = 1 - eating_swing; + is = is * is * is; + is = is * is * is; + is = is * is * is; + float iss = 1 - is; + arm0->xRot = - Mth::abs(Mth::cos(eating_t / 4.0f * PI) * 0.1f) * (eating_swing > 0.2 ? 1.0f : 0.0f) * 2.0f; // This factor is the chomping bit (conditional factor is so that he doesn't eat whilst the food is being pulled away at the end) + arm0->yRot -= iss * 0.5f; // This factor and the following to the general arm movement through the life of the swing + arm0->xRot -= iss * 1.2f; + + } + + if (sneaking) + { + body->xRot = 0.5f; + leg0->xRot -= 0.0f; + leg1->xRot -= 0.0f; + arm0->xRot += 0.4f; + arm1->xRot += 0.4f; + leg0->z = +4.0f; + leg1->z = +4.0f; + body->y = 0.0f; + arm0->y = 2.0f; + arm1->y = 2.0f; + leg0->y = +9.0f; + leg1->y = +9.0f; + head->y = +1.0f; + hair->y = +1.0f; + ear->y = +1.0f; + cloak->y = 0.0f; + } + else + { + body->xRot = 0.0f; + leg0->z = 0.1f; + leg1->z = 0.1f; + + if(!riding && idle) + { + leg0->y = 22.0f; + leg1->y = 22.0f; + body->y = 10.0f; + arm0->y = 12.0f; + arm1->y = 12.0f; + head->y = 10.0f; + hair->y = 10.0f; + ear->y = 11.0f; + cloak->y = 10.0f; + } + else + { + leg0->y = 12.0f; + leg1->y = 12.0f; + body->y = 0.0f; + arm0->y = 2.0f; + arm1->y = 2.0f; + head->y = 0.0f; + hair->y = 0.0f; + ear->y = 1.0f; + cloak->y = 0.0f; + } + } + + + arm0->zRot += ((Mth::cos(bob * 0.09f)) * 0.05f + 0.05f); + arm1->zRot -= ((Mth::cos(bob * 0.09f)) * 0.05f + 0.05f); + arm0->xRot += ((Mth::sin(bob * 0.067f)) * 0.05f); + arm1->xRot -= ((Mth::sin(bob * 0.067f)) * 0.05f); + + if (bowAndArrow) + { + float attack2 = 0.0f; + float attack = 0.0f; + + arm0->zRot = 0.0f; + arm1->zRot = 0.0f; + arm0->yRot = -(0.1f - attack2 * 0.6f) + head->yRot; + arm1->yRot = +(0.1f - attack2 * 0.6f) + head->yRot + 0.4f; + arm0->xRot = -HALF_PI + head->xRot; + arm1->xRot = -HALF_PI + head->xRot; + arm0->xRot -= attack2 * 1.2f - attack * 0.4f; + arm1->xRot -= attack2 * 1.2f - attack * 0.4f; + arm0->zRot += ((float) (Mth::cos(bob * 0.09f)) * 0.05f + 0.05f); + arm1->zRot -= ((float) (Mth::cos(bob * 0.09f)) * 0.05f + 0.05f); + arm0->xRot += ((float) (Mth::sin(bob * 0.067f)) * 0.05f); + arm1->xRot -= ((float) (Mth::sin(bob * 0.067f)) * 0.05f); + } + } +} + +void HumanoidModel::renderHair(float scale,bool usecompiled) +{ + hair->yRot = head->yRot; + hair->xRot = head->xRot; + hair->render(scale,usecompiled); +} + +void HumanoidModel::renderEars(float scale,bool usecompiled) +{ + ear->yRot = head->yRot; + ear->xRot = head->xRot; + ear->x=0; + ear->y=0; + ear->render(scale,usecompiled); +} + +void HumanoidModel::renderCloak(float scale,bool usecompiled) +{ + cloak->render(scale,usecompiled); +} + +void HumanoidModel::render(HumanoidModel *model, float scale, bool usecompiled) +{ + head->yRot = model->head->yRot; + head->y = model->head->y; + head->xRot = model->head->xRot; + hair->y = head->y; + hair->yRot = head->yRot; + hair->xRot = head->xRot; + + body->yRot = model->body->yRot; + + arm0->xRot = model->arm0->xRot; + arm0->yRot = model->arm0->yRot; + arm0->zRot = model->arm0->zRot; + + arm1->xRot = model->arm1->xRot; + arm1->yRot = model->arm1->yRot; + arm1->zRot = model->arm1->zRot; + + leg0->xRot = model->leg0->xRot; + leg1->xRot = model->leg1->xRot; + + head->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + body->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + arm0->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + arm1->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + leg0->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + leg1->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); + hair->render(scale, usecompiled,(m_uiAnimOverrideBitmask&(1<0); +} diff --git a/Minecraft.Client/HumanoidModel.h b/Minecraft.Client/HumanoidModel.h new file mode 100644 index 0000000..0ca10f4 --- /dev/null +++ b/Minecraft.Client/HumanoidModel.h @@ -0,0 +1,65 @@ +#pragma once +#include "Model.h" + +class HumanoidModel : public Model +{ +public: + ModelPart *head, *hair, *body, *arm0, *arm1, *leg0, *leg1, *ear, *cloak; + //ModelPart *hat; + + int holdingLeftHand; + int holdingRightHand; + bool idle; + bool sneaking; + bool bowAndArrow; + bool eating; // 4J added + float eating_t; // 4J added + float eating_swing; // 4J added + unsigned int m_uiAnimOverrideBitmask; // 4J added + float m_fYOffset; // 4J added + enum animbits + { + eAnim_ArmsDown =0, + eAnim_ArmsOutFront, + eAnim_NoLegAnim, + eAnim_HasIdle, + eAnim_ForceAnim, // Claptrap looks bad if the user turns off custom skin anim + // 4J-PB - DaveK wants Fish characters to move both legs in the same way + eAnim_SingleLegs, + eAnim_SingleArms, + eAnim_StatueOfLiberty, // Dr Who Weeping Angel + eAnim_DontRenderArmour, // Dr Who Daleks + eAnim_NoBobbing, // Dr Who Daleks + eAnim_DisableRenderHead, + eAnim_DisableRenderArm0, + eAnim_DisableRenderArm1, + eAnim_DisableRenderTorso, + eAnim_DisableRenderLeg0, + eAnim_DisableRenderLeg1, + eAnim_DisableRenderHair + + }; + + static const unsigned int m_staticBitmaskIgnorePlayerCustomAnimSetting= (1< entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); + void renderHair(float scale, bool usecompiled); + void renderEars(float scale, bool usecompiled); + void renderCloak(float scale, bool usecompiled); + void render(HumanoidModel *model, float scale, bool usecompiled); + +// Add new bits to models + ModelPart * AddOrRetrievePart(SKIN_BOX *pBox); +}; diff --git a/Minecraft.Client/InBedChatScreen.cpp b/Minecraft.Client/InBedChatScreen.cpp new file mode 100644 index 0000000..fca3d02 --- /dev/null +++ b/Minecraft.Client/InBedChatScreen.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "InBedChatScreen.h" +#include "Button.h" +#include "MultiplayerLocalPlayer.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\StringHelpers.h" + +void InBedChatScreen::init() +{ + Keyboard::enableRepeatEvents(true); + + Language *language = Language::getInstance(); + + buttons.push_back(new Button(WAKE_UP_BUTTON, width / 2 - 100, height - 40, language->getElement(L"multiplayer.stopSleeping"))); + +} + +void InBedChatScreen::removed() +{ + Keyboard::enableRepeatEvents(false); +} + +void InBedChatScreen::keyPressed(wchar_t ch, int eventKey) +{ + if (eventKey == Keyboard::KEY_ESCAPE) + { + sendWakeUp(); + } + else if (eventKey == Keyboard::KEY_RETURN) + { + wstring msg = trimString(message); + if (msg.length() > 0) + { + minecraft->player->chat(trimString(message)); + } + message = L""; + } + else + { + ChatScreen::keyPressed(ch, eventKey); + } +} + +void InBedChatScreen::render(int xm, int ym, float a) +{ + ChatScreen::render(xm, ym, a); +} + +void InBedChatScreen::buttonClicked(Button *button) +{ + if (button->id == WAKE_UP_BUTTON) + { + sendWakeUp(); + } + else + { + ChatScreen::buttonClicked(button); + } +} + +void InBedChatScreen::sendWakeUp() +{ + /* 4J - TODO + if (minecraft.player instanceof MultiplayerLocalPlayer) + { + ClientConnection connection = ((MultiplayerLocalPlayer) minecraft.player).connection; + connection.send(new PlayerCommandPacket(minecraft.player, PlayerCommandPacket.STOP_SLEEPING)); + } + */ +} \ No newline at end of file diff --git a/Minecraft.Client/InBedChatScreen.h b/Minecraft.Client/InBedChatScreen.h new file mode 100644 index 0000000..84bfd68 --- /dev/null +++ b/Minecraft.Client/InBedChatScreen.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ChatScreen.h" + +class InBedChatScreen : public ChatScreen +{ +private: + static const int WAKE_UP_BUTTON = 1; +public: + virtual void init(); + virtual void removed(); +protected: + virtual void keyPressed(wchar_t ch, int eventKey); +public: + virtual void render(int xm, int ym, float a); +protected: + virtual void buttonClicked(Button *button); +private: + void sendWakeUp(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Input.cpp b/Minecraft.Client/Input.cpp new file mode 100644 index 0000000..bc95ad5 --- /dev/null +++ b/Minecraft.Client/Input.cpp @@ -0,0 +1,187 @@ +#include "stdafx.h" +#include "Minecraft.h" +#include "GameMode.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "Input.h" +#include "..\Minecraft.Client\LocalPlayer.h" +#include "Options.h" +#ifdef _WINDOWS64 +#include "KeyboardMouseInput.h" +#endif + +Input::Input() +{ + xa = 0; + ya = 0; + wasJumping = false; + jumping = false; + sneaking = false; + + lReset = false; + rReset = false; +} + +void Input::tick(LocalPlayer *player) +{ + // 4J Stu - Assume that we only need one input class, even though the java has subclasses for keyboard/controller + // This function is based on the ControllerInput class in the Java, and will probably need changed + //OutputDebugString("INPUT: Beginning input tick\n"); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + int iPad=player->GetXboxPad(); + + float controllerXA = 0.0f; + float controllerYA = 0.0f; + + // 4J-PB minecraft movement seems to be the wrong way round, so invert x! + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) + controllerXA = -InputManager.GetJoypadStick_LX(iPad); + + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) + controllerYA = InputManager.GetJoypadStick_LY(iPad); + + float kbXA = 0.0f; + float kbYA = 0.0f; +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsMouseGrabbed()) + { + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_RIGHT) ) + kbXA = g_KBMInput.GetMoveX(); + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_FORWARD) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_BACKWARD) ) + kbYA = g_KBMInput.GetMoveY(); + } +#endif + + if (kbXA != 0.0f || kbYA != 0.0f) + { + xa = kbXA; + ya = kbYA; + } + else + { + xa = controllerXA; + ya = controllerYA; + } + +#ifndef _CONTENT_PACKAGE + if (app.GetFreezePlayers()) + { + xa = ya = 0.0f; + player->abilities.flying = true; + } +#endif + + if (!lReset) + { + if (xa*xa+ya*ya==0.0f) + { + lReset = true; + } + xa = ya = 0.0f; + } + + // 4J - in flying mode, don't actually toggle sneaking + + if(!player->abilities.flying) + { + if((player->ullButtonsPressed&(1LL<localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) + { + sneaking=!sneaking; + } + } + +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsMouseGrabbed() && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_SNEAK_TOGGLE)) + { + if (!player->abilities.flying) + { + sneaking = g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_SNEAK); + } + } +#endif + + if(sneaking) + { + xa*=0.3f; + ya*=0.3f; + } + + float turnSpeed = 50.0f; + + float tx = 0.0f; + float ty = 0.0f; + + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_LEFT) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_RIGHT) ) + tx = InputManager.GetJoypadStick_RX(iPad)*(((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame))/100.0f); // apply sensitivity to look + if( pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_UP) || pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_LOOK_DOWN) ) + ty = InputManager.GetJoypadStick_RY(iPad)*(((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame))/100.0f); // apply sensitivity to look + +#ifndef _CONTENT_PACKAGE + if (app.GetFreezePlayers()) tx = ty = 0.0f; +#endif + + // 4J: WESTY : Invert look Y if required. + if ( app.GetGameSettings(iPad,eGameSetting_ControlInvertLook) ) + { + ty = -ty; + } + + if (!rReset) + { + if (tx*tx+ty*ty==0.0f) + { + rReset = true; + } + tx = ty = 0.0f; + } + + float turnX = tx * abs(tx) * turnSpeed; + float turnY = ty * abs(ty) * turnSpeed; + +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsMouseGrabbed()) + { + float mouseSensitivity = ((float)app.GetGameSettings(iPad,eGameSetting_Sensitivity_InGame)) / 100.0f; + float mouseLookScale = 1.0f; + float mx = g_KBMInput.GetLookX(mouseSensitivity * mouseLookScale); + float my = g_KBMInput.GetLookY(mouseSensitivity * mouseLookScale); + + if ( app.GetGameSettings(iPad,eGameSetting_ControlInvertLook) ) + { + my = -my; + } + + turnX += mx; + turnY += my; + } +#endif + + player->interpolateTurn(turnX, turnY); + + //jumping = controller.isButtonPressed(0); + + unsigned int jump = InputManager.GetValue(iPad, MINECRAFT_ACTION_JUMP); + bool kbJump = false; +#ifdef _WINDOWS64 + kbJump = (iPad == 0) && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKeyDown(KeyboardMouseInput::KEY_JUMP); +#endif + if( (jump > 0 || kbJump) && pMinecraft->localgameModes[iPad]->isInputAllowed(MINECRAFT_ACTION_JUMP) ) + jumping = true; + else + jumping = false; + +#ifndef _CONTENT_PACKAGE + if (app.GetFreezePlayers()) jumping = false; +#endif + +#ifdef _WINDOWS64 + if (iPad == 0 && g_KBMInput.IsKeyPressed(VK_ESCAPE) && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + + //OutputDebugString("INPUT: End input tick\n"); +} \ No newline at end of file diff --git a/Minecraft.Client/Input.h b/Minecraft.Client/Input.h new file mode 100644 index 0000000..0a44d76 --- /dev/null +++ b/Minecraft.Client/Input.h @@ -0,0 +1,22 @@ +#pragma once +class Player; + +class Input +{ +public: + float xa; + float ya; + + bool wasJumping; + bool jumping; + bool sneaking; + + Input(); // 4J - added + + virtual void tick(LocalPlayer *player); + +private: + + bool lReset; + bool rReset; +}; \ No newline at end of file diff --git a/Minecraft.Client/InventoryScreen.cpp b/Minecraft.Client/InventoryScreen.cpp new file mode 100644 index 0000000..726a5e8 --- /dev/null +++ b/Minecraft.Client/InventoryScreen.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "InventoryScreen.h" +#include "MultiplayerLocalPlayer.h" +#include "Font.h" +#include "EntityRenderDispatcher.h" +#include "Lighting.h" +#include "Textures.h" +#include "Button.h" +#include "AchievementScreen.h" +#include "StatsScreen.h" +#include "..\Minecraft.World\net.minecraft.stats.h" + +InventoryScreen::InventoryScreen(shared_ptr player) : AbstractContainerScreen(player->inventoryMenu) +{ + xMouse = yMouse = 0.0f; // 4J added + + this->passEvents = true; + player->awardStat(GenericStats::openInventory(), GenericStats::param_noArgs()); +} + +void InventoryScreen::init() +{ + buttons.clear(); +} + +void InventoryScreen::renderLabels() +{ + font->draw(L"Crafting", 84 + 2, 8 * 2, 0x404040); +} + +void InventoryScreen::render(int xm, int ym, float a) +{ + AbstractContainerScreen::render(xm, ym, a); + this->xMouse = (float)xm; + this->yMouse = (float)ym; +} + +void InventoryScreen::renderBg(float a) +{ + // 4J Unused +#if 0 + int tex = minecraft->textures->loadTexture(L"/gui/inventory.png"); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, imageHeight); + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glPushMatrix(); + glTranslatef((float)xo + 51, (float)yo + 75, 50); + float ss = 30; + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + float oybr = minecraft->player->yBodyRot; + float oyr = minecraft->player->yRot; + float oxr = minecraft->player->xRot; + + float xd = (xo + 51) - xMouse; + float yd = (yo + 75 - 50) - yMouse; + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float) atan(yd / 40.0f) * 20, 1, 0, 0); + + minecraft->player->yBodyRot = (float) atan(xd / 40.0f) * 20; + minecraft->player->yRot = (float) atan(xd / 40.0f) * 40; + minecraft->player->xRot = -(float) atan(yd / 40.0f) * 20; + glTranslatef(0, minecraft->player->heightOffset, 0); + EntityRenderDispatcher::instance->playerRotY = 180; + EntityRenderDispatcher::instance->render(minecraft->player, 0, 0, 0, 0, 1); + minecraft->player->yBodyRot = oybr; + minecraft->player->yRot = oyr; + minecraft->player->xRot = oxr; + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +#endif +} + +void InventoryScreen::buttonClicked(Button *button) +{ + if (button->id == 0) + { + minecraft->setScreen(new AchievementScreen(minecraft->stats[minecraft->player->GetXboxPad()])); + } + if (button->id == 1) + { + minecraft->setScreen(new StatsScreen(this, minecraft->stats[minecraft->player->GetXboxPad()])); + } +} diff --git a/Minecraft.Client/InventoryScreen.h b/Minecraft.Client/InventoryScreen.h new file mode 100644 index 0000000..c39edfe --- /dev/null +++ b/Minecraft.Client/InventoryScreen.h @@ -0,0 +1,20 @@ +#pragma once +#include "AbstractContainerScreen.h" +class Player; +class Button; + +class InventoryScreen : public AbstractContainerScreen +{ +public: + InventoryScreen(shared_ptr player); + virtual void init(); +protected: + virtual void renderLabels(); +private: + float xMouse, yMouse; +public: + virtual void render(int xm, int ym, float a); +protected: + virtual void renderBg(float a); + virtual void buttonClicked(Button *button); +}; \ No newline at end of file diff --git a/Minecraft.Client/ItemFrameRenderer.cpp b/Minecraft.Client/ItemFrameRenderer.cpp new file mode 100644 index 0000000..40769fb --- /dev/null +++ b/Minecraft.Client/ItemFrameRenderer.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "ItemRenderer.h" +#include "tileRenderer.h" +#include "entityRenderDispatcher.h" +//#include "ItemFrame" +#include "ItemFrameRenderer.h" + +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\net.minecraft.world.entity.Item.h" +#include "..\Minecraft.World\net.minecraft.world.Item.h" +#include "..\Minecraft.World\net.minecraft.world.Item.alchemy.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "Minecraft.h" +#include "..\Minecraft.World\Item.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "..\Minecraft.World\net.minecraft.h" +#include "CompassTexture.h" +#include "Minimap.h" + +void ItemFrameRenderer::registerTerrainTextures(IconRegister *iconRegister) +{ + backTexture = iconRegister->registerIcon(L"itemframe_back"); +} + +void ItemFrameRenderer::render(shared_ptr _itemframe, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type EnderCrystal rather than shared_ptr we have here - + // do some casting around instead + shared_ptr itemFrame = dynamic_pointer_cast(_itemframe); + + glPushMatrix(); + float xOffs = (float) (itemFrame->x - x) - 0.5f; + float yOffs = (float) (itemFrame->y - y) - 0.5f; + float zOffs = (float) (itemFrame->z - z) - 0.5f; + + int xt = itemFrame->xTile + Direction::STEP_X[itemFrame->dir]; + int yt = itemFrame->yTile; + int zt = itemFrame->zTile + Direction::STEP_Z[itemFrame->dir]; + + glTranslatef((float) xt - xOffs, (float) yt - yOffs, (float) zt - zOffs); + + drawFrame(itemFrame); + drawItem(itemFrame); + + glPopMatrix(); +} + + +void ItemFrameRenderer::drawFrame(shared_ptr itemFrame) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + glPushMatrix(); + entityRenderDispatcher->textures->bindTexture(TN_TERRAIN); + glRotatef(itemFrame->yRot, 0, 1, 0); + + Tile *wood = Tile::wood; + float depth = 1.0f / 16.0f; + float width = 12.0f / 16.0f; + float widthHalf = width / 2.0f; + + // Back + glPushMatrix(); + + tileRenderer->setFixedShape(0, 0.5f - widthHalf + 1.0f / 16.0f, 0.5f - widthHalf + 1.0f / 16.0f, depth * .5f, 0.5f + widthHalf - 1.0f / 16.0f, 0.5f + widthHalf - 1.0f / 16.0f); + tileRenderer->setFixedTexture(backTexture); + tileRenderer->renderTile(wood, 0, 1); + tileRenderer->clearFixedTexture(); + tileRenderer->clearFixedShape(); + glPopMatrix(); + + tileRenderer->setFixedTexture(Tile::wood->getTexture(Facing::UP, TreeTile::BIRCH_TRUNK)); + + // Bottom + glPushMatrix(); + tileRenderer->setFixedShape(0, 0.5f - widthHalf, 0.5f - widthHalf, depth + 0.0001f, depth + 0.5f - widthHalf, 0.5f + widthHalf); + tileRenderer->renderTile(wood, 0, 1); + glPopMatrix(); + + // Top + glPushMatrix(); + tileRenderer->setFixedShape(0, 0.5f + widthHalf - depth, 0.5f - widthHalf, depth + 0.0001f, 0.5f + widthHalf, 0.5f + widthHalf); + tileRenderer->renderTile(wood, 0, 1); + glPopMatrix(); + + // Right + glPushMatrix(); + tileRenderer->setFixedShape(0, 0.5f - widthHalf, 0.5f - widthHalf, depth, 0.5f + widthHalf, depth + 0.5f - widthHalf); + tileRenderer->renderTile(wood, 0, 1); + glPopMatrix(); + + // Left + glPushMatrix(); + tileRenderer->setFixedShape(0, 0.5f - widthHalf, 0.5f + widthHalf - depth, depth, 0.5f + widthHalf, 0.5f + widthHalf); + tileRenderer->renderTile(wood, 0, 1); + glPopMatrix(); + + tileRenderer->clearFixedShape(); + tileRenderer->clearFixedTexture(); + + glPopMatrix(); +} + +void ItemFrameRenderer::drawItem(shared_ptr entity) +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + shared_ptr instance = entity->getItem(); + if (instance == NULL) return; + + shared_ptr itemEntity = shared_ptr(new ItemEntity(entity->level, 0, 0, 0, instance)); + itemEntity->getItem()->count = 1; + itemEntity->bobOffs = 0; + + glPushMatrix(); + + glTranslatef((-7.25f / 16.0f) * Direction::STEP_X[entity->dir], -0.18f, (-7.25f / 16.0f) * Direction::STEP_Z[entity->dir]); + glRotatef(180 + entity->yRot, 0, 1, 0); + glRotatef(-90 * entity->getRotation(), 0, 0, 1); + + switch (entity->getRotation()) + { + case 1: + glTranslatef(-0.16f, -0.16f, 0); + break; + case 2: + glTranslatef(0, -0.32f, 0); + break; + case 3: + glTranslatef(0.16f, -0.16f, 0); + break; + } + + if (itemEntity->getItem()->getItem() == Item::map) + { + entityRenderDispatcher->textures->bindTexture(TN_MISC_MAPBG); + Tesselator *t = Tesselator::getInstance(); + + glRotatef(180, 0, 1, 0); + glRotatef(180, 0, 0, 1); + glScalef(1.0f / 256.0f, 1.0f / 256.0f, 1.0f / 256.0f); + glTranslatef(-65, -107, -3); + glNormal3f(0, 0, -1); + t->begin(); + int vo = 7; + t->vertexUV(0 - vo, 128 + vo, 0, 0, 1); + t->vertexUV(128 + vo, 128 + vo, 0, 1, 1); + t->vertexUV(128 + vo, 0 - vo, 0, 1, 0); + t->vertexUV(0 - vo, 0 - vo, 0, 0, 0); + t->end(); + + shared_ptr data = Item::map->getSavedData(itemEntity->getItem(), entity->level); + if (data != NULL) + { + entityRenderDispatcher->itemInHandRenderer->minimap->render(nullptr, entityRenderDispatcher->textures, data, entity->entityId); + } + } + else + { + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + double compassRot = ct->rot; + double compassRotA = ct->rota; + ct->rot = 0; + ct->rota = 0; + ct->updateFromPosition(entity->level, entity->x, entity->z, Mth::wrapDegrees( (float)(180 + entity->dir * 90) ), false, true); + ct->rot = compassRot; + ct->rota = compassRotA; + } + + EntityRenderDispatcher::instance->render(itemEntity, 0, 0, 0, 0, 0, true); + + if (itemEntity->getItem()->getItem() == Item::compass) + { + CompassTexture *ct = CompassTexture::instance; + ct->cycleFrames(); + } + } + + glPopMatrix(); +} + diff --git a/Minecraft.Client/ItemFrameRenderer.h b/Minecraft.Client/ItemFrameRenderer.h new file mode 100644 index 0000000..47f2fe9 --- /dev/null +++ b/Minecraft.Client/ItemFrameRenderer.h @@ -0,0 +1,17 @@ +#pragma once +#include "EntityRenderer.h" + +class ItemFrameRenderer : public EntityRenderer +{ +private: + Icon *backTexture; + + //@Override +public: + void registerTerrainTextures(IconRegister *iconRegister); + virtual void render(shared_ptr _itemframe, double x, double y, double z, float rot, float a); + +private: + void drawFrame(shared_ptr itemFrame); + void drawItem(shared_ptr entity); +}; diff --git a/Minecraft.Client/ItemInHandRenderer.cpp b/Minecraft.Client/ItemInHandRenderer.cpp new file mode 100644 index 0000000..cee9254 --- /dev/null +++ b/Minecraft.Client/ItemInHandRenderer.cpp @@ -0,0 +1,878 @@ +#include "stdafx.h" +#include "ItemInHandRenderer.h" +#include "TileRenderer.h" +#include "Tesselator.h" +#include "Textures.h" +#include "EntityRenderer.h" +#include "PlayerRenderer.h" +#include "EntityRenderDispatcher.h" +#include "Lighting.h" +#include "MultiplayerLocalPlayer.h" +#include "Minimap.h" +#include "MultiPlayerLevel.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.h" + +int ItemInHandRenderer::list = -1; +int ItemInHandRenderer::listGlint = -1; + +ItemInHandRenderer::ItemInHandRenderer(Minecraft *mc, bool optimisedMinimap) +{ + // 4J - added + height = 0; + oHeight = 0; + selectedItem = nullptr; + tileRenderer = new TileRenderer(); + lastSlot = -1; + + this->mc = mc; + minimap = new Minimap(mc->font, mc->options, mc->textures, optimisedMinimap); + + // 4J - replaced mesh that is used to render held items with individual cubes, so we can make it all join up properly without seams. This + // has a lot more quads in it than the original, so is now precompiled with a UV matrix offset to put it in the final place for the + // current icon. Compile it on demand for the first ItemInHandRenderer (list is static) + if( list == -1 ) + { + list = MemoryTracker::genLists(1); + float dd = 1 / 16.0f; + + glNewList(list, GL_COMPILE); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + for( int yp = 0; yp < 16; yp++ ) + for( int xp = 0; xp < 16; xp++ ) + { + float u = (15-xp) / 256.0f; + float v = (15-yp) / 256.0f; + u += 0.5f / 256.0f; + v += 0.5f / 256.0f; + float x0 = xp / 16.0f; + float x1 = x0 + 1.0f/16.0f; + float y0 = yp / 16.0f; + float y1 = y0 + 1.0f/16.0f; + float z0 = 0.0f; + float z1 = -dd; + + t->normal(0, 0, 1); + t->vertexUV(x0, y0, z0, u, v); + t->vertexUV(x1, y0, z0, u, v); + t->vertexUV(x1, y1, z0, u, v); + t->vertexUV(x0, y1, z0, u, v); + t->normal(0, 0, -1); + t->vertexUV(x0, y1, z1, u, v); + t->vertexUV(x1, y1, z1, u, v); + t->vertexUV(x1, y0, z1, u, v); + t->vertexUV(x0, y0, z1, u, v); + t->normal(-1, 0, 0); + t->vertexUV(x0, y0, z1, u, v); + t->vertexUV(x0, y0, z0, u, v); + t->vertexUV(x0, y1, z0, u, v); + t->vertexUV(x0, y1, z1, u, v); + t->normal(1, 0, 0); + t->vertexUV(x1, y1, z1, u, v); + t->vertexUV(x1, y1, z0, u, v); + t->vertexUV(x1, y0, z0, u, v); + t->vertexUV(x1, y0, z1, u, v); + t->normal(0, 1, 0); + t->vertexUV(x1, y0, z0, u, v); + t->vertexUV(x0, y0, z0, u, v); + t->vertexUV(x0, y0, z1, u, v); + t->vertexUV(x1, y0, z1, u, v); + t->normal(0, -1, 0); + t->vertexUV(x1, y1, z1, u, v); + t->vertexUV(x0, y1, z1, u, v); + t->vertexUV(x0, y1, z0, u, v); + t->vertexUV(x1, y1, z0, u, v); + } + t->end(); + glEndList(); + } + + // Also create special object for glint overlays - this is the same as the previous one, with a different UV scalings, and depth test set to equal + if( listGlint == -1 ) + { + listGlint = MemoryTracker::genLists(1); + float dd = 1 / 16.0f; + + glNewList(listGlint, GL_COMPILE); + glDepthFunc(GL_EQUAL); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + for( int yp = 0; yp < 16; yp++ ) + for( int xp = 0; xp < 16; xp++ ) + { + float u0 = (15-xp) / 16.0f; + float v0 = (15-yp) / 16.0f; + float u1 = u0 - (1.0f/16.0f); + float v1 = v0 - (1.0f/16.0f);; + + float x0 = xp / 16.0f; + float x1 = x0 + 1.0f/16.0f; + float y0 = yp / 16.0f; + float y1 = y0 + 1.0f/16.0f; + float z0 = 0.0f; + float z1 = -dd; + + float br = 0.76f; + t->color(0.5f * br, 0.25f * br, 0.8f * br, 1.0f); // MGH - added the color here, as the glColour below wasn't making it through to render + + t->normal(0, 0, 1); + t->vertexUV(x0, y0, z0, u0, v0); + t->vertexUV(x1, y0, z0, u1, v0); + t->vertexUV(x1, y1, z0, u1, v1); + t->vertexUV(x0, y1, z0, u0, v1); + t->normal(0, 0, -1); + t->vertexUV(x0, y1, z1, u0, v1); + t->vertexUV(x1, y1, z1, u1, v1); + t->vertexUV(x1, y0, z1, u1, v0); + t->vertexUV(x0, y0, z1, u0, v0); + t->normal(-1, 0, 0); + t->vertexUV(x0, y0, z1, u0, v0); + t->vertexUV(x0, y0, z0, u0, v0); + t->vertexUV(x0, y1, z0, u0, v1); + t->vertexUV(x0, y1, z1, u0, v1); + t->normal(1, 0, 0); + t->vertexUV(x1, y1, z1, u1, v1); + t->vertexUV(x1, y1, z0, u1, v1); + t->vertexUV(x1, y0, z0, u1, v0); + t->vertexUV(x1, y0, z1, u1, v0); + t->normal(0, 1, 0); + t->vertexUV(x1, y0, z0, u1, v0); + t->vertexUV(x0, y0, z0, u0, v0); + t->vertexUV(x0, y0, z1, u0, v0); + t->vertexUV(x1, y0, z1, u1, v0); + t->normal(0, -1, 0); + t->vertexUV(x1, y1, z1, u1, v1); + t->vertexUV(x0, y1, z1, u0, v1); + t->vertexUV(x0, y1, z0, u0, v1); + t->vertexUV(x1, y1, z0, u1, v1); + } + t->end(); + glDepthFunc(GL_LEQUAL); + glEndList(); + } + +} + +void ItemInHandRenderer::renderItem(shared_ptr mob, shared_ptr item, int layer, bool setColor/* = true*/) +{ + // 4J - code borrowed from render method below, although not factoring in brightness as that should already be being taken into account + // by texture lighting. This is for colourising things held in 3rd person view. + if ( (setColor) && (item != NULL) ) + { + int col = Item::items[item->id]->getColor(item,0); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + glColor4f(red, g, b, 1); + } + + glPushMatrix(); + Tile *tile = Tile::tiles[item->id]; + if (item->getIconType() == Icon::TYPE_TERRAIN && tile != NULL && TileRenderer::canRender(tile->getRenderShape())) + { + MemSect(31); + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + tileRenderer->renderTile(tile, item->getAuxValue(), SharedConstants::TEXTURE_LIGHTING ? 1.0f : mob->getBrightness(1)); // 4J - change brought forward from 1.8.2 + } + else + { + MemSect(31); + Icon *icon = mob->getItemInHandIcon(item, layer); + if (icon == NULL) + { + glPopMatrix(); + MemSect(0); + return; + } + + if (item->getIconType() == Icon::TYPE_TERRAIN) + { + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + } + else + { + mc->textures->bindTexture(TN_GUI_ITEMS); // 4J was L"/gui/items.png" + } + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + + // Consider forcing the mipmap LOD level to use, if this is to be rendered from a larger than standard source texture. + int iconWidth = icon->getWidth(); + int LOD = -1; // Default to not doing anything special with LOD forcing + if( iconWidth == 32 ) + { + LOD = 1; // Force LOD level 1 to achieve texture reads from 256x256 map + } + else if( iconWidth == 64 ) + { + LOD = 2; // Force LOD level 2 to achieve texture reads from 256x256 map + } + RenderManager.StateSetForceLOD(LOD); + + // 4J Original comment + // Yes, these are backwards. + // No, I don't know why. + // 4J Stu - Make them the right way round...u coords were swapped + float u0 = icon->getU0(); + float u1 = icon->getU1(); + float v0 = icon->getV0(); + float v1 = icon->getV1(); + + float xo = 0.0f; + float yo = 0.3f; + + glEnable(GL_RESCALE_NORMAL); + glTranslatef(-xo, -yo, 0); + float s = 1.5f; + glScalef(s, s, s); + + glRotatef(50, 0, 1, 0); + glRotatef(45 + 290, 0, 0, 1); + glTranslatef(-15 / 16.0f, -1 / 16.0f, 0); + float dd = 1 / 16.0f; + + renderItem3D(t, u0, v0, u1, v1, icon->getSourceWidth(), icon->getSourceHeight(), 1 / 16.0f, false); + + if (item != NULL && item->isFoil() && layer == 0) + { + glDepthFunc(GL_EQUAL); + glDisable(GL_LIGHTING); + mc->textures->bind(mc->textures->loadTexture(TN__BLUR__MISC_GLINT)); // 4J was L"%blur%/misc/glint.png" + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_COLOR, GL_ONE); + float br = 0.76f; + glColor4f(0.5f * br, 0.25f * br, 0.8f * br, 1); // MGH - for some reason this colour isn't making it through to the render, so I've added to the tesselator for the glint geom above + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + float ss = 1 / 8.0f; + glScalef(ss, ss, ss); + float sx = Minecraft::currentTimeMillis() % (3000) / (3000.0f) * 8; + glTranslatef(sx, 0, 0); + glRotatef(-50, 0, 0, 1); + + renderItem3D(t, 0, 0, 1, 1, 256, 256, 1 / 16.0f, true); + glPopMatrix(); + glPushMatrix(); + glScalef(ss, ss, ss); + sx = System::currentTimeMillis() % (3000 + 1873) / (3000 + 1873.0f) * 8; + glTranslatef(-sx, 0, 0); + glRotatef(10, 0, 0, 1); + renderItem3D(t, 0, 0, 1, 1, 256, 256, 1 / 16.0f, true); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + glDepthFunc(GL_LEQUAL); + } + + RenderManager.StateSetForceLOD(-1); + + glDisable(GL_RESCALE_NORMAL); + } + glPopMatrix(); +} + +// 4J added useList parameter +void ItemInHandRenderer::renderItem3D(Tesselator *t, float u0, float v0, float u1, float v1, int width, int height, float depth, bool isGlint) +{ + float r = 1.0f; + + // 4J - replaced mesh that is used to render held items with individual cubes, so we can make it all join up properly without seams. This + // has a lot more quads in it than the original, so is now precompiled with a UV matrix offset to put it in the final place for the + // current icon + + if( isGlint ) + { + glCallList(listGlint); + } + else + { + // 4J - replaced mesh that is used to render held items with individual cubes, so we can make it all join up properly without seams. This + // has a lot more quads in it than the original, so is now precompiled with a UV matrix offset to put it in the final place for the + // current icon + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glTranslatef(u0, v0, 0); + glCallList(list); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + // 4J added since we are setting the colour to other values at the start of the function now + glColor4f(1.0f,1.0f,1.0f,1.0f); +} + +void ItemInHandRenderer::render(float a) +{ + float h = oHeight + (height - oHeight) * a; + shared_ptr player = mc->player; + + // 4J - added so we can adjust the position of the hands for horizontal & vertical split screens + float fudgeX = 0.0f; + float fudgeY = 0.0f; + float fudgeZ = 0.0f; + bool splitHoriz = false; + shared_ptr localPlayer = dynamic_pointer_cast(player); + if( localPlayer ) + { + if( localPlayer->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM || + localPlayer->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_TOP ) + { + fudgeY = 0.08f; + splitHoriz = true; + } + else if( localPlayer->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT || + localPlayer->m_iScreenSection == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT ) + { + fudgeX = -0.18f; + } + } + + float xr = player->xRotO + (player->xRot - player->xRotO) * a; + + glPushMatrix(); + glRotatef(xr, 1, 0, 0); + glRotatef(player->yRotO + (player->yRot - player->yRotO) * a, 0, 1, 0); + Lighting::turnOn(); + glPopMatrix(); + + if (localPlayer) + { + float xrr = localPlayer->xBobO + (localPlayer->xBob - localPlayer->xBobO) * a; + float yrr = localPlayer->yBobO + (localPlayer->yBob - localPlayer->yBobO) * a; + // 4J - was using player->xRot and yRot directly here rather than interpolating between old & current with a + float yr = player->yRotO + (player->yRot - player->yRotO) * a; + glRotatef((xr - xrr) * 0.1f, 1, 0, 0); + glRotatef((yr - yrr) * 0.1f, 0, 1, 0); + } + + shared_ptr item = selectedItem; + + float br = mc->level->getBrightness(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + // 4J - change brought forward from 1.8.2 + if (SharedConstants::TEXTURE_LIGHTING) + { + br = 1; + int col = mc->level->getLightColor(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z), 0); + int u = col % 65536; + int v = col / 65536; + glMultiTexCoord2f(GL_TEXTURE1, u / 1.0f, v / 1.0f); + glColor4f(1, 1, 1, 1); + } + if (item != NULL) + { + int col = Item::items[item->id]->getColor(item,0); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + glColor4f(br * red, br * g, br * b, 1); + } + else + { + glColor4f(br, br, br, 1); + } + + if (item != NULL && item->id == Item::map->id) + { + glPushMatrix(); + float d = 0.8f; + + // 4J - move the map away a bit if we're in horizontal split screen, so it doesn't clip out of the save zone + if( splitHoriz ) + { + glTranslatef(0.0f, 0.0f, -0.3f ); + } + + { + float swing = player->getAttackAnim(a); + + float swing1 = Mth::sin(swing * PI); + float swing2 = Mth::sin((sqrt(swing)) * PI); + glTranslatef(-swing2 * 0.4f, Mth::sin(sqrt(swing) * PI * 2) * 0.2f, -swing1 * 0.2f); + } + + float tilt = 1 - xr / 45.0f + 0.1f; + if (tilt < 0) tilt = 0; + if (tilt > 1) tilt = 1; + tilt = -Mth::cos(tilt * PI) * 0.5f + 0.5f; + + glTranslatef(0.0f, 0.0f * d - (1 - h) * 1.2f - tilt * 0.5f + 0.04f, -0.9f * d); + + glRotatef(90, 0, 1, 0); + glRotatef((tilt) * -85, 0, 0, 1); + glEnable(GL_RESCALE_NORMAL); + + + { + // 4J-PB - if we've got a player texture, use that + //glBindTexture(GL_TEXTURE_2D, mc->textures->loadHttpTexture(mc->player->customTextureUrl, mc->player->getTexture())); + glBindTexture(GL_TEXTURE_2D, mc->textures->loadMemTexture(mc->player->customTextureUrl, mc->player->getTexture())); + mc->textures->clearLastBoundId(); + for (int i = 0; i < 2; i++) + { + int flip = i * 2 - 1; + glPushMatrix(); + + glTranslatef(-0.0f, -0.6f, 1.1f * flip); + glRotatef((float)(-45 * flip), 1, 0, 0); + glRotatef(-90, 0, 0, 1); + glRotatef(59, 0, 0, 1); + glRotatef((float)(-65 * flip), 0, 1, 0); + + EntityRenderer *er = EntityRenderDispatcher::instance->getRenderer(mc->player); + PlayerRenderer *playerRenderer = (PlayerRenderer *) er; + float ss = 1; + glScalef(ss, ss, ss); + + // Can't turn off the hand if the player is holding a map + shared_ptr itemInstance = player->inventory->getSelected(); + if ((itemInstance && (itemInstance->getItem()->id==Item::map_Id)) || app.GetGameSettings(localPlayer->GetXboxPad(),eGameSetting_DisplayHand)!=0 ) + { + playerRenderer->renderHand(); + } + glPopMatrix(); + } + } + + { + float swing = player->getAttackAnim(a); + float swing3 = Mth::sin(swing * swing * PI); + float swing2 = Mth::sin(sqrt(swing) * PI); + glRotatef(-swing3 * 20, 0, 1, 0); + glRotatef(-swing2 * 20, 0, 0, 1); + glRotatef(-swing2 * 80, 1, 0, 0); + } + + float ss = 0.38f; + glScalef(ss, ss, ss); + + glRotatef(90, 0, 1, 0); + glRotatef(180, 0, 0, 1); + + glTranslatef(-1, -1, +0); + + float s = 2 / 128.0f; + glScalef(s, s, s); + + MemSect(31); + mc->textures->bindTexture(TN_MISC_MAPBG); // 4J was L"/misc/mapbg.png" + MemSect(0); + Tesselator *t = Tesselator::getInstance(); + +// glNormal3f(0, 0, -1); // 4J - changed to use tesselator + t->begin(); + int vo = 7; + t->normal(0,0,-1); + t->vertexUV((float)(0 - vo), (float)( 128 + vo), (float)( 0), (float)( 0), (float)( 1)); + t->vertexUV((float)(128 + vo), (float)( 128 + vo), (float)( 0), (float)( 1), (float)( 1)); + t->vertexUV((float)(128 + vo), (float)( 0 - vo), (float)( 0), (float)( 1), (float)( 0)); + t->vertexUV((float)(0 - vo), (float)( 0 - vo), (float)( 0), (float)( 0), (float)( 0)); + t->end(); + + shared_ptr data = Item::map->getSavedData(item, mc->level); + PIXBeginNamedEvent(0,"Minimap render"); + if(data != NULL) minimap->render(mc->player, mc->textures, data, mc->player->entityId); + PIXEndNamedEvent(); + + glPopMatrix(); + } + else if (item != NULL) + { + glPushMatrix(); + float d = 0.8f; + +#if defined __ORBIS__ || defined __PS3__ + static const float swingPowFactor = 1.0f; +#else + static const float swingPowFactor = 4.0f; // 4J added, to slow the swing down when nearest the player for avoiding luminance flash issues +#endif + if (player->getUseItemDuration() > 0) + { + UseAnim anim = item->getUseAnimation(); + if ( (anim == UseAnim_eat) || (anim == UseAnim_drink) ) + { + float t = (player->getUseItemDuration() - a + 1); + float swing = 1 - (t / item->getUseDuration()); + + float is = 1 - swing; + is = is * is * is; + is = is * is * is; + is = is * is * is; + float iss = 1 - is; + glTranslatef(0, Mth::abs(Mth::cos(t / 4 * PI) * 0.1f) * (swing > 0.2 ? 1 : 0), 0); + glTranslatef(iss * 0.6f, -iss * 0.5f, 0); + glRotatef(iss * 90, 0, 1, 0); + glRotatef(iss * 10, 1, 0, 0); + glRotatef(iss * 30, 0, 0, 1); + } + } + else + { + float swing = powf(player->getAttackAnim(a),swingPowFactor); + + float swing1 = Mth::sin(swing * PI); + float swing2 = Mth::sin((sqrt(swing)) * PI); + glTranslatef(-swing2 * 0.4f, Mth::sin(sqrt(swing) * PI * 2) * 0.2f, -swing1 * 0.2f); + + } + + glTranslatef(0.7f * d, -0.65f * d - (1 - h) * 0.6f, -0.9f * d); + glTranslatef(fudgeX, fudgeY, fudgeZ); // 4J added + + glRotatef(45, 0, 1, 0); + glEnable(GL_RESCALE_NORMAL); + + float swing = powf(player->getAttackAnim(a),swingPowFactor); + float swing3 = Mth::sin(swing * swing * PI); + float swing2 = Mth::sin(sqrt(swing) * PI); + glRotatef(-swing3 * 20, 0, 1, 0); + glRotatef(-swing2 * 20, 0, 0, 1); + glRotatef(-swing2 * 80, 1, 0, 0); + + float ss = 0.4f; + glScalef(ss, ss, ss); + + if (player->getUseItemDuration() > 0) + { + UseAnim anim = item->getUseAnimation(); + if (anim == UseAnim_block) + { + glTranslatef(-0.5f, 0.2f, 0.0f); + glRotatef(30, 0, 1, 0); + glRotatef(-80, 1, 0, 0); + glRotatef(60, 0, 1, 0); + } + else if (anim == UseAnim_bow) + { + + glRotatef(-18, 0, 0, 1); + glRotatef(-12, 0, 1, 0); + glRotatef(-8, 1, 0, 0); + glTranslatef(-0.9f, 0.2f, 0.0f); + float timeHeld = (item->getUseDuration() - (player->getUseItemDuration() - a + 1)); + float pow = timeHeld / (float) (BowItem::MAX_DRAW_DURATION); + pow = ((pow * pow) + pow * 2) / 3; + if (pow > 1) pow = 1; + if (pow > 0.1f) + { + glTranslatef(0, Mth::sin((timeHeld - 0.1f) * 1.3f) * 0.01f * (pow - 0.1f), 0); + } + glTranslatef(0, 0, pow * 0.1f); + + glRotatef(-45 - 290, 0, 0, 1); + glRotatef(-50, 0, 1, 0); + glTranslatef(0, 0.5f, 0); + float ys = 1 + pow * 0.2f; + glScalef(1, 1, ys); + glTranslatef(0, -0.5f, 0); + glRotatef(50, 0, 1, 0); + glRotatef(45 + 290, 0, 0, 1); + } + } + + + if (item->getItem()->isMirroredArt()) + { + glRotatef(180, 0, 1, 0); + } + + if (item->getItem()->hasMultipleSpriteLayers()) + { + // special case for potions, refactor this when we get more + // items that have two layers + renderItem(player, item, 0, false); + + int col = Item::items[item->id]->getColor(item, 1); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + glColor4f(br * red, br * g, br * b, 1); + + renderItem(player, item, 1, false); + } + else + { + renderItem(player, item, 0, false); + } + glPopMatrix(); + } + else if (!player->isInvisible()) + { + glPushMatrix(); + float d = 0.8f; + + { + float swing = player->getAttackAnim(a); + + float swing1 = Mth::sin(swing * PI); + float swing2 = Mth::sin((sqrt(swing)) * PI); + glTranslatef(-swing2 * 0.3f, Mth::sin(sqrt(swing) * PI * 2) * 0.4f, -swing1 * 0.4f); + } + + glTranslatef(0.8f * d, -0.75f * d - (1 - h) * 0.6f, -0.9f * d); + glTranslatef(fudgeX, fudgeY, fudgeZ); // 4J added + + glRotatef(45, 0, 1, 0); + glEnable(GL_RESCALE_NORMAL); + { + float swing = player->getAttackAnim(a); + float swing3 = Mth::sin(swing * swing * PI); + float swing2 = Mth::sin(sqrt(swing) * PI); + glRotatef(swing2 * 70, 0, 1, 0); + glRotatef(-swing3 * 20, 0, 0, 1); + } + + // 4J-PB - if we've got a player texture, use that + + //glBindTexture(GL_TEXTURE_2D, mc->textures->loadHttpTexture(mc->player->customTextureUrl, mc->player->getTexture())); + + MemSect(31); + glBindTexture(GL_TEXTURE_2D, mc->textures->loadMemTexture(mc->player->customTextureUrl, mc->player->getTexture())); + MemSect(0); + mc->textures->clearLastBoundId(); + glTranslatef(-1.0f, +3.6f, +3.5f); + glRotatef(120, 0, 0, 1); + glRotatef(180 + 20, 1, 0, 0); + glRotatef(-90 - 45, 0, 1, 0); + glScalef(1.5f / 24.0f * 16, 1.5f / 24.0f * 16, 1.5f / 24.0f * 16); + glTranslatef(5.6f, 0, 0); + + EntityRenderer *er = EntityRenderDispatcher::instance->getRenderer(mc->player); + PlayerRenderer *playerRenderer = (PlayerRenderer *) er; + float ss = 1; + glScalef(ss, ss, ss); + MemSect(31); + // Can't turn off the hand if the player is holding a map + shared_ptr itemInstance = player->inventory->getSelected(); + + if ( (itemInstance && (itemInstance->getItem()->id==Item::map_Id)) || app.GetGameSettings(localPlayer->GetXboxPad(),eGameSetting_DisplayHand)!=0 ) + { + playerRenderer->renderHand(); + } + MemSect(0); + glPopMatrix(); + } + + glDisable(GL_RESCALE_NORMAL); + Lighting::turnOff(); + +} + +void ItemInHandRenderer::renderScreenEffect(float a) +{ + glDisable(GL_ALPHA_TEST); + if (mc->player->isOnFire()) + { + MemSect(31); + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + renderFire(a); + } + + + if (mc->player->isInWall()) // Inside a tile + { + int x = Mth::floor(mc->player->x); + int y = Mth::floor(mc->player->y); + int z = Mth::floor(mc->player->z); + + MemSect(31); + mc->textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + MemSect(0); + int tile = mc->level->getTile(x, y, z); + if (mc->level->isSolidBlockingTile(x, y, z)) + { + renderTex(a, Tile::tiles[tile]->getTexture(2)); + } + else + { + for (int i = 0; i < 8; i++) + { + float xo = ((i >> 0) % 2 - 0.5f) * mc->player->bbWidth * 0.9f; + float yo = ((i >> 1) % 2 - 0.5f) * mc->player->bbHeight * 0.2f; + float zo = ((i >> 2) % 2 - 0.5f) * mc->player->bbWidth * 0.9f; + int xt = Mth::floor(x + xo); + int yt = Mth::floor(y + yo); + int zt = Mth::floor(z + zo); + if (mc->level->isSolidBlockingTile(xt, yt, zt)) + { + tile = mc->level->getTile(xt, yt, zt); + } + } + } + + if (Tile::tiles[tile] != NULL) renderTex(a, Tile::tiles[tile]->getTexture(2)); + } + + if (mc->player->isUnderLiquid(Material::water)) + { + MemSect(31); + mc->textures->bindTexture(TN_MISC_WATER); // 4J was L"/misc/water.png" + MemSect(0); + renderWater(a); + } + glEnable(GL_ALPHA_TEST); + +} + +void ItemInHandRenderer::renderTex(float a, Icon *slot) +{ + Tesselator *t = Tesselator::getInstance(); + + float br = 0.1f; + br = 0.1f; + glColor4f(br, br, br, 0.5f); + + glPushMatrix(); + + float x0 = -1; + float x1 = +1; + float y0 = -1; + float y1 = +1; + float z0 = -0.5f; + + float r = 2 / 256.0f; + float u0 = slot->getU0(); + float u1 = slot->getU1(); + float v0 = slot->getV0(); + float v1 = slot->getV1(); + + t->begin(); + t->vertexUV((float)(x0), (float)( y0), (float)( z0), (float)( u1), (float)( v1)); + t->vertexUV((float)(x1), (float)( y0), (float)( z0), (float)( u0), (float)( v1)); + t->vertexUV((float)(x1), (float)( y1), (float)( z0), (float)( u0), (float)( v0)); + t->vertexUV((float)(x0), (float)( y1), (float)( z0), (float)( u1), (float)( v0)); + t->end(); + glPopMatrix(); + + glColor4f(1, 1, 1, 1); + +} + +void ItemInHandRenderer::renderWater(float a) +{ + Tesselator *t = Tesselator::getInstance(); + + float br = mc->player->getBrightness(a); + glColor4f(br, br, br, 0.5f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glPushMatrix(); + + float size = 4; + + float x0 = -1; + float x1 = +1; + float y0 = -1; + float y1 = +1; + float z0 = -0.5f; + + float uo = -mc->player->yRot / 64.0f; + float vo = +mc->player->xRot / 64.0f; + + t->begin(); + t->vertexUV((float)(x0), (float)( y0), (float)( z0), (float)( size + uo), (float)( size + vo)); + t->vertexUV((float)(x1), (float)( y0), (float)( z0), (float)( 0 + uo), (float)( size + vo)); + t->vertexUV((float)(x1), (float)( y1), (float)( z0), (float)( 0 + uo), (float)( 0 + vo)); + t->vertexUV((float)(x0), (float)( y1), (float)( z0), (float)( size + uo), (float)( 0 + vo)); + t->end(); + glPopMatrix(); + + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + +} + +void ItemInHandRenderer::renderFire(float a) +{ + Tesselator *t = Tesselator::getInstance(); + glColor4f(1, 1, 1, 0.9f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + float size = 1; + for (int i = 0; i < 2; i++) + { + glPushMatrix(); + Icon *slot = Tile::fire->getTextureLayer(1); + + float u0 = slot->getU0(true); + float u1 = slot->getU1(true); + float v0 = slot->getV0(true); + float v1 = slot->getV1(true); + + float x0 = (0 - size) / 2; + float x1 = x0 + size; + float y0 = 0 - size / 2; + float y1 = y0 + size; + float z0 = -0.5f; + glTranslatef(-(i * 2 - 1) * 0.24f, -0.3f, 0); + glRotatef((i * 2 - 1) * 10.0f, 0, 1, 0); + + t->begin(); + t->vertexUV((float)(x0), (float)( y0), (float)( z0), (float)( u1), (float)( v1)); + t->vertexUV((float)(x1), (float)( y0), (float)( z0), (float)( u0), (float)( v1)); + t->vertexUV((float)(x1), (float)( y1), (float)( z0), (float)( u0), (float)( v0)); + t->vertexUV((float)(x0), (float)( y1), (float)( z0), (float)( u1), (float)( v0)); + t->end(); + glPopMatrix(); + } + glColor4f(1, 1, 1, 1); + glDisable(GL_BLEND); + +} + +void ItemInHandRenderer::tick() +{ + oHeight = height; + + + shared_ptr player = mc->player; + shared_ptr nextTile = player->inventory->getSelected(); + + bool matches = lastSlot == player->inventory->selected && nextTile == selectedItem; + if (selectedItem == NULL && nextTile == NULL) + { + matches = true; + } + if (nextTile != NULL && selectedItem != NULL && nextTile != selectedItem && nextTile->id == selectedItem->id && nextTile->getAuxValue() == selectedItem->getAuxValue()) + { + selectedItem = nextTile; + matches = true; + } + + float max = 0.4f; + float tHeight = matches ? 1.0f : 0; + float dd = tHeight - height; + if (dd < -max) dd = -max; + if (dd > max) dd = max; + + height += dd; + if (height < 0.1f) + { + selectedItem = nextTile; + lastSlot = player->inventory->selected; + } + +} + +void ItemInHandRenderer::itemPlaced() +{ + height = 0; +} + +void ItemInHandRenderer::itemUsed() +{ + height = 0; +} + diff --git a/Minecraft.Client/ItemInHandRenderer.h b/Minecraft.Client/ItemInHandRenderer.h new file mode 100644 index 0000000..dee51a7 --- /dev/null +++ b/Minecraft.Client/ItemInHandRenderer.h @@ -0,0 +1,40 @@ +#pragma once + +class Minecraft; +class ItemInstance; +class Minimap; +class Mob; +class TileRenderer; +class Tesselator; + +class ItemInHandRenderer +{ +private: + Minecraft *mc; + shared_ptr selectedItem; + float height; + float oHeight; + TileRenderer *tileRenderer; + static int list, listGlint; + +public: + // 4J Stu - Made public so we can use it from ItemFramRenderer + Minimap *minimap; + +public: + ItemInHandRenderer(Minecraft *mc, bool optimisedMinimap = true); // 4J Added optimisedMinimap param + void renderItem(shared_ptr mob, shared_ptr item, int layer, bool setColor = true); // 4J added setColor parameter + static void renderItem3D(Tesselator *t, float u0, float v0, float u1, float v1, int width, int height, float depth, bool isGlint); // 4J added isGlint parameter +public: + void render(float a); + void renderScreenEffect(float a); +private: + void renderTex(float a, Icon *slot); + void renderWater(float a); + void renderFire(float a); + int lastSlot; +public: + void tick(); + void itemPlaced(); + void itemUsed(); +}; diff --git a/Minecraft.Client/ItemRenderer.cpp b/Minecraft.Client/ItemRenderer.cpp new file mode 100644 index 0000000..03d0d3a --- /dev/null +++ b/Minecraft.Client/ItemRenderer.cpp @@ -0,0 +1,741 @@ +#include "stdafx.h" +#include "ItemRenderer.h" +#include "TileRenderer.h" +#include "entityRenderDispatcher.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.item.alchemy.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "Options.h" + +ItemRenderer::ItemRenderer() : EntityRenderer() +{ + random = new Random(); + setColor = true; + blitOffset = 0; + + this->shadowRadius = 0.15f; + this->shadowStrength = 0.75f; + + // 4J added + m_bItemFrame= false; +} + +ItemRenderer::~ItemRenderer() +{ + delete random; +} + +void ItemRenderer::render(shared_ptr _itemEntity, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr itemEntity = dynamic_pointer_cast(_itemEntity); + + random->setSeed(187); + shared_ptr item = itemEntity->getItem(); + + glPushMatrix(); + float bob = Mth::sin((itemEntity->age + a) / 10.0f + itemEntity->bobOffs) * 0.1f + 0.1f; + float spin = ((itemEntity->age + a) / 20.0f + itemEntity->bobOffs) * Mth::RADDEG; + + int count = 1; + if (itemEntity->getItem()->count > 1) count = 2; + if (itemEntity->getItem()->count > 5) count = 3; + if (itemEntity->getItem()->count > 20) count = 4; + + glTranslatef((float) x, (float) y + bob, (float) z); + glEnable(GL_RESCALE_NORMAL); + + Tile *tile = Tile::tiles[item->id]; + if (item->getIconType() == Icon::TYPE_TERRAIN && tile != NULL && TileRenderer::canRender(tile->getRenderShape())) + { + glRotatef(spin, 0, 1, 0); + + if (m_bItemFrame) + { + glScalef(1.25f, 1.25f, 1.25f); + glTranslatef(0, 0.05f, 0); + glRotatef(-90, 0, 1, 0); + } + + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + float s = 1 / 4.0f; + int shape = tile->getRenderShape(); + if (shape == Tile::SHAPE_CROSS_TEXTURE || shape == Tile::SHAPE_STEM || shape == Tile::SHAPE_LEVER || shape == Tile::SHAPE_TORCH ) + { + s = 0.5f; + } + + glScalef(s, s, s); + for (int i = 0; i < count; i++) + { + glPushMatrix(); + if (i > 0) + { + float xo = (random->nextFloat() * 2 - 1) * 0.2f / s; + float yo = (random->nextFloat() * 2 - 1) * 0.2f / s; + float zo = (random->nextFloat() * 2 - 1) * 0.2f / s; + glTranslatef(xo, yo, zo); + } + // 4J - change brought forward from 1.8.2 + float br = SharedConstants::TEXTURE_LIGHTING ? 1.0f : itemEntity->getBrightness(a); + tileRenderer->renderTile(tile, item->getAuxValue(), br); + glPopMatrix(); + } + } + else if (item->getItem()->hasMultipleSpriteLayers()) + { + if (m_bItemFrame) + { + glScalef(1 / 1.95f, 1 / 1.95f, 1 / 1.95f); + glTranslatef(0, -0.05f, 0); + glDisable(GL_LIGHTING); + } + else + { + glScalef(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + } + + bindTexture(TN_GUI_ITEMS); // 4J was "/gui/items.png" + + for (int layer = 0; layer <= 1; layer++) + { + random->setSeed(187); + Icon *icon = item->getItem()->getLayerIcon(item->getAuxValue(), layer); + float brightness = SharedConstants::TEXTURE_LIGHTING ? 1 : itemEntity->getBrightness(a); + if (setColor) + { + int col = Item::items[item->id]->getColor(item, layer); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + glColor4f(red * brightness, g * brightness, b * brightness, 1); + renderItemBillboard(itemEntity, icon, count, a, red * brightness, g * brightness, b * brightness); + } + else + { + renderItemBillboard(itemEntity, icon, count, a, 1, 1, 1); + } + } + } + else + { + if (m_bItemFrame) + { + glScalef(1 / 1.95f, 1 / 1.95f, 1 / 1.95f); + glTranslatef(0, -0.05f, 0); + glDisable(GL_LIGHTING); + } + else + { + glScalef(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + } + + // 4J Stu - For rendering the static compass, we give it a non-zero aux value + if(item->id == Item::compass_Id) item->setAuxValue(255); + Icon *icon = item->getIcon(); + if(item->id == Item::compass_Id) item->setAuxValue(0); + if (item->getIconType() == Icon::TYPE_TERRAIN) + { + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + } + else + { + bindTexture(TN_GUI_ITEMS); // 4J was L"/gui/items.png" + } + if (setColor) + { + int col = Item::items[item->id]->getColor(item,0); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + float brightness = SharedConstants::TEXTURE_LIGHTING ? 1 : itemEntity->getBrightness(a); + + glColor4f(red * brightness, g * brightness, b * brightness, 1); + renderItemBillboard(itemEntity, icon, count, a, red * brightness, g * brightness, b * brightness); + } + else + { + renderItemBillboard(itemEntity, icon, count, a, 1, 1, 1); + } + + } + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); + if( m_bItemFrame ) + { + glEnable(GL_LIGHTING); + } +} + +void ItemRenderer::renderItemBillboard(shared_ptr entity, Icon *icon, int count, float a, float red, float green, float blue) +{ + Tesselator *t = Tesselator::getInstance(); + + if (icon == NULL) icon = entityRenderDispatcher->textures->getMissingIcon(entity->getItem()->getIconType()); + float u0 = icon->getU0(); + float u1 = icon->getU1(); + float v0 = icon->getV0(); + float v1 = icon->getV1(); + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + if (entityRenderDispatcher->options->fancyGraphics) + { + // Consider forcing the mipmap LOD level to use, if this is to be rendered from a larger than standard source texture. + int iconWidth = icon->getWidth(); + int LOD = -1; // Default to not doing anything special with LOD forcing + if( iconWidth == 32 ) + { + LOD = 1; // Force LOD level 1 to achieve texture reads from 256x256 map + } + else if( iconWidth == 64 ) + { + LOD = 2; // Force LOD level 2 to achieve texture reads from 256x256 map + } + RenderManager.StateSetForceLOD(LOD); + + glPushMatrix(); + if (m_bItemFrame) + { + glRotatef(180, 0, 1, 0); + } + else + { + glRotatef(((entity->age + a) / 20.0f + entity->bobOffs) * Mth::RADDEG, 0, 1, 0); + } + + float width = 1 / 16.0f; + float margin = 0.35f / 16.0f; + shared_ptr item = entity->getItem(); + int items = item->count; + + if (items < 2) + { + count = 1; + } + else if (items < 16) + { + count = 2; + } + else if (items < 32) + { + count = 3; + } + else + { + count = 4; + } + + glTranslatef(-xo, -yo, -((width + margin) * count / 2)); + + for (int i = 0; i < count; i++) + { + glTranslatef(0, 0, width + margin); + if (item->getIconType() == Icon::TYPE_TERRAIN && Tile::tiles[item->id] != NULL) + { + bindTexture(TN_TERRAIN); // Was L"/terrain.png"); + } + else + { + bindTexture(TN_GUI_ITEMS); //L"/gui/items.png"); + } + glColor4f(red, green, blue, 1); + // 4J Stu - u coords were swapped in Java + //ItemInHandRenderer::renderItem3D(t, u1, v0, u0, v1, icon->getSourceWidth(), icon->getSourceHeight(), width, false); + ItemInHandRenderer::renderItem3D(t, u0, v0, u1, v1, icon->getSourceWidth(), icon->getSourceHeight(), width, false); + + if (item != NULL && item->isFoil()) + { + glDepthFunc(GL_EQUAL); + glDisable(GL_LIGHTING); + entityRenderDispatcher->textures->bindTexture(TN__BLUR__MISC_GLINT); // was L"%blur%/misc/glint.png"); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_COLOR, GL_ONE); + float br = 0.76f; + glColor4f(0.5f * br, 0.25f * br, 0.8f * br, 1); + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + float ss = 1 / 8.0f; + glScalef(ss, ss, ss); + float sx = Minecraft::currentTimeMillis() % (3000) / (3000.0f) * 8; + glTranslatef(sx, 0, 0); + glRotatef(-50, 0, 0, 1); + + ItemInHandRenderer::renderItem3D(t, 0, 0, 1, 1, 255, 255, width, true); + glPopMatrix(); + glPushMatrix(); + glScalef(ss, ss, ss); + sx = Minecraft::currentTimeMillis() % (3000 + 1873) / (3000 + 1873.0f) * 8; + glTranslatef(-sx, 0, 0); + glRotatef(10, 0, 0, 1); + ItemInHandRenderer::renderItem3D(t, 0, 0, 1, 1, 255, 255, width, true); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + glDepthFunc(GL_LEQUAL); + } + } + + glPopMatrix(); + + RenderManager.StateSetForceLOD(-1); + } + else + { + for (int i = 0; i < count; i++) + { + glPushMatrix(); + if (i > 0) + { + float _xo = (random->nextFloat() * 2 - 1) * 0.3f; + float _yo = (random->nextFloat() * 2 - 1) * 0.3f; + float _zo = (random->nextFloat() * 2 - 1) * 0.3f; + glTranslatef(_xo, _yo, _zo); + } + if (!m_bItemFrame) glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glColor4f(red, green, blue, 1); + t->begin(); + t->normal(0, 1, 0); + t->vertexUV((float)(0 - xo), (float)( 0 - yo), (float)( 0), (float)( u0), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 1 - yo), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(0 - xo), (float)( 1 - yo), (float)( 0), (float)( u0), (float)( v0)); + t->end(); + + glPopMatrix(); + } +} +} + +void ItemRenderer::renderGuiItem(Font *font, Textures *textures, shared_ptr item, float x, float y, float fScale, float fAlpha) +{ + renderGuiItem(font,textures,item,x,y,fScale,fScale,fAlpha, true); +} + +#ifdef _XBOX +extern IDirect3DDevice9 *g_pD3DDevice; +#endif + +// 4J - this used to take x and y as ints, and no scale and alpha - but this interface is now implemented as a wrapper round this more fully featured one +void ItemRenderer::renderGuiItem(Font *font, Textures *textures, shared_ptr item, float x, float y, float fScaleX,float fScaleY, float fAlpha, bool useCompiled) +{ + int itemId = item->id; + int itemAuxValue = item->getAuxValue(); + Icon *itemIcon = item->getIcon(); + + if (item->getIconType() == Icon::TYPE_TERRAIN && TileRenderer::canRender(Tile::tiles[itemId]->getRenderShape())) + { + PIXBeginNamedEvent(0,"3D gui item render %d\n",itemId); + MemSect(31); + textures->bindTexture(TN_TERRAIN);//L"/terrain.png")); + MemSect(0); + + Tile *tile = Tile::tiles[itemId]; + glPushMatrix(); + // 4J - original code left here for reference +#if 0 + glTranslatef((float)(x), (float)(y), 0.0f); + glScalef(fScale, fScale, fScale); + glTranslatef(-2.0f,3.0f, -3.0f + blitOffset); + glScalef(10.0f, 10.0f, 10.0f); + glTranslatef(1.0f, 0.5f, 8.0f); + glScalef(1.0f, 1.0f, -1.0f); + glRotatef(180.0f + 30.0f, 1.0f, 0.0f, 0.0f); + glRotatef(45.0f, 0.0f, 1.0f, 0.0f); +#else + glTranslatef(x, y, 0.0f); // Translate to screen coords + glScalef(16.0f*fScaleX, 16.0f*fScaleY, 1.0f); // Scale to 0 to 16*scale range + glTranslatef(0.5f,0.5f,0.0f); // Translate to 0 to 1 range + glScalef(0.55f,0.55f, -1.0f); // Scale to occupy full -0.5 to 0.5 bounding region (just touching top & bottom) + // 0.55 comes from 1/(1+sqrt(2)/sqrt(3)) which is determined by the angles that the cube is rotated in an orthographic projection + glRotatef(180.0f + 30.0f, 1.0f, 0.0f, 0.0f); // Rotate round x axis (centre at origin) + glRotatef(45.0f, 0.0f, 1.0f, 0.0f); // Rotate round y axis (centre at origin) +#endif + // 4J-PB - pass the alpha value in - the grass block render has the top surface coloured differently to the rest of the block + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); + + glPopMatrix(); + PIXEndNamedEvent(); + } + else if (Item::items[itemId]->hasMultipleSpriteLayers()) + { + PIXBeginNamedEvent(0,"Potion gui item render %d\n",itemIcon); + // special double-layered + glDisable(GL_LIGHTING); + textures->bindTexture(TN_GUI_ITEMS); // "/gui/items.png" + + for (int layer = 0; layer <= 1; layer++) + { + Icon *fillingIcon = Item::items[itemId]->getLayerIcon(itemAuxValue, layer); + + int col = Item::items[itemId]->getColor(item, layer); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + if (setColor) glColor4f(r, g, b, fAlpha); + // scale the x and y by the scale factor + if((fScaleX!=1.0f) ||(fScaleY!=1.0f)) + { + blit(x, y, fillingIcon, 16 * fScaleX, 16 * fScaleY); + } + else + { + blit((int)x, (int)y, fillingIcon, 16, 16); + } + } + glEnable(GL_LIGHTING); + PIXEndNamedEvent(); + } + else + { + PIXBeginNamedEvent(0,"2D gui item render %d\n",itemIcon); + glDisable(GL_LIGHTING); + MemSect(31); + if (item->getIconType() == Icon::TYPE_TERRAIN) + { + textures->bindTexture(TN_TERRAIN);//L"/terrain.png")); + } + else + { + textures->bindTexture(TN_GUI_ITEMS);//L"/gui/items.png")); +#ifdef _XBOX + // 4J - make sure we've got linear sampling on minification here as non-mipmapped things like this currently + // default to having point sampling, which makes very small icons render rather badly + g_pD3DDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); +#endif + + } + MemSect(0); + + if (itemIcon == NULL) + { + itemIcon = textures->getMissingIcon(item->getIconType()); + } + + int col = Item::items[itemId]->getColor(item,0); + float r = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + if (setColor) glColor4f(r, g, b, fAlpha); + + // scale the x and y by the scale factor + if((fScaleX!=1.0f) ||(fScaleY!=1.0f)) + { + blit(x, y, itemIcon, 16 * fScaleX, 16 * fScaleY); + } + else + { + blit((int)x, (int)y, itemIcon, 16, 16); + } + glEnable(GL_LIGHTING); + PIXEndNamedEvent(); + + } + glEnable(GL_CULL_FACE); + +} + +// 4J - original interface, now just a wrapper for preceding overload +void ItemRenderer::renderGuiItem(Font *font, Textures *textures, shared_ptr item, int x, int y) +{ + renderGuiItem(font, textures, item, (float)x, (float)y, 1.0f, 1.0f ); +} + +// 4J - this used to take x and y as ints, and no scale, alpha or foil - but this interface is now implemented as a wrapper round this more fully featured one +void ItemRenderer::renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, float x, float y,float fScale,float fAlpha, bool isFoil) +{ + if(item==NULL) return; + renderAndDecorateItem(font, textures, item, x, y,fScale, fScale, fAlpha, isFoil, true); +} + +// 4J - added isConstantBlended and blendFactor parameters. This is true if the gui item is being rendered from a context where it already has blending enabled to do general interface fading +// (ie from the gui rather than xui). In this case we dno't want to enable/disable blending, and do need to restore the blend state when we are done. +void ItemRenderer::renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, float x, float y,float fScaleX, float fScaleY,float fAlpha, bool isFoil, bool isConstantBlended, bool useCompiled) +{ + if (item == NULL) + { + return; + } + + renderGuiItem(font, textures, item, x, y,fScaleX,fScaleY,fAlpha, useCompiled); + + if (isFoil || item->isFoil()) + { + glDepthFunc(GL_GREATER); + glDisable(GL_LIGHTING); + glDepthMask(false); + textures->bindTexture(TN__BLUR__MISC_GLINT); // 4J was "%blur%/misc/glint.png" + blitOffset -= 50; + if( !isConstantBlended ) glEnable(GL_BLEND); + + glBlendFunc(GL_DST_COLOR, GL_ONE); // 4J - changed blend equation from GL_DST_COLOR, GL_DST_COLOR so we can fade this out + + float blendFactor = isConstantBlended ? Gui::currentGuiBlendFactor : 1.0f; + + glColor4f(0.5f * blendFactor, 0.25f * blendFactor, 0.8f * blendFactor, 1); // 4J - scale back colourisation with blendFactor + // scale the x and y by the scale factor + if((fScaleX!=1.0f) ||(fScaleY!=1.0f)) + { + // 4J Stu - Scales were multiples of 20, making 16 to not overlap in xui scenes + blitGlint(x * 431278612 + y * 32178161, x - 2, y - 2, 16 * fScaleX, 16 * fScaleY); + } + else + { + blitGlint(x * 431278612 + y * 32178161, x - 2, y - 2, 20, 20); + } + glColor4f(1.0f, 1.0f, 1.0f, 1); // 4J added + if( !isConstantBlended ) glDisable(GL_BLEND); + + glDepthMask(true); + blitOffset += 50; + glEnable(GL_LIGHTING); + glDepthFunc(GL_LEQUAL); + + if( isConstantBlended ) glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + } +} + +// 4J - original interface, now just a wrapper for preceding overload +void ItemRenderer::renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, int x, int y) +{ + renderAndDecorateItem( font, textures, item, (float)x, (float)y, 1.0f, 1.0f, item->isFoil() ); +} + +// 4J - a few changes here to get x, y, w, h in as floats (for xui rendering accuracy), and to align +// final pixels to the final screen resolution +void ItemRenderer::blitGlint(int id, float x, float y, float w, float h) +{ + float us = 1.0f / 64.0f / 4; + float vs = 1.0f / 64.0f / 4; + + // 4J - calculate what the pixel coordinates will be in final screen coordinates + float sfx = (float)Minecraft::GetInstance()->width / (float)Minecraft::GetInstance()->width_phys; + float sfy = (float)Minecraft::GetInstance()->height / (float)Minecraft::GetInstance()->height_phys; + float xx0 = x * sfx; + float xx1 = ( x + w ) * sfx; + float yy0 = y * sfy; + float yy1 = ( y + h ) * sfy; + // Round to whole pixels - rounding inwards so that we don't overlap any surrounding graphics + xx0 = ceilf(xx0); + xx1 = floorf(xx1); + yy0 = ceilf(yy0); + yy1 = floorf(yy1); + // Offset by half to get actual centre of pixel - again moving inwards to avoid overlap with surrounding graphics + xx0 += 0.5f; + xx1 -= 0.5f; + yy0 += 0.5f; + yy1 -= 0.5f; + // Convert back to game coordinate space + float xx0f = xx0 / sfx; + float xx1f = xx1 / sfx; + float yy0f = yy0 / sfy; + float yy1f = yy1 / sfy; + + for (int i = 0; i < 2; i++) + { + if (i == 0) glBlendFunc(GL_SRC_COLOR, GL_ONE); + if (i == 1) glBlendFunc(GL_SRC_COLOR, GL_ONE); + float sx = Minecraft::currentTimeMillis() % (3000 + i * 1873) / (3000.0f + i * 1873) * 256; + float sy = 0; + Tesselator *t = Tesselator::getInstance(); + float vv = 4; + if (i == 1) vv = -1; + t->begin(); + t->vertexUV(xx0f, yy1f, blitOffset, (sx + h * vv) * us, (sy + h) * vs); + t->vertexUV(xx1f, yy1f, blitOffset, (sx + w + h * vv) * us, (sy + h) * vs); + t->vertexUV(xx1f, yy0f, blitOffset, (sx + w) * us, (sy + 0) * vs); + t->vertexUV(xx0f, yy0f, blitOffset, (sx + 0) * us, (sy + 0) * vs); + t->end(); + } +} + +void ItemRenderer::renderGuiItemDecorations(Font *font, Textures *textures, shared_ptr item, int x, int y, float fAlpha) +{ + renderGuiItemDecorations(font, textures, item, x, y, L"", fAlpha); +} + +void ItemRenderer::renderGuiItemDecorations(Font *font, Textures *textures, shared_ptr item, int x, int y, const wstring &countText, float fAlpha) +{ + if (item == NULL) + { + return; + } + + glEnable(GL_BLEND); + RenderManager.StateSetBlendFactor(0xffffff |(((unsigned int)(fAlpha * 0xff))<<24)); + glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); + if (item->count > 1 || !countText.empty() || item->GetForceNumberDisplay()) + { + MemSect(31); + wstring amount = countText; + if(amount.empty()) + { + int count = item->count; + if(count > 64) + { + amount = _toString(64) + L"+"; + } + else + { + amount = _toString(item->count); + } + } + MemSect(0); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + font->drawShadow(amount, x + 19 - 2 - font->width(amount), y + 6 + 3, 0xffffff); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + } + + if (item->isDamaged()) + { + int p = (int) Math::round(13.0 - (double) item->getDamageValue() * 13.0 / (double) item->getMaxDamage()); + int cc = (int) Math::round(255.0 - (double) item->getDamageValue() * 255.0 / (double) item->getMaxDamage()); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + + Tesselator *t = Tesselator::getInstance(); + + int ca = (255 - cc) << 16 | (cc) << 8; + int cb = ((255 - cc) / 4) << 16 | (255 / 4) << 8; + fillRect(t, x + 2, y + 13, 13, 2, 0x000000); + fillRect(t, x + 2, y + 13, 12, 1, cb); + fillRect(t, x + 2, y + 13, p, 1, ca); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + glColor4f(1, 1, 1, 1); + } + else if(item->hasPotionStrengthBar()) + { + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + + Tesselator *t = Tesselator::getInstance(); + + fillRect(t, x + 3, y + 13, 11, 2, 0x000000); + //fillRect(t, x + 2, y + 13, 13, 1, 0x1dabc0); + fillRect(t, x + 3, y + 13, m_iPotionStrengthBarWidth[item->GetPotionStrength()], 2, 0x00e1eb); + fillRect(t, x + 2 + 3, y + 13, 1, 2, 0x000000); + fillRect(t, x + 2 + 3+3, y + 13, 1, 2, 0x000000); + fillRect(t, x + 2 + 3+3+3, y + 13, 1, 2, 0x000000); + + + glEnable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + glColor4f(1, 1, 1, 1); + } + glDisable(GL_BLEND); +} + +const int ItemRenderer::m_iPotionStrengthBarWidth[]= +{ + 3,6,9,11 +}; + +void ItemRenderer::fillRect(Tesselator *t, int x, int y, int w, int h, int c) +{ + t->begin(); + t->color(c); + t->vertex((float)(x + 0), (float)( y + 0), (float)( 0)); + t->vertex((float)(x + 0), (float)( y + h), (float)( 0)); + t->vertex((float)(x + w), (float)( y + h), (float)( 0)); + t->vertex((float)(x + w), (float)( y + 0), (float)( 0)); + t->end(); +} + +// 4J - a few changes here to get x, y, w, h in as floats (for xui rendering accuracy), and to align +// final pixels to the final screen resolution +void ItemRenderer::blit(float x, float y, int sx, int sy, float w, float h) +{ + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + + // 4J - calculate what the pixel coordinates will be in final screen coordinates + float sfx = (float)Minecraft::GetInstance()->width / (float)Minecraft::GetInstance()->width_phys; + float sfy = (float)Minecraft::GetInstance()->height / (float)Minecraft::GetInstance()->height_phys; + float xx0 = x * sfx; + float xx1 = ( x + w ) * sfx; + float yy0 = y * sfy; + float yy1 = ( y + h ) * sfy; + // Round to whole pixels - rounding inwards so that we don't overlap any surrounding graphics + xx0 = ceilf(xx0); + xx1 = floorf(xx1); + yy0 = ceilf(yy0); + yy1 = floorf(yy1); + // Offset by half to get actual centre of pixel - again moving inwards to avoid overlap with surrounding graphics + xx0 += 0.5f; + xx1 -= 0.5f; + yy0 += 0.5f; + yy1 -= 0.5f; + // Convert back to game coordinate space + float xx0f = xx0 / sfx; + float xx1f = xx1 / sfx; + float yy0f = yy0 / sfy; + float yy1f = yy1 / sfy; + + // 4J - subtracting 0.5f (actual screen pixels, so need to compensate for physical & game width) from each x & y coordinate to compensate for centre of pixels in directx vs openGL + float f = ( 0.5f * (float)Minecraft::GetInstance()->width ) / (float)Minecraft::GetInstance()->width_phys; + + t->vertexUV(xx0f, yy1f, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + 16) * vs)); + t->vertexUV(xx1f, yy1f, (float)( blitOffset), (float)( (sx + 16) * us), (float)( (sy + 16) * vs)); + t->vertexUV(xx1f, yy0f, (float)( blitOffset), (float)( (sx + 16) * us), (float)( (sy + 0) * vs)); + t->vertexUV(xx0f, yy0f, (float)( blitOffset), (float)( (sx + 0) * us), (float)( (sy + 0) * vs)); + t->end(); +} + +void ItemRenderer::blit(float x, float y, Icon *tex, float w, float h) +{ + Tesselator *t = Tesselator::getInstance(); + t->begin(); + + // 4J - calculate what the pixel coordinates will be in final screen coordinates + float sfx = (float)Minecraft::GetInstance()->width / (float)Minecraft::GetInstance()->width_phys; + float sfy = (float)Minecraft::GetInstance()->height / (float)Minecraft::GetInstance()->height_phys; + float xx0 = x * sfx; + float xx1 = ( x + w ) * sfx; + float yy0 = y * sfy; + float yy1 = ( y + h ) * sfy; + // Round to whole pixels - rounding inwards so that we don't overlap any surrounding graphics + xx0 = ceilf(xx0); + xx1 = floorf(xx1); + yy0 = ceilf(yy0); + yy1 = floorf(yy1); + // Offset by half to get actual centre of pixel - again moving inwards to avoid overlap with surrounding graphics + xx0 += 0.5f; + xx1 -= 0.5f; + yy0 += 0.5f; + yy1 -= 0.5f; + // Convert back to game coordinate space + float xx0f = xx0 / sfx; + float xx1f = xx1 / sfx; + float yy0f = yy0 / sfy; + float yy1f = yy1 / sfy; + + // 4J - subtracting 0.5f (actual screen pixels, so need to compensate for physical & game width) from each x & y coordinate to compensate for centre of pixels in directx vs openGL + float f = ( 0.5f * (float)Minecraft::GetInstance()->width ) / (float)Minecraft::GetInstance()->width_phys; + + t->vertexUV(xx0f, yy1f, blitOffset, tex->getU0(true), tex->getV1(true)); + t->vertexUV(xx1f, yy1f, blitOffset, tex->getU1(true), tex->getV1(true)); + t->vertexUV(xx1f, yy0f, blitOffset, tex->getU1(true), tex->getV0(true)); + t->vertexUV(xx0f, yy0f, blitOffset, tex->getU0(true), tex->getV0(true)); + t->end(); +} diff --git a/Minecraft.Client/ItemRenderer.h b/Minecraft.Client/ItemRenderer.h new file mode 100644 index 0000000..9690f77 --- /dev/null +++ b/Minecraft.Client/ItemRenderer.h @@ -0,0 +1,52 @@ +#pragma once +#include "EntityRenderer.h" + +class Textures; +class ItemInstance; +class Random; +class ItemEntity; + +class ItemRenderer : public EntityRenderer +{ +private: +// TileRenderer *tileRenderer; // 4J - removed - this is shadowing the tilerenderer from entityrenderer + Random *random; + bool m_bItemFrame; +public: + bool setColor; + float blitOffset; + + ItemRenderer(); + virtual ~ItemRenderer(); + virtual void render(shared_ptr _itemEntity, double x, double y, double z, float rot, float a); + +private: + virtual void renderItemBillboard(shared_ptr entity, Icon *icon, int count, float a, float red, float green, float blue); + +public: + // 4J - original 2 interface variants + void renderGuiItem(Font *font, Textures *textures, shared_ptr item, int x, int y); + void renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, int x, int y); + // 4J - new interfaces added + void renderGuiItem(Font *font, Textures *textures, shared_ptr item, float x, float y, float fScale, float fAlpha); + void renderGuiItem(Font *font, Textures *textures, shared_ptr item, float x, float y, float fScaleX,float fScaleY, float fAlpha, bool useCompiled); // 4J Added useCompiled + void renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, float x, float y, float fScale, float fAlpha, bool isFoil); + void renderAndDecorateItem(Font *font, Textures *textures, const shared_ptr item, float x, float y, float fScaleX, float fScaleY, float fAlpha, bool isFoil, bool isConstantBlended, bool useCompiled = true); // 4J - added isConstantBlended and useCompiled + + // 4J Added + virtual void SetItemFrame(bool bSet) {m_bItemFrame=bSet;} + + static const int m_iPotionStrengthBarWidth[4]; + +private: + void blitGlint(int id, float x, float y, float w, float h); // 4J - changed x,y,w,h to floats + +public: + void renderGuiItemDecorations(Font *font, Textures *textures, shared_ptr item, int x, int y, float fAlpha = 1.0f); + void renderGuiItemDecorations(Font *font, Textures *textures, shared_ptr item, int x, int y, const wstring &countText, float fAlpha = 1.0f); +private: + void fillRect(Tesselator *t, int x, int y, int w, int h, int c); +public: + void blit(float x, float y, int sx, int sy, float w, float h); // 4J - changed x,y,w,h to floats + void blit(float x, float y, Icon *tex, float w, float h); +}; diff --git a/Minecraft.Client/ItemSpriteRenderer.cpp b/Minecraft.Client/ItemSpriteRenderer.cpp new file mode 100644 index 0000000..5a1ea62 --- /dev/null +++ b/Minecraft.Client/ItemSpriteRenderer.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "ItemSpriteRenderer.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.item.alchemy.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.h" + +ItemSpriteRenderer::ItemSpriteRenderer(Item *sourceItem, int sourceItemAuxValue /*= 0*/) : EntityRenderer() +{ + this->sourceItem = sourceItem; + this->sourceItemAuxValue = sourceItemAuxValue; +} + +//ItemSpriteRenderer::ItemSpriteRenderer(int icon) : EntityRenderer() +//{ +// this(sourceItem, 0); +//} + +void ItemSpriteRenderer::render(shared_ptr e, double x, double y, double z, float rot, float a) +{ + // the icon is already cached in the item object, so there should not be any performance impact by not caching it here + Icon *icon = sourceItem->getIcon(sourceItemAuxValue); + if (icon == NULL) + { + return; + } + + glPushMatrix(); + + glTranslatef((float) x, (float) y, (float) z); + glEnable(GL_RESCALE_NORMAL); + glScalef(1 / 2.0f, 1 / 2.0f, 1 / 2.0f); + bindTexture(TN_GUI_ITEMS); // 4J - was L"/gui/items.png" + Tesselator *t = Tesselator::getInstance(); + + if (icon == PotionItem::getTexture(PotionItem::THROWABLE_ICON) ) + { + + int col = PotionBrewing::getColorValue((dynamic_pointer_cast(e) )->getPotionValue(), false); + float red = ((col >> 16) & 0xff) / 255.0f; + float g = ((col >> 8) & 0xff) / 255.0f; + float b = ((col) & 0xff) / 255.0f; + + + glColor3f(red, g, b); + glPushMatrix(); + renderIcon(t, PotionItem::getTexture(PotionItem::CONTENTS_ICON)); + glPopMatrix(); + glColor3f(1, 1, 1); + } + + renderIcon(t, icon); + + glDisable(GL_RESCALE_NORMAL); + glPopMatrix(); +} + +void ItemSpriteRenderer::renderIcon(Tesselator *t, Icon *icon) +{ + float u0 = icon->getU0(); + float u1 = icon->getU1(); + float v0 = icon->getV0(); + float v1 = icon->getV1(); + + float r = 1.0f; + float xo = 0.5f; + float yo = 0.25f; + + glRotatef(180 - entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(-entityRenderDispatcher->playerRotX, 1, 0, 0); + t->begin(); + t->normal(0, 1, 0); + t->vertexUV((float)(0 - xo), (float)( 0 - yo), (float)( 0), (float)( u0), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( 0 - yo), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(r - xo), (float)( r - yo), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(0 - xo), (float)( r - yo), (float)( 0), (float)( u0), (float)( v0)); + t->end(); +} \ No newline at end of file diff --git a/Minecraft.Client/ItemSpriteRenderer.h b/Minecraft.Client/ItemSpriteRenderer.h new file mode 100644 index 0000000..e60feec --- /dev/null +++ b/Minecraft.Client/ItemSpriteRenderer.h @@ -0,0 +1,18 @@ +#pragma once +#include "EntityRenderer.h" + +class Item; + +class ItemSpriteRenderer : public EntityRenderer +{ +private: + Item *sourceItem; + int sourceItemAuxValue; +public: + ItemSpriteRenderer(Item *sourceItem, int sourceItemAuxValue = 0); + //ItemSpriteRenderer(Item *icon); + virtual void render(shared_ptr e, double x, double y, double z, float rot, float a); + +private: + void renderIcon(Tesselator *t, Icon *icon); +}; \ No newline at end of file diff --git a/Minecraft.Client/JoinMultiplayerScreen.cpp b/Minecraft.Client/JoinMultiplayerScreen.cpp new file mode 100644 index 0000000..a98e7be --- /dev/null +++ b/Minecraft.Client/JoinMultiplayerScreen.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "JoinMultiplayerScreen.h" +#include "Button.h" +#include "EditBox.h" +#include "Options.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + +JoinMultiplayerScreen::JoinMultiplayerScreen(Screen *lastScreen) +{ + ipEdit = NULL; + this->lastScreen = lastScreen; +} + +void JoinMultiplayerScreen::tick() +{ + ipEdit->tick(); +} + +void JoinMultiplayerScreen::init() +{ + Language *language = Language::getInstance(); + + Keyboard::enableRepeatEvents(true); + buttons.clear(); + buttons.push_back(new Button(0, width / 2 - 100, height / 4 + 24 * 4 + 12, language->getElement(L"multiplayer.connect"))); + buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 5 + 12, language->getElement(L"gui.cancel"))); + wstring ip = replaceAll(minecraft->options->lastMpIp,L"_", L":"); + buttons[0]->active = ip.length() > 0; + + ipEdit = new EditBox(this, font, width / 2 - 100, height / 4 - 10 + 50 + 18, 200, 20, ip); + ipEdit->inFocus = true; + ipEdit->setMaxLength(128); + +} + +void JoinMultiplayerScreen::removed() +{ + Keyboard::enableRepeatEvents(false); +} + +void JoinMultiplayerScreen::buttonClicked(Button *button) +{ + if (!button->active) return; + if (button->id == 1) + { + minecraft->setScreen(lastScreen); + } + else if (button->id == 0) + { + wstring ip = trimString(ipEdit->getValue()); + + minecraft->options->lastMpIp = replaceAll(ip,L":", L"_"); + minecraft->options->save(); + + vector parts = stringSplit(ip,L'L'); + if (ip[0]==L'[') + { + int pos = (int)ip.find(L"]"); + if (pos != wstring::npos) + { + wstring path = ip.substr(1, pos); + wstring port = trimString(ip.substr(pos + 1)); + if (port[0]==L':' && port.length() > 0) + { + port = port.substr(1); + parts.clear(); + parts.push_back(path); + parts.push_back(port); + } + else + { + parts.clear(); + parts.push_back(path); + } + } + + } + if (parts.size() > 2) + { + parts.clear(); + parts.push_back(ip); + } + + // 4J - TODO +// minecraft->setScreen(new ConnectScreen(minecraft, parts[0], parts.length > 1 ? parseInt(parts[1], 25565) : 25565)); + } +} + +int JoinMultiplayerScreen::parseInt(const wstring& str, int def) +{ + return _fromString(str); +} + +void JoinMultiplayerScreen::keyPressed(wchar_t ch, int eventKey) +{ + ipEdit->keyPressed(ch, eventKey); + + if (ch == 13) + { + buttonClicked(buttons[0]); + } + buttons[0]->active = ipEdit->getValue().length() > 0; +} + +void JoinMultiplayerScreen::mouseClicked(int x, int y, int buttonNum) +{ + Screen::mouseClicked(x, y, buttonNum); + + ipEdit->mouseClicked(x, y, buttonNum); +} + +void JoinMultiplayerScreen::render(int xm, int ym, float a) +{ + Language *language = Language::getInstance(); + + // fill(0, 0, width, height, 0x40000000); + renderBackground(); + + drawCenteredString(font, language->getElement(L"multiplayer.title"), width / 2, height / 4 - 60 + 20, 0xffffff); + drawString(font, language->getElement(L"multiplayer.info1"), width / 2 - 140, height / 4 - 60 + 60 + 9 * 0, 0xa0a0a0); + drawString(font, language->getElement(L"multiplayer.info2"), width / 2 - 140, height / 4 - 60 + 60 + 9 * 1, 0xa0a0a0); + drawString(font, language->getElement(L"multiplayer.ipinfo"), width / 2 - 140, height / 4 - 60 + 60 + 9 * 4, 0xa0a0a0); + + ipEdit->render(); + + Screen::render(xm, ym, a); + +} \ No newline at end of file diff --git a/Minecraft.Client/JoinMultiplayerScreen.h b/Minecraft.Client/JoinMultiplayerScreen.h new file mode 100644 index 0000000..99b7707 --- /dev/null +++ b/Minecraft.Client/JoinMultiplayerScreen.h @@ -0,0 +1,26 @@ +#pragma once +#include "Screen.h" +class EditBox; +class Button; + +class JoinMultiplayerScreen : public Screen +{ +private: + Screen *lastScreen; + EditBox *ipEdit; + +public: + JoinMultiplayerScreen(Screen *lastScreen); + virtual void tick(); + virtual void init(); + virtual void removed(); +protected: + virtual void buttonClicked(Button *button); +private: + virtual int parseInt(const wstring& str, int def); +protected: + virtual void keyPressed(wchar_t ch, int eventKey); + virtual void mouseClicked(int x, int y, int buttonNum); +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/KeyMapping.cpp b/Minecraft.Client/KeyMapping.cpp new file mode 100644 index 0000000..9e93041 --- /dev/null +++ b/Minecraft.Client/KeyMapping.cpp @@ -0,0 +1,8 @@ +#include "stdafx.h" +#include "KeyMapping.h" + +KeyMapping::KeyMapping(const wstring& name, int key) +{ + this->name = name; + this->key = key; +} \ No newline at end of file diff --git a/Minecraft.Client/KeyMapping.h b/Minecraft.Client/KeyMapping.h new file mode 100644 index 0000000..45be54a --- /dev/null +++ b/Minecraft.Client/KeyMapping.h @@ -0,0 +1,10 @@ +#pragma once +using namespace std; +// 4J Stu - Not updated to 1.8.2 as we don't use this +class KeyMapping +{ +public: + wstring name; + int key; + KeyMapping(const wstring& name, int key); +}; \ No newline at end of file diff --git a/Minecraft.Client/KeyboardMouseInput.cpp b/Minecraft.Client/KeyboardMouseInput.cpp new file mode 100644 index 0000000..9824581 --- /dev/null +++ b/Minecraft.Client/KeyboardMouseInput.cpp @@ -0,0 +1,294 @@ +#include "stdafx.h" +#include "KeyboardMouseInput.h" +#include + +KeyboardMouseInput g_KBMInput; + +extern HWND g_hWnd; + +// coded by notpies fr +void KeyboardMouseInput::Init() +{ + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseX = 0; + m_mouseY = 0; + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheel = 0; + m_mouseWheelAccum = 0; + m_mouseGrabbed = false; + m_windowFocused = true; + m_hasInput = false; + + RAWINPUTDEVICE rid; + rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE + rid.dwFlags = 0; + rid.hwndTarget = g_hWnd; + RegisterRawInputDevices(&rid, 1, sizeof(rid)); + + if (g_hWnd) + { + while (ShowCursor(FALSE) >= 0) {} + } +} + +void KeyboardMouseInput::ClearAllState() +{ + memset(m_keyDown, 0, sizeof(m_keyDown)); + memset(m_keyDownPrev, 0, sizeof(m_keyDownPrev)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + memset(m_keyPressed, 0, sizeof(m_keyPressed)); + memset(m_keyReleased, 0, sizeof(m_keyReleased)); + memset(m_mouseButtonDown, 0, sizeof(m_mouseButtonDown)); + memset(m_mouseButtonDownPrev, 0, sizeof(m_mouseButtonDownPrev)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressed, 0, sizeof(m_mouseBtnPressed)); + memset(m_mouseBtnReleased, 0, sizeof(m_mouseBtnReleased)); + m_mouseDeltaX = 0; + m_mouseDeltaY = 0; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + m_mouseWheel = 0; + m_mouseWheelAccum = 0; +} + +void KeyboardMouseInput::Tick() +{ + memcpy(m_keyDownPrev, m_keyDown, sizeof(m_keyDown)); + memcpy(m_mouseButtonDownPrev, m_mouseButtonDown, sizeof(m_mouseButtonDown)); + + memcpy(m_keyPressed, m_keyPressedAccum, sizeof(m_keyPressedAccum)); + memcpy(m_keyReleased, m_keyReleasedAccum, sizeof(m_keyReleasedAccum)); + memset(m_keyPressedAccum, 0, sizeof(m_keyPressedAccum)); + memset(m_keyReleasedAccum, 0, sizeof(m_keyReleasedAccum)); + + memcpy(m_mouseBtnPressed, m_mouseBtnPressedAccum, sizeof(m_mouseBtnPressedAccum)); + memcpy(m_mouseBtnReleased, m_mouseBtnReleasedAccum, sizeof(m_mouseBtnReleasedAccum)); + memset(m_mouseBtnPressedAccum, 0, sizeof(m_mouseBtnPressedAccum)); + memset(m_mouseBtnReleasedAccum, 0, sizeof(m_mouseBtnReleasedAccum)); + + m_mouseDeltaX = m_mouseDeltaAccumX; + m_mouseDeltaY = m_mouseDeltaAccumY; + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + + m_mouseWheel = m_mouseWheelAccum; + m_mouseWheelAccum = 0; + + m_hasInput = (m_mouseDeltaX != 0 || m_mouseDeltaY != 0 || m_mouseWheel != 0); + if (!m_hasInput) + { + for (int i = 0; i < MAX_KEYS; i++) + { + if (m_keyDown[i]) { m_hasInput = true; break; } + } + } + if (!m_hasInput) + { + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + { + if (m_mouseButtonDown[i]) { m_hasInput = true; break; } + } + } + + if (m_mouseGrabbed && g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + } +} + +void KeyboardMouseInput::OnKeyDown(int vkCode) +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (!m_keyDown[vkCode]) + m_keyPressedAccum[vkCode] = true; + m_keyDown[vkCode] = true; + } +} + +void KeyboardMouseInput::OnKeyUp(int vkCode) +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + { + if (m_keyDown[vkCode]) + m_keyReleasedAccum[vkCode] = true; + m_keyDown[vkCode] = false; + } +} + +void KeyboardMouseInput::OnMouseButtonDown(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (!m_mouseButtonDown[button]) + m_mouseBtnPressedAccum[button] = true; + m_mouseButtonDown[button] = true; + } +} + +void KeyboardMouseInput::OnMouseButtonUp(int button) +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + { + if (m_mouseButtonDown[button]) + m_mouseBtnReleasedAccum[button] = true; + m_mouseButtonDown[button] = false; + } +} + +void KeyboardMouseInput::OnMouseMove(int x, int y) +{ + m_mouseX = x; + m_mouseY = y; +} + +void KeyboardMouseInput::OnMouseWheel(int delta) +{ + m_mouseWheelAccum += delta; +} + +void KeyboardMouseInput::OnRawMouseDelta(int dx, int dy) +{ + m_mouseDeltaAccumX += dx; + m_mouseDeltaAccumY += dy; +} + +bool KeyboardMouseInput::IsKeyDown(int vkCode) const +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyDown[vkCode]; + return false; +} + +bool KeyboardMouseInput::IsKeyPressed(int vkCode) const +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyPressed[vkCode]; + return false; +} + +bool KeyboardMouseInput::IsKeyReleased(int vkCode) const +{ + if (vkCode >= 0 && vkCode < MAX_KEYS) + return m_keyReleased[vkCode]; + return false; +} + +bool KeyboardMouseInput::IsMouseButtonDown(int button) const +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseButtonDown[button]; + return false; +} + +bool KeyboardMouseInput::IsMouseButtonPressed(int button) const +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnPressed[button]; + return false; +} + +bool KeyboardMouseInput::IsMouseButtonReleased(int button) const +{ + if (button >= 0 && button < MAX_MOUSE_BUTTONS) + return m_mouseBtnReleased[button]; + return false; +} + +void KeyboardMouseInput::SetMouseGrabbed(bool grabbed) +{ + if (m_mouseGrabbed == grabbed) + return; + + m_mouseGrabbed = grabbed; + if (grabbed && g_hWnd) + { + RECT rc; + GetClientRect(g_hWnd, &rc); + POINT center; + center.x = (rc.right - rc.left) / 2; + center.y = (rc.bottom - rc.top) / 2; + ClientToScreen(g_hWnd, ¢er); + SetCursorPos(center.x, center.y); + + m_mouseDeltaAccumX = 0; + m_mouseDeltaAccumY = 0; + } +} + +static void ClipCursorToWindow(HWND hWnd) +{ + if (!hWnd) return; + RECT rc; + GetClientRect(hWnd, &rc); + POINT topLeft = { rc.left, rc.top }; + POINT bottomRight = { rc.right, rc.bottom }; + ClientToScreen(hWnd, &topLeft); + ClientToScreen(hWnd, &bottomRight); + RECT clipRect = { topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }; + ClipCursor(&clipRect); +} + +void KeyboardMouseInput::SetWindowFocused(bool focused) +{ + m_windowFocused = focused; + if (focused) + { + while (ShowCursor(FALSE) >= 0) {} + ClipCursorToWindow(g_hWnd); + } + else + { + while (ShowCursor(TRUE) < 0) {} + ClipCursor(NULL); + } +} + +float KeyboardMouseInput::GetMoveX() const +{ + float x = 0.0f; + if (m_keyDown[KEY_LEFT]) x += 1.0f; + if (m_keyDown[KEY_RIGHT]) x -= 1.0f; + return x; +} + +float KeyboardMouseInput::GetMoveY() const +{ + float y = 0.0f; + if (m_keyDown[KEY_FORWARD]) y += 1.0f; + if (m_keyDown[KEY_BACKWARD]) y -= 1.0f; + return y; +} + +float KeyboardMouseInput::GetLookX(float sensitivity) const +{ + return (float)m_mouseDeltaX * sensitivity; +} + +float KeyboardMouseInput::GetLookY(float sensitivity) const +{ + return (float)(-m_mouseDeltaY) * sensitivity; +} diff --git a/Minecraft.Client/KeyboardMouseInput.h b/Minecraft.Client/KeyboardMouseInput.h new file mode 100644 index 0000000..098e6e9 --- /dev/null +++ b/Minecraft.Client/KeyboardMouseInput.h @@ -0,0 +1,105 @@ +#pragma once + +#include + +class KeyboardMouseInput +{ +public: + static const int MAX_KEYS = 256; + + static const int MOUSE_LEFT = 0; + static const int MOUSE_RIGHT = 1; + static const int MOUSE_MIDDLE = 2; + static const int MAX_MOUSE_BUTTONS = 3; + + static const int KEY_FORWARD = 'W'; + static const int KEY_BACKWARD = 'S'; + static const int KEY_LEFT = 'A'; + static const int KEY_RIGHT = 'D'; + static const int KEY_JUMP = VK_SPACE; + static const int KEY_SNEAK = VK_LSHIFT; + static const int KEY_INVENTORY = 'E'; + static const int KEY_DROP = 'Q'; + static const int KEY_CRAFTING = VK_TAB; + static const int KEY_PAUSE = VK_ESCAPE; + static const int KEY_THIRD_PERSON = VK_F5; + static const int KEY_DEBUG_INFO = VK_F3; + + void Init(); + void Tick(); + void ClearAllState(); + + void OnKeyDown(int vkCode); + void OnKeyUp(int vkCode); + void OnMouseButtonDown(int button); + void OnMouseButtonUp(int button); + void OnMouseMove(int x, int y); + void OnMouseWheel(int delta); + void OnRawMouseDelta(int dx, int dy); + + bool IsKeyDown(int vkCode) const; + bool IsKeyPressed(int vkCode) const; + bool IsKeyReleased(int vkCode) const; + + bool IsMouseButtonDown(int button) const; + bool IsMouseButtonPressed(int button) const; + bool IsMouseButtonReleased(int button) const; + + int GetMouseX() const { return m_mouseX; } + int GetMouseY() const { return m_mouseY; } + + int GetMouseDeltaX() const { return m_mouseDeltaX; } + int GetMouseDeltaY() const { return m_mouseDeltaY; } + + int GetMouseWheel() const { return m_mouseWheel; } + + void SetMouseGrabbed(bool grabbed); + bool IsMouseGrabbed() const { return m_mouseGrabbed; } + + void SetWindowFocused(bool focused); + bool IsWindowFocused() const { return m_windowFocused; } + + bool HasAnyInput() const { return m_hasInput; } + + float GetMoveX() const; + float GetMoveY() const; + + float GetLookX(float sensitivity) const; + float GetLookY(float sensitivity) const; + +private: + bool m_keyDown[MAX_KEYS]; + bool m_keyDownPrev[MAX_KEYS]; + + bool m_keyPressedAccum[MAX_KEYS]; + bool m_keyReleasedAccum[MAX_KEYS]; + bool m_keyPressed[MAX_KEYS]; + bool m_keyReleased[MAX_KEYS]; + + bool m_mouseButtonDown[MAX_MOUSE_BUTTONS]; + bool m_mouseButtonDownPrev[MAX_MOUSE_BUTTONS]; + + bool m_mouseBtnPressedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleasedAccum[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnPressed[MAX_MOUSE_BUTTONS]; + bool m_mouseBtnReleased[MAX_MOUSE_BUTTONS]; + + int m_mouseX; + int m_mouseY; + + int m_mouseDeltaX; + int m_mouseDeltaY; + int m_mouseDeltaAccumX; + int m_mouseDeltaAccumY; + + int m_mouseWheel; + int m_mouseWheelAccum; + + bool m_mouseGrabbed; + + bool m_windowFocused; + + bool m_hasInput; +}; + +extern KeyboardMouseInput g_KBMInput; diff --git a/Minecraft.Client/LargeChestModel.cpp b/Minecraft.Client/LargeChestModel.cpp new file mode 100644 index 0000000..fad7aae --- /dev/null +++ b/Minecraft.Client/LargeChestModel.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "LargeChestModel.h" +#include "ModelPart.h" + +LargeChestModel::LargeChestModel() +{ + lid = ((new ModelPart(this, 0, 0)))->setTexSize(128, 64); + lid->addBox(0.0f, -5.0f, -14.0f, 14+16, 5, 14, 0.0f); + lid->x = 1; + lid->y = 7; + lid->z = 15; + + lock = ((new ModelPart(this, 0, 0)))->setTexSize(128, 64); + lock->addBox(-1.0f, -2.0f, -15.0f, 2, 4, 1, 0.0f); + lock->x = 8+8; + lock->y = 7; + lock->z = 15; + + bottom = ((new ModelPart(this, 0, 19)))->setTexSize(128, 64); + bottom->addBox(0.0f, 0.0f, 0.0f, 14+16, 10, 14, 0.0f); + bottom->x = 1; + bottom->y = 6; + bottom->z = 1; + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + lid->compile(1.0f/16.0f); + lock->compile(1.0f/16.0f); + bottom->compile(1.0f/16.0f); +} \ No newline at end of file diff --git a/Minecraft.Client/LargeChestModel.h b/Minecraft.Client/LargeChestModel.h new file mode 100644 index 0000000..a2dcb99 --- /dev/null +++ b/Minecraft.Client/LargeChestModel.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ChestModel.h" + +class LargeChestModel : public ChestModel +{ +public: + LargeChestModel(); +}; \ No newline at end of file diff --git a/Minecraft.Client/LavaParticle.cpp b/Minecraft.Client/LavaParticle.cpp new file mode 100644 index 0000000..ac0608e --- /dev/null +++ b/Minecraft.Client/LavaParticle.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "LavaParticle.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" + +LavaParticle::LavaParticle(Level *level, double x, double y, double z) : Particle(level, x, y, z, 0, 0, 0) +{ + xd *= 0.8f; + yd *= 0.8f; + zd *= 0.8f; + yd = random->nextFloat() * 0.4f + 0.05f; + + rCol = gCol = bCol = 1; + size *= (random->nextFloat() * 2 + 0.2f); + oSize = size; + + lifetime = (int) (16 / (Math::random() * 0.8 + 0.2)); + noPhysics = false; + setMiscTex(49); +} + +// 4J - brought forward from 1.8.2 +int LavaParticle::getLightColor(float a) +{ + float l = (age + a) / lifetime; + if (l < 0) l = 0; + if (l > 1) l = 1; + int br = Particle::getLightColor(a); + + int br1 = 15 * 16; + int br2 = (br >> 16) & 0xff; + return br1 | br2 << 16; +} + +float LavaParticle::getBrightness(float a) +{ + return 1; +} + +void LavaParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float s = (age + a) / (float) lifetime; + size = oSize * (1 - s*s); + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void LavaParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + float odds = age / (float) lifetime; + if (random->nextFloat() > odds) level->addParticle(eParticleType_smoke, x, y, z, xd, yd, zd); + + yd -= 0.03; + move(xd, yd, zd); + xd *= 0.999f; + yd *= 0.999f; + zd *= 0.999f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } +} diff --git a/Minecraft.Client/LavaParticle.h b/Minecraft.Client/LavaParticle.h new file mode 100644 index 0000000..235edcf --- /dev/null +++ b/Minecraft.Client/LavaParticle.h @@ -0,0 +1,16 @@ +#pragma once +#include "Particle.h" + +class LavaParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_LAVAPARTICLE; } +private: + float oSize; +public: + LavaParticle(Level *level, double x, double y, double z); + virtual int getLightColor(float a); // 4J - brought forward from 1.8.2 + virtual float getBrightness(float a); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); +}; diff --git a/Minecraft.Client/LavaSlimeModel.cpp b/Minecraft.Client/LavaSlimeModel.cpp new file mode 100644 index 0000000..427b954 --- /dev/null +++ b/Minecraft.Client/LavaSlimeModel.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "LavaSlimeModel.h" +#include "ModelPart.h" +#include "..\Minecraft.World\LavaSlime.h" + + +LavaSlimeModel::LavaSlimeModel() +{ + for (int i = 0; i < BODYCUBESLENGTH; i++) + { + int u = 0; + int v = i; + if (i == 2) + { + u = 24; + v = 10; + } + else if (i == 3) + { + u = 24; + v = 19; + } + bodyCubes[i] = new ModelPart(this, u, v); + bodyCubes[i]->addBox(-4.0f, 16.0f + (float)i, -4.0f, 8, 1, 8); + } + + insideCube = new ModelPart(this, 0, 16); + insideCube->addBox(-2, 16 + 2, -2, 4, 4, 4); + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + insideCube->compile(1.0f/16.0f); + for( int i = 0; i < BODYCUBESLENGTH; i++ ) + { + bodyCubes[i]->compile(1.0f/16.0f); + } +} + +int LavaSlimeModel::getModelVersion() +{ + return 5; +} + +void LavaSlimeModel::prepareMobModel(shared_ptr mob, float time, float r, float a) +{ + shared_ptr lavaSlime = dynamic_pointer_cast(mob); + + float slimeSquish = (lavaSlime->oSquish + (lavaSlime->squish - lavaSlime->oSquish) * a); + if (slimeSquish < 0) + { + slimeSquish = 0.0f; + } + + for (int i = 0; i < BODYCUBESLENGTH; i++) + { + bodyCubes[i]->y = -(4 - i) * slimeSquish * 1.7f; + } +} + +void LavaSlimeModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + setupAnim(time, r, bob, yRot, xRot, scale); + + insideCube->render(scale, usecompiled); + for (int i = 0; i < BODYCUBESLENGTH; i++) + { + bodyCubes[i]->render(scale, usecompiled); + } + +} + diff --git a/Minecraft.Client/LavaSlimeModel.h b/Minecraft.Client/LavaSlimeModel.h new file mode 100644 index 0000000..44bd166 --- /dev/null +++ b/Minecraft.Client/LavaSlimeModel.h @@ -0,0 +1,15 @@ +#pragma once +#include "Model.h" + +class LavaSlimeModel : public Model +{ + static const int BODYCUBESLENGTH=8; + ModelPart *bodyCubes[BODYCUBESLENGTH]; + ModelPart *insideCube; + +public: + LavaSlimeModel(); + int getModelVersion(); + virtual void prepareMobModel(shared_ptr mob, float time, float r, float a); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +}; diff --git a/Minecraft.Client/LavaSlimeRenderer.cpp b/Minecraft.Client/LavaSlimeRenderer.cpp new file mode 100644 index 0000000..3d2cbf4 --- /dev/null +++ b/Minecraft.Client/LavaSlimeRenderer.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "LavaSlimeModel.h" +#include "LavaSlimeRenderer.h" + +LavaSlimeRenderer::LavaSlimeRenderer() : MobRenderer(new LavaSlimeModel(), .25f) +{ + this->modelVersion = ((LavaSlimeModel *) model)->getModelVersion(); +} + +void LavaSlimeRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type LavaSlime rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + int modelVersion = ((LavaSlimeModel *) model)->getModelVersion(); + if (modelVersion != this->modelVersion) + { + this->modelVersion = modelVersion; + model = new LavaSlimeModel(); + app.DebugPrintf("new lava slime model\n"); + } + MobRenderer::render(mob, x, y, z, rot, a); +} + +void LavaSlimeRenderer::scale(shared_ptr _slime, float a) +{ + // 4J - original version used generics and thus had an input parameter of type LavaSlime rather than shared_ptr we have here - + // do some casting around instead + shared_ptr slime = dynamic_pointer_cast(_slime); + int size = slime->getSize(); + float ss = (slime->oSquish + (slime->squish - slime->oSquish) * a) / (size * 0.5f + 1); + float w = 1 / (ss + 1); + float s = size; + glScalef(w * s, 1 / w * s, w * s); +} \ No newline at end of file diff --git a/Minecraft.Client/LavaSlimeRenderer.h b/Minecraft.Client/LavaSlimeRenderer.h new file mode 100644 index 0000000..b0c44d1 --- /dev/null +++ b/Minecraft.Client/LavaSlimeRenderer.h @@ -0,0 +1,17 @@ +#pragma once + +#include "MobRenderer.h" + +class LavaSlimeRenderer : public MobRenderer +{ +private: + int modelVersion; + +public: + LavaSlimeRenderer(); + + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); + +protected: + virtual void scale(shared_ptr _slime, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp new file mode 100644 index 0000000..9836007 --- /dev/null +++ b/Minecraft.Client/LevelRenderer.cpp @@ -0,0 +1,3644 @@ +#include "stdafx.h" +#include "LevelRenderer.h" +#include "Textures.h" +#include "Tesselator.h" +#include "Chunk.h" +#include "EntityRenderDispatcher.h" +#include "TileEntityRenderDispatcher.h" +#include "DistanceChunkSorter.h" +#include "DirtyChunkSorter.h" +#include "MobSkinTextureProcessor.h" +#include "MobSkinMemTextureProcessor.h" +#include "GameRenderer.h" +#include "BubbleParticle.h" +#include "SmokeParticle.h" +#include "NoteParticle.h" +#include "NetherPortalParticle.h" +#include "EnderParticle.h" +#include "ExplodeParticle.h" +#include "FlameParticle.h" +#include "LavaParticle.h" +#include "FootstepParticle.h" +#include "SplashParticle.h" +#include "SmokeParticle.h" +#include "RedDustParticle.h" +#include "BreakingItemParticle.h" +#include "SnowShovelParticle.h" +#include "BreakingItemParticle.h" +#include "HeartParticle.h" +#include "HugeExplosionParticle.h" +#include "HugeExplosionSeedParticle.h" +#include "SuspendedParticle.h" +#include "SuspendedTownParticle.h" +#include "CritParticle2.h" +#include "TerrainParticle.h" +#include "SpellParticle.h" +#include "DripParticle.h" +#include "EchantmentTableParticle.h" +#include "DragonBreathParticle.h" +#include "Lighting.h" +#include "Options.h" +#include "MultiPlayerChunkCache.h" +#include "..\Minecraft.World\ParticleTypes.h" +#include "..\Minecraft.World\IntCache.h" +#include "..\Minecraft.World\IntBuffer.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "MultiplayerLocalPlayer.h" +#include "MultiPlayerLevel.h" +#include "..\Minecraft.World\SoundTypes.h" +#include "FrustumCuller.h" +#include "..\Minecraft.World\BasicTypeContainers.h" + +#ifdef __PS3__ +#include "PS3\SPU_Tasks\LevelRenderer_cull\LevelRenderer_cull.h" +#include "PS3\SPU_Tasks\LevelRenderer_FindNearestChunk\LevelRenderer_FindNearestChunk.h" +#include "C4JSpursJob.h" + +static LevelRenderer_cull_DataIn g_cullDataIn[4] __attribute__((__aligned__(16))); +static LevelRenderer_FindNearestChunk_DataIn g_findNearestChunkDataIn __attribute__((__aligned__(16))); +#endif + +const unsigned int HALO_RING_RADIUS = 100; + +#ifdef _LARGE_WORLDS +Chunk LevelRenderer::permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; +C4JThread *LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; +C4JThread::EventArray *LevelRenderer::s_rebuildCompleteEvents; +C4JThread::Event *LevelRenderer::s_activationEventA[MAX_CHUNK_REBUILD_THREADS]; + +// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side +// so that we can render the "infinite" sea at the edges. Currently defined as: +const int overworldSize = LEVEL_MAX_WIDTH + LevelRenderer::PLAYER_VIEW_DISTANCE + LevelRenderer::PLAYER_VIEW_DISTANCE; +const int netherSize = HELL_LEVEL_MAX_WIDTH + 2; // 4J Stu - The plus 2 is really just to make our total chunk count a multiple of 8 for the flags, we will never see these in the nether +const int endSize = END_LEVEL_MAX_WIDTH; +const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { overworldSize, netherSize, endSize }; +const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (overworldSize * overworldSize * CHUNK_Y_COUNT) , (overworldSize * overworldSize * CHUNK_Y_COUNT) + ( netherSize * netherSize * CHUNK_Y_COUNT ) }; +#else +// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side +// so that we can render the "infinite" sea at the edges. Currently defined as: +// Dimension idx 0 (overworld) : 80 ( = 54 + 13 + 13 ) +// Dimension idx 1 (nether) : 44 ( = 18 + 13 + 13 ) +// Dimension idx 2 (the end) : 44 ( = 18 + 13 + 13 ) + +const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { 80, 44, 44 }; + +// Linked directly to the sizes in the previous array, these next values dictate the start offset for each dimension index into the global array for these things. +// Each dimension uses MAX_LEVEL_RENDER_SIZE[i]^2 * 8 indices, as a MAX_LEVEL_RENDER_SIZE * MAX_LEVEL_RENDER_SIZE * 8 sized cube of references. + +const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (80 * 80 * CHUNK_Y_COUNT) , (80 * 80 * CHUNK_Y_COUNT) + ( 44 * 44 * CHUNK_Y_COUNT ) }; +#endif + +LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) +{ + breakingTextures = NULL; + + for( int i = 0; i < 4; i++ ) + { + level[i] = NULL; + tileRenderer[i] = NULL; + xOld[i] = -9999; + yOld[i] = -9999; + zOld[i] = -9999; + } + xChunks= yChunks= zChunks = 0; + chunkLists = 0; + + ticks = 0; + starList= skyList= darkList = 0; + xMinChunk= yMinChunk= zMinChunk = 0; + xMaxChunk= yMaxChunk= zMaxChunk = 0; + lastViewDistance = -1; + noEntityRenderFrames = 2; + totalEntities = 0; + renderedEntities = 0; + culledEntities = 0; + chunkFixOffs = 0; + frame = 0; + repeatList = MemoryTracker::genLists(1); + + destroyProgress = 0.0f; + + totalChunks= offscreenChunks= occludedChunks= renderedChunks= emptyChunks = 0; + for( int i = 0; i < 4; i++ ) + { + // sortedChunks[i] = NULL; // 4J - removed - not sorting our chunks anymore + chunks[i] = ClipChunkArray(); + lastPlayerCount[i] = 0; + } + + InitializeCriticalSection(&m_csDirtyChunks); + InitializeCriticalSection(&m_csRenderableTileEntities); +#ifdef _LARGE_WORLDS + InitializeCriticalSection(&m_csChunkFlags); +#endif + + dirtyChunkPresent = false; + lastDirtyChunkFound = 0; + + this->mc = mc; + this->textures = textures; + + chunkLists = MemoryTracker::genLists(getGlobalChunkCount()*2); // *2 here is because there is one renderlist per chunk here for each of the opaque & transparent layers + globalChunkFlags = new unsigned char[getGlobalChunkCount()]; + memset(globalChunkFlags, 0, getGlobalChunkCount()); + + starList = MemoryTracker::genLists(4); + + glPushMatrix(); + glNewList(starList, GL_COMPILE); + renderStars(); + glEndList(); + + // 4J added - create geometry for rendering clouds + createCloudMesh(); + + glPopMatrix(); + + + + Tesselator *t = Tesselator::getInstance(); + skyList = starList + 1; + glNewList(skyList, GL_COMPILE); + glDepthMask(false); // 4J - added to get depth mask disabled within the command buffer + float yy; + int s = 64; + int d = (256 / s) + 2; + yy = (float) (16); + for (int xx = -s * d; xx <= s * d; xx += s) + { + for (int zz = -s * d; zz <= s * d; zz += s) + { + t->begin(); + t->vertex((float)(xx + 0), (float)( yy), (float)( zz + 0)); + t->vertex((float)(xx + s), (float)( yy), (float)( zz + 0)); + t->vertex((float)(xx + s), (float)( yy), (float)( zz + s)); + t->vertex((float)(xx + 0), (float)( yy), (float)( zz + s)); + t->end(); + } + } + glEndList(); + + darkList = starList + 2; + glNewList(darkList, GL_COMPILE); + yy = -(float) (16); + t->begin(); + for (int xx = -s * d; xx <= s * d; xx += s) + { + for (int zz = -s * d; zz <= s * d; zz += s) + { + t->vertex((float)(xx + s), (float)( yy), (float)( zz + 0)); + t->vertex((float)(xx + 0), (float)( yy), (float)( zz + 0)); + t->vertex((float)(xx + 0), (float)( yy), (float)( zz + s)); + t->vertex((float)(xx + s), (float)( yy), (float)( zz + s)); + } + } + t->end(); + glEndList(); + + // HALO ring for the texture pack + { + const unsigned int ARC_SEGMENTS = 50; + const float VERTICAL_OFFSET = HALO_RING_RADIUS * 999/1000; // How much we raise the circle origin to make the circle curve back towards us + const int WIDTH = 10; + const float ARC_RADIANS = 2.0f*PI/ARC_SEGMENTS; + const float HALF_ARC_SEG = ARC_SEGMENTS/2; + const float WIDE_ARC_SEGS = ARC_SEGMENTS/8; + const float WIDE_ARC_SEGS_SQR = WIDE_ARC_SEGS * WIDE_ARC_SEGS; + + float u = 0.0f; + float width = WIDTH; + + haloRingList = starList + 3; + glNewList(haloRingList, GL_COMPILE); + t->begin(GL_TRIANGLE_STRIP); + t->color(0xffffff); + + for(unsigned int i = 0; i <= ARC_SEGMENTS; ++i) + { + float DIFF = abs(i - HALF_ARC_SEG); + if(DIFF<(HALF_ARC_SEG-WIDE_ARC_SEGS)) DIFF = 0; + else DIFF-=(HALF_ARC_SEG-WIDE_ARC_SEGS); + width = 1 + ( (DIFF * DIFF) / (WIDE_ARC_SEGS_SQR) ) * WIDTH; + t->vertexUV((HALO_RING_RADIUS * cos(i*ARC_RADIANS)) - VERTICAL_OFFSET, (HALO_RING_RADIUS * sin(i*ARC_RADIANS)), 0-width, u, 0); + t->vertexUV((HALO_RING_RADIUS * cos(i*ARC_RADIANS)) - VERTICAL_OFFSET, (HALO_RING_RADIUS * sin(i*ARC_RADIANS)), 0+width, u, 1); + //--u; + u -= 0.25; + } + t->end(); + glEndList(); + } + + Chunk::levelRenderer = this; + + destroyedTileManager = new DestroyedTileManager(); + + dirtyChunksLockFreeStack.Initialize(); +#ifdef __PS3__ + m_jobPort_CullSPU = new C4JSpursJobQueue::Port("C4JSpursJob_LevelRenderer_cull"); + m_jobPort_FindNearestChunk = new C4JSpursJobQueue::Port("C4JSpursJob_LevelRenderer_FindNearestChunk"); +#endif // __PS3__ +} + +void LevelRenderer::renderStars() +{ + Random random = Random(10842); + Tesselator *t = Tesselator::getInstance(); + t->begin(); + for (int i = 0; i < 1500; i++) + { + double x = random.nextFloat() * 2 - 1; + double y = random.nextFloat() * 2 - 1; + double z = random.nextFloat() * 2 - 1; + double ss = 0.15f + random.nextFloat() * 0.10f; + double d = x * x + y * y + z * z; + if (d < 1 && d > 0.01) + { + d = 1 / sqrt(d); + x *= d; + y *= d; + z *= d; + double xp = x * 160; // 4J - moved further away (were 100) as they were cutting through far chunks + double yp = y * 160; + double zp = z * 160; + + double yRot = atan2(x, z); + double ySin = sin(yRot); + double yCos = cos(yRot); + + double xRot = atan2(sqrt(x * x + z * z), y); + double xSin = sin(xRot); + double xCos = cos(xRot); + + double zRot = random.nextDouble() * PI * 2; + double zSin = sin(zRot); + double zCos = cos(zRot); + + for (int c = 0; c < 4; c++) + { + double ___xo = 0; + double ___yo = ((c & 2) - 1) * ss; + double ___zo = (((c + 1) & 2) - 1) * ss; + + double __xo = ___xo; + double __yo = ___yo * zCos - ___zo * zSin; + double __zo = ___zo * zCos + ___yo * zSin; + + double _zo = __zo; + double _yo = __yo * xSin + __xo * xCos; + double _xo = __xo * xSin - __yo * xCos; + + double xo = _xo * ySin - _zo * yCos; + double yo = _yo; + double zo = _zo * ySin + _xo * yCos; + + t->vertex((float)(xp + xo), (float)( yp + yo), (float)( zp + zo)); + } + } + } + t->end(); + +} + + +void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel *level) +{ + if (this->level[playerIndex] != NULL) + { + // Remove listener for this level if this is the last player referencing it + Level *prevLevel = this->level[playerIndex]; + int refCount = 0; + for( int i = 0; i < 4; i++ ) + { + if( this->level[i] == prevLevel ) refCount++; + } + if( refCount == 1 ) + { + this->level[playerIndex]->removeListener(this); + } + } + + xOld[playerIndex] = -9999; + yOld[playerIndex] = -9999; + zOld[playerIndex] = -9999; + + this->level[playerIndex] = level; + if( tileRenderer[playerIndex] != NULL ) + { + delete tileRenderer[playerIndex]; + } + tileRenderer[playerIndex] = new TileRenderer(level); + if (level != NULL) + { + // If we're the only player referencing this level, add a new listener for it + int refCount = 0; + for( int i = 0; i < 4; i++ ) + { + if( this->level[i] == level ) refCount++; + } + if( refCount == 1 ) + { + level->addListener(this); + } + + allChanged(playerIndex); + } + else + { + // printf("NULLing player %d, chunks @ 0x%x\n",playerIndex,chunks[playerIndex]); + if( chunks[playerIndex].data != NULL ) + { + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) + { + chunks[playerIndex][i].chunk->_delete(); + delete chunks[playerIndex][i].chunk; + } + delete chunks[playerIndex].data; + chunks[playerIndex].data = NULL; + chunks[playerIndex].length = 0; + // delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore + // sortedChunks[playerIndex] = NULL; // 4J - removed - not sorting our chunks anymore + } + + // 4J Stu - If we do this for splitscreen players leaving, then all the tile entities in the world dissappear + // We should only do this when actually exiting the game, so only when the primary player sets there level to NULL + if(playerIndex == ProfileManager.GetPrimaryPad()) renderableTileEntities.clear(); + } +} + +void LevelRenderer::AddDLCSkinsToMemTextures() +{ + for(int i=0;iaddMemTexture(app.vSkinNames[i], new MobSkinMemTextureProcessor()); + } +} + +void LevelRenderer::allChanged() +{ + int playerIndex = mc->player->GetXboxPad(); // 4J added + allChanged(playerIndex); +} + +int LevelRenderer::activePlayers() +{ + int playerCount = 0; + for( int i = 0; i < 4; i++ ) + { + if( level[i] ) playerCount++; + } + return playerCount; +} + +void LevelRenderer::allChanged(int playerIndex) +{ + // 4J Stu - This was required by the threaded Minecraft::tick(). If we need to add it back then: + // If this CS is entered before DisableUpdateThread is called then (on 360 at least) we can get a + // deadlock when starting a game in splitscreen. + //EnterCriticalSection(&m_csDirtyChunks); + if( level == NULL ) + { + return; + } + + Minecraft::GetInstance()->gameRenderer->DisableUpdateThread(); + + Tile::leaves->setFancy(mc->options->fancyGraphics); + lastViewDistance = mc->options->viewDistance; + + // Calculate size of area we can render based on number of players we need to render for + int dist = (int)sqrtf( (float)PLAYER_RENDER_AREA / (float)activePlayers() ); + + // AP - poor little Vita just can't cope with such a big area +#ifdef __PSVITA__ + dist = 10; +#endif + + lastPlayerCount[playerIndex] = activePlayers(); + + xChunks = dist; + yChunks = Level::maxBuildHeight / CHUNK_SIZE; + zChunks = dist; + + if( chunks[playerIndex].data != NULL ) + { + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) + { + chunks[playerIndex][i].chunk->_delete(); + delete chunks[playerIndex][i].chunk; + } + delete chunks[playerIndex].data; + // delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore + } + + chunks[playerIndex] = ClipChunkArray(xChunks * yChunks * zChunks); + // sortedChunks[playerIndex] = new vector(xChunks * yChunks * zChunks); // 4J - removed - not sorting our chunks anymore + int id = 0; + int count = 0; + + xMinChunk = 0; + yMinChunk = 0; + zMinChunk = 0; + xMaxChunk = xChunks; + yMaxChunk = yChunks; + zMaxChunk = zChunks; + + // 4J removed - we now only fully clear this on exiting the game (setting level to NULL). Apart from that, the chunk rebuilding is responsible for maintaining this + // renderableTileEntities.clear(); + + for (int x = 0; x < xChunks; x++) + { + for (int y = 0; y < yChunks; y++) + { + for (int z = 0; z < zChunks; z++) + { + chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk = new Chunk(level[playerIndex], renderableTileEntities, m_csRenderableTileEntities, x * CHUNK_XZSIZE, y * CHUNK_SIZE, z * CHUNK_XZSIZE, &chunks[playerIndex][(z * yChunks + y) * xChunks + x]); + chunks[playerIndex][(z * yChunks + y) * xChunks + x].visible = true; + chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk->id = count++; + // sortedChunks[playerIndex]->at((z * yChunks + y) * xChunks + x) = chunks[playerIndex]->at((z * yChunks + y) * xChunks + x); // 4J - removed - not sorting our chunks anymore + + id += 3; + } + } + } + nonStackDirtyChunksAdded(); + + if (level != NULL) + { + shared_ptr player = mc->cameraTargetPlayer; + if (player != NULL) + { + this->resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + // sort(sortedChunks[playerIndex]->begin(),sortedChunks[playerIndex]->end(), DistanceChunkSorter(player)); // 4J - removed - not sorting our chunks anymore + } + } + + noEntityRenderFrames = 2; + + Minecraft::GetInstance()->gameRenderer->EnableUpdateThread(); + + // 4J Stu - Remove. See comment above. + //LeaveCriticalSection(&m_csDirtyChunks); +} + +void LevelRenderer::renderEntities(Vec3 *cam, Culler *culler, float a) +{ + int playerIndex = mc->player->GetXboxPad(); // 4J added + + // 4J Stu - Set these up every time, even when not rendering as other things (like particle render) may depend on it for those frames. + TileEntityRenderDispatcher::instance->prepare(level[playerIndex], textures, mc->font, mc->cameraTargetPlayer, a); + EntityRenderDispatcher::instance->prepare(level[playerIndex], textures, mc->font, mc->cameraTargetPlayer, mc->options, a); + + if (noEntityRenderFrames > 0) + { + noEntityRenderFrames--; + return; + } + + totalEntities = 0; + renderedEntities = 0; + culledEntities = 0; + + shared_ptr player = mc->cameraTargetPlayer; + + EntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); + EntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); + EntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); + TileEntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); + TileEntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); + TileEntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); + + mc->gameRenderer->turnOnLightLayer(a); // 4J - brought forward from 1.8.2 + + vector > entities = level[playerIndex]->getAllEntities(); + totalEntities = (int)entities.size(); + + AUTO_VAR(itEndGE, level[playerIndex]->globalEntities.end()); + for (AUTO_VAR(it, level[playerIndex]->globalEntities.begin()); it != itEndGE; it++) + { + shared_ptr entity = *it; //level->globalEntities[i]; + renderedEntities++; + if (entity->shouldRender(cam)) EntityRenderDispatcher::instance->render(entity, a); + } + + AUTO_VAR(itEndEnts, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEndEnts; it++) + { + shared_ptr entity = *it; //entities[i]; + + if ((entity->shouldRender(cam) && (entity->noCulling || culler->isVisible(entity->bb)))) + { + // 4J-PB - changing this to be per player + //if (entity == mc->cameraTargetPlayer && !mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) continue; + shared_ptr localplayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + + if (localplayer && entity == mc->cameraTargetPlayer && !localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) continue; + + if (!level[playerIndex]->hasChunkAt(Mth::floor(entity->x), 0, Mth::floor(entity->z))) + { + continue; + } + renderedEntities++; + EntityRenderDispatcher::instance->render(entity, a); + } + } + + Lighting::turnOn(); + // 4J - have restructed this so that the tile entities are stored within a hashmap by chunk/dimension index. The index + // is calculated in the same way as the global flags. + EnterCriticalSection(&m_csRenderableTileEntities); + for (AUTO_VAR(it, renderableTileEntities.begin()); it != renderableTileEntities.end(); it++) + { + int idx = it->first; + // Don't render if it isn't in the same dimension as this player + if( !isGlobalIndexInSameDimension(idx, level[playerIndex]) ) continue; + + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++) + { + TileEntityRenderDispatcher::instance->render(*it2, a); + } + } + + // Now consider if any of these renderable tile entities have been flagged for removal, and if so, remove + for (AUTO_VAR(it, renderableTileEntities.begin()); it != renderableTileEntities.end();) + { + int idx = it->first; + + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); ) + { + // If it has been flagged for removal, remove + if((*it2)->shouldRemoveForRender()) + { + it2 = it->second.erase(it2); + } + else + { + it2++; + } + } + + // If there aren't any entities left for this key, then delete the key + if( it->second.size() == 0 ) + { + it = renderableTileEntities.erase(it); + } + else + { + it++; + } + } + + LeaveCriticalSection(&m_csRenderableTileEntities); + + mc->gameRenderer->turnOffLightLayer(a); // 4J - brought forward from 1.8.2 +} + +wstring LevelRenderer::gatherStats1() +{ + return L"C: " + _toString(renderedChunks) + L"/" + _toString(totalChunks) + L". F: " + _toString(offscreenChunks) + L", O: " + _toString(occludedChunks) + L", E: " + _toString(emptyChunks); +} + +wstring LevelRenderer::gatherStats2() +{ + return L"E: " + _toString(renderedEntities) + L"/" + _toString(totalEntities) + L". B: " + _toString(culledEntities) + L", I: " + _toString((totalEntities - culledEntities) - renderedEntities); +} + +void LevelRenderer::resortChunks(int xc, int yc, int zc) +{ + EnterCriticalSection(&m_csDirtyChunks); + xc -= CHUNK_XZSIZE / 2; + yc -= CHUNK_SIZE / 2; + zc -= CHUNK_XZSIZE / 2; + xMinChunk = INT_MAX; + yMinChunk = INT_MAX; + zMinChunk = INT_MAX; + xMaxChunk = INT_MIN; + yMaxChunk = INT_MIN; + zMaxChunk = INT_MIN; + + int playerIndex = mc->player->GetXboxPad(); // 4J added + + int s2 = xChunks * CHUNK_XZSIZE; + int s1 = s2 / 2; + + for (int x = 0; x < xChunks; x++) + { + int xx = x * CHUNK_XZSIZE; + + int xOff = (xx + s1 - xc); + if (xOff < 0) xOff -= (s2 - 1); + xOff /= s2; + xx -= xOff * s2; + + if (xx < xMinChunk) xMinChunk = xx; + if (xx > xMaxChunk) xMaxChunk = xx; + + for (int z = 0; z < zChunks; z++) + { + int zz = z * CHUNK_XZSIZE; + int zOff = (zz + s1 - zc); + if (zOff < 0) zOff -= (s2 - 1); + zOff /= s2; + zz -= zOff * s2; + + if (zz < zMinChunk) zMinChunk = zz; + if (zz > zMaxChunk) zMaxChunk = zz; + + for (int y = 0; y < yChunks; y++) + { + int yy = y * CHUNK_SIZE; + if (yy < yMinChunk) yMinChunk = yy; + if (yy > yMaxChunk) yMaxChunk = yy; + + Chunk *chunk = chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk; + chunk->setPos(xx, yy, zz); + } + } + } + nonStackDirtyChunksAdded(); + LeaveCriticalSection(&m_csDirtyChunks); +} + +int LevelRenderer::render(shared_ptr player, int layer, double alpha, bool updateChunks) +{ + int playerIndex = mc->player->GetXboxPad(); + + // 4J - added - if the number of players has changed, we need to rebuild things for the new draw distance this will require + if( lastPlayerCount[playerIndex] != activePlayers() ) + { + allChanged(); + } + else if (mc->options->viewDistance != lastViewDistance) + { + allChanged(); + } + + if (layer == 0) + { + totalChunks = 0; + offscreenChunks = 0; + occludedChunks = 0; + renderedChunks = 0; + emptyChunks = 0; + } + + double xOff = player->xOld + (player->x - player->xOld) * alpha; + double yOff = player->yOld + (player->y - player->yOld) * alpha; + double zOff = player->zOld + (player->z - player->zOld) * alpha; + + double xd = player->x - xOld[playerIndex]; + double yd = player->y - yOld[playerIndex]; + double zd = player->z - zOld[playerIndex]; + + if (xd * xd + yd * yd + zd * zd > 4 * 4) + { + xOld[playerIndex] = player->x; + yOld[playerIndex] = player->y; + zOld[playerIndex] = player->z; + + resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + // sort(sortedChunks[playerIndex]->begin(),sortedChunks[playerIndex]->end(), DistanceChunkSorter(player)); // 4J - removed - not sorting our chunks anymore + } + Lighting::turnOff(); + + int count = renderChunks(0, (int)chunks[playerIndex].length, layer, alpha); + + return count; + +} + +#ifdef __PSVITA__ +#include + +// this is need to sort the chunks by depth +typedef struct +{ + int Index; + float Depth; +} SChunckSort; + +int compare (const void * a, const void * b) +{ + return ( ((SChunckSort*)a)->Depth - ((SChunckSort*)b)->Depth ); +} + +#endif + +int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) +{ + int playerIndex = mc->player->GetXboxPad(); // 4J added + +#if 1 + // 4J - cut down version, we're not using offsetted render lists, or a sorted chunk list, anymore + mc->gameRenderer->turnOnLightLayer(alpha); // 4J - brought forward from 1.8.2 + shared_ptr player = mc->cameraTargetPlayer; + double xOff = player->xOld + (player->x - player->xOld) * alpha; + double yOff = player->yOld + (player->y - player->yOld) * alpha; + double zOff = player->zOld + (player->z - player->zOld) * alpha; + + glPushMatrix(); + glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); + +#ifdef __PSVITA__ + // AP - also set the camera position so we can work out if a chunk is fogged or not + RenderManager.SetCameraPosition((float)-xOff, (float)-yOff, (float)-zOff); +#endif + +#if defined __PS3__ && !defined DISABLE_SPU_CODE + // pre- calc'd on the SPU + int count = 0; + waitForCull_SPU(); + if(layer == 0) + { + count = g_cullDataIn[playerIndex].numToRender_layer0; + RenderManager.CBuffCallMultiple(g_cullDataIn[playerIndex].listArray_layer0, count); + } + else // layer == 1 + { + count = g_cullDataIn[playerIndex].numToRender_layer1; + RenderManager.CBuffCallMultiple(g_cullDataIn[playerIndex].listArray_layer1, count); + } + +#else // __PS3__ + +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. First render all the non-alpha cut outs + glDisable(GL_ALPHA_TEST); +#endif + + bool first = true; + int count = 0; + ClipChunk *pClipChunk = chunks[playerIndex].data; + unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; + for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ ) + { + if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set + if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check + if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty + + // List can be calculated directly from the chunk's global idex + int list = pClipChunk->globalIdx * 2 + layer; + list += chunkLists; + + if(RenderManager.CBuffCall(list, first)) + { + first = false; + } + count++; + } + +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. Now we render all the alpha cut outs + glEnable(GL_ALPHA_TEST); + RenderManager.StateSetForceLOD(0); // AP - force mipmapping off for cut outs + first = true; + pClipChunk = chunks[playerIndex].data; + emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; + for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ ) + { + if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set + if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check + if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty + if( !(globalChunkFlags[pClipChunk->globalIdx] & LevelRenderer::CHUNK_FLAG_CUT_OUT) ) continue; // Does this chunk contain any cut out geometry + + // List can be calculated directly from the chunk's global idex + int list = pClipChunk->globalIdx * 2 + layer; + list += chunkLists; + + if(RenderManager.CBuffCallCutOut(list, first)) + { + first = false; + } + } + RenderManager.StateSetForceLOD(-1); // AP - back to normal mipmapping +#endif + +#endif // __PS3__ + + glPopMatrix(); + mc->gameRenderer->turnOffLightLayer(alpha); // 4J - brought forward from 1.8.2 + +#else + _renderChunks.clear(); + // int p = 0; + int count = 0; + for (int i = from; i < to; i++) + { + if (layer == 0) + { + totalChunks++; + if (sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer)) emptyChunks++; + else if (!sortedChunks[playerIndex]->at(i)->visible) offscreenChunks++; + else renderedChunks++; + } + + // if (!sortedChunks[i].empty[layer] && sortedChunks[i].visible && (sortedChunks[i].occlusion_visible)) { + if (!(sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer) && sortedChunks[playerIndex]->at(i)->visible )) + { + int list = sortedChunks[playerIndex]->at(i)->getList(layer); + if (list >= 0) + { + _renderChunks.push_back(sortedChunks[playerIndex]->at(i)); + count++; + } + } + } + + shared_ptr player = mc->cameraTargetPlayer; + double xOff = player->xOld + (player->x - player->xOld) * alpha; + double yOff = player->yOld + (player->y - player->yOld) * alpha; + double zOff = player->zOld + (player->z - player->zOld) * alpha; + + int lists = 0; + for (int l = 0; l < RENDERLISTS_LENGTH; l++) + { + renderLists[l].clear(); + } + + AUTO_VAR(itEnd, _renderChunks.end()); + for (AUTO_VAR(it, _renderChunks.begin()); it != itEnd; it++) + { + Chunk *chunk = *it; //_renderChunks[i]; + + int list = -1; + for (int l = 0; l < lists; l++) + { + if (renderLists[l].isAt(chunk->xRender, chunk->yRender, chunk->zRender)) + { + list = l; + } + } + if (list < 0) + { + list = lists++; + renderLists[list].init(chunk->xRender, chunk->yRender, chunk->zRender, xOff, yOff, zOff); + } + + renderLists[list].add(chunk->getList(layer)); + } + + renderSameAsLast(layer, alpha); +#endif + + return count; + +} + + +void LevelRenderer::renderSameAsLast(int layer, double alpha) +{ + for (int i = 0; i < RENDERLISTS_LENGTH; i++) + { + renderLists[i].render(); + } +} + +void LevelRenderer::tick() +{ + ticks++; + + if ((ticks % SharedConstants::TICKS_PER_SECOND) == 0) + { + AUTO_VAR(it , destroyingBlocks.begin()); + while (it != destroyingBlocks.end()) + { + BlockDestructionProgress *block = it->second; + + int updatedRenderTick = block->getUpdatedRenderTick(); + + if (ticks - updatedRenderTick > (SharedConstants::TICKS_PER_SECOND * 20)) + { + delete it->second; + it = destroyingBlocks.erase(it); + } + else + { + ++it; + } + } + } +} + +void LevelRenderer::renderSky(float alpha) +{ + if (mc->level->dimension->id == 1) + { + glDisable(GL_FOG); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Lighting::turnOff(); + + + glDepthMask(false); + textures->bind(textures->loadTexture(TN_MISC_TUNNEL)); // 4J was L"/1_2_2/misc/tunnel.png" + Tesselator *t = Tesselator::getInstance(); + t->setMipmapEnable(false); + for (int i = 0; i < 6; i++) + { + glPushMatrix(); + if (i == 1) glRotatef(90, 1, 0, 0); + if (i == 2) glRotatef(-90, 1, 0, 0); + if (i == 3) glRotatef(180, 1, 0, 0); + if (i == 4) glRotatef(90, 0, 0, 1); + if (i == 5) glRotatef(-90, 0, 0, 1); + t->begin(); + t->color(0x282828); + t->vertexUV(-100, -100, -100, 0, 0); + t->vertexUV(-100, -100, +100, 0, 16); + t->vertexUV(+100, -100, +100, 16, 16); + t->vertexUV(+100, -100, -100, 16, 0); + t->end(); + glPopMatrix(); + } + t->setMipmapEnable(true); + glDepthMask(true); + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + + return; + } + + if (!mc->level->dimension->isNaturalDimension()) return; + + glDisable(GL_TEXTURE_2D); + + int playerIndex = mc->player->GetXboxPad(); + Vec3 *sc = level[playerIndex]->getSkyColor(mc->cameraTargetPlayer, alpha); + float sr = (float) sc->x; + float sg = (float) sc->y; + float sb = (float) sc->z; + + if (mc->options->anaglyph3d) + { + float srr = (sr * 30 + sg * 59 + sb * 11) / 100; + float sgg = (sr * 30 + sg * 70) / (100); + float sbb = (sr * 30 + sb * 70) / (100); + + sr = srr; + sg = sgg; + sb = sbb; + } + + glColor3f(sr, sg, sb); + + Tesselator *t = Tesselator::getInstance(); + + glDepthMask(false); + +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. + glDisable(GL_ALPHA_TEST); +#endif + + glEnable(GL_FOG); + glColor3f(sr, sg, sb); + glCallList(skyList); + + glDisable(GL_FOG); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Lighting::turnOff(); + + float *c = level[playerIndex]->dimension->getSunriseColor(level[playerIndex]->getTimeOfDay(alpha), alpha); + if (c != NULL) + { + glDisable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + + glPushMatrix(); + { + glRotatef(90, 1, 0, 0); + glRotatef(Mth::sin(level[playerIndex]->getSunAngle(alpha)) < 0 ? 180 : 0, 0, 0, 1); + glRotatef(90, 0, 0, 1); + + float r = c[0]; + float g = c[1]; + float b = c[2]; + if (mc->options->anaglyph3d) + { + float srr = (r * 30 + g * 59 + b * 11) / 100; + float sgg = (r * 30 + g * 70) / (100); + float sbb = (r * 30 + b * 70) / (100); + + r = srr; + g = sgg; + b = sbb; + } + + t->begin(GL_TRIANGLE_FAN); + t->color(r, g, b, c[3]); + + t->vertex((float)(0), (float)( 100), (float)( 0)); + int steps = 16; + t->color(c[0], c[1], c[2], 0.0f); + for (int i = 0; i <= steps; i++) + { + float a = i * PI * 2 / steps; + float _sin = Mth::sin(a); + float _cos = Mth::cos(a); + t->vertex((float)(_sin * 120), (float)( _cos * 120), (float)( -_cos * 40 * c[3])); + } + t->end(); + } + glPopMatrix(); + glShadeModel(GL_FLAT); + } + + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glPushMatrix(); + { + float rainBrightness = 1 - level[playerIndex]->getRainLevel(alpha); + float xp = 0; + float yp = 0; + float zp = 0; + glColor4f(1, 1, 1, rainBrightness); + glTranslatef(xp, yp, zp); + glRotatef(-90, 0, 1, 0); + glRotatef(level[playerIndex]->getTimeOfDay(alpha) * 360, 1, 0, 0); + float ss = 30; + + MemSect(31); + textures->bindTexture(TN_TERRAIN_SUN); // 4J was L"/terrain/sun.png" + MemSect(0); + t->begin(); + t->vertexUV((float)(-ss), (float)( 100), (float)( -ss), (float)( 0), (float)( 0)); + t->vertexUV((float)(+ss), (float)( 100), (float)( -ss), (float)( 1), (float)( 0)); + t->vertexUV((float)(+ss), (float)( 100), (float)( +ss), (float)( 1), (float)( 1)); + t->vertexUV((float)(-ss), (float)( 100), (float)( +ss), (float)( 0), (float)( 1)); + t->end(); + + ss = 20; + textures->bindTexture(TN_TERRAIN_MOON_PHASES); // 4J was L"/1_2_2/terrain/moon_phases.png" + int phase = level[playerIndex]->getMoonPhase(alpha); + int u = phase % 4; + int v = phase / 4 % 2; + float u0 = (u + 0) / 4.0f; + float v0 = (v + 0) / 2.0f; + float u1 = (u + 1) / 4.0f; + float v1 = (v + 1) / 2.0f; + t->begin(); + t->vertexUV(-ss, -100, +ss, u1, v1); + t->vertexUV(+ss, -100, +ss, u0, v1); + t->vertexUV(+ss, -100, -ss, u0, v0); + t->vertexUV(-ss, -100, -ss, u1, v0); + t->end(); + + glDisable(GL_TEXTURE_2D); + float br = level[playerIndex]->getStarBrightness(alpha) * rainBrightness; + if (br > 0) + { + glColor4f(br, br, br, br); + glCallList(starList); + } + glColor4f(1, 1, 1, 1); + } + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_FOG); + +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. + glDisable(GL_ALPHA_TEST); +#endif + + glPopMatrix(); + glDisable(GL_TEXTURE_2D); + glColor3f(0, 0, 0); + + double yy = mc->player->getPos(alpha)->y - level[playerIndex]->getHorizonHeight(); // 4J - getHorizonHeight moved forward from 1.2.3 + if (yy < 0) + { + glPushMatrix(); + glTranslatef(0, -(float) (-12), 0); + glCallList(darkList); + glPopMatrix(); + + // 4J - can't work out what this big black box is for. Taking it out until someone misses it... it causes a big black box to visible appear in 3rd person mode whilst under the ground. +#if 0 + float ss = 1; + float yo = -(float) (yy + 65); + float y0 = -ss; + float y1 = yo; + + + t->begin(); + t->color(0x000000, 255); + t->vertex(-ss, y1, ss); + t->vertex(+ss, y1, ss); + t->vertex(+ss, y0, ss); + t->vertex(-ss, y0, ss); + + t->vertex(-ss, y0, -ss); + t->vertex(+ss, y0, -ss); + t->vertex(+ss, y1, -ss); + t->vertex(-ss, y1, -ss); + + t->vertex(+ss, y0, -ss); + t->vertex(+ss, y0, +ss); + t->vertex(+ss, y1, +ss); + t->vertex(+ss, y1, -ss); + + t->vertex(-ss, y1, -ss); + t->vertex(-ss, y1, +ss); + t->vertex(-ss, y0, +ss); + t->vertex(-ss, y0, -ss); + + t->vertex(-ss, y0, -ss); + t->vertex(-ss, y0, +ss); + t->vertex(+ss, y0, +ss); + t->vertex(+ss, y0, -ss); + t->end(); +#endif + } + + if (level[playerIndex]->dimension->hasGround()) + { + glColor3f(sr * 0.2f + 0.04f, sg * 0.2f + 0.04f, sb * 0.6f + 0.1f); + } + else + { + glColor3f(sr, sg, sb); + } + glPushMatrix(); + glTranslatef(0, -(float) (yy - 16), 0); + glCallList(darkList); + glPopMatrix(); + glEnable(GL_TEXTURE_2D); + + glDepthMask(true); +} + +void LevelRenderer::renderHaloRing(float alpha) +{ +#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) + if (!mc->level->dimension->isNaturalDimension()) return; + + glDisable(GL_ALPHA_TEST); + glDisable(GL_TEXTURE_2D); + glDepthMask(false); + glEnable(GL_FOG); + + int playerIndex = mc->player->GetXboxPad(); + + Vec3 *sc = level[playerIndex]->getSkyColor(mc->cameraTargetPlayer, alpha); + float sr = (float) sc->x; + float sg = (float) sc->y; + float sb = (float) sc->z; + + // Rough lumninance calculation + float Y = (sr+sr+sb+sg+sg+sg)/6; + float br = 0.6f + (Y*0.4f); + //app.DebugPrintf("Luminance = %f, brightness = %f\n", Y, br); + glColor3f(br,br,br); + + // Fog at the base near the world + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_START, HALO_RING_RADIUS); + glFogf(GL_FOG_END, HALO_RING_RADIUS * 0.20f); + + Lighting::turnOn(); + + glDepthMask(false); + textures->bindTexture(L"misc/haloRing.png"); // 4J was L"/1_2_2/misc/tunnel.png" + Tesselator *t = Tesselator::getInstance(); + bool prev = t->setMipmapEnable(true); + + glPushMatrix(); + glRotatef(-90, 1, 0, 0); + glRotatef(90, 0, 1, 0); + glCallList(haloRingList); + glPopMatrix(); + t->setMipmapEnable(prev); + + glDepthMask(true); + glEnable(GL_TEXTURE_2D); + glEnable(GL_ALPHA_TEST); + + glDisable(GL_FOG); +#endif +} + +void LevelRenderer::renderClouds(float alpha) +{ + int iTicks=ticks; + int playerIndex = mc->player->GetXboxPad(); + + // if the primary player has clouds off, so do all players on this machine + if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Clouds)==0) + { + return; + } + + // debug setting added to keep it at day time + if (!mc->level->dimension->isNaturalDimension()) return; + + if (mc->options->fancyGraphics) + { + renderAdvancedClouds(alpha); + return; + } + + if(app.DebugSettingsOn()) + { + if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<cameraTargetPlayer->yOld + (mc->cameraTargetPlayer->y - mc->cameraTargetPlayer->yOld) * alpha); + int s = 32; + int d = 256 / s; + Tesselator *t = Tesselator::getInstance(); + + textures->bindTexture(TN_ENVIRONMENT_CLOUDS); // 4J was L"/environment/clouds.png" + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Vec3 *cc = level[playerIndex]->getCloudColor(alpha); + float cr = (float) cc->x; + float cg = (float) cc->y; + float cb = (float) cc->z; + + if (mc->options->anaglyph3d) + { + float crr = (cr * 30 + cg * 59 + cb * 11) / 100; + float cgg = (cr * 30 + cg * 70) / (100); + float cbb = (cr * 30 + cb * 70) / (100); + + cr = crr; + cg = cgg; + cb = cbb; + } + + + + float scale = 1 / 2048.0f; + + double time = (ticks + alpha); + double xo = mc->cameraTargetPlayer->xo + (mc->cameraTargetPlayer->x - mc->cameraTargetPlayer->xo) * alpha + time * 0.03f; + double zo = mc->cameraTargetPlayer->zo + (mc->cameraTargetPlayer->z - mc->cameraTargetPlayer->zo) * alpha; + int xOffs = Mth::floor(xo / 2048); + int zOffs = Mth::floor(zo / 2048); + xo -= xOffs * 2048; + zo -= zOffs * 2048; + + float yy = (float) (level[playerIndex]->dimension->getCloudHeight() - yOffs + 0.33f); + float uo = (float) (xo * scale); + float vo = (float) (zo * scale); + t->begin(); + + t->color(cr, cg, cb, 0.8f); + for (int xx = -s * d; xx < +s * d; xx += s) + { + for (int zz = -s * d; zz < +s * d; zz += s) + { + t->vertexUV((float)(xx + 0), (float)( yy), (float)( zz + s), (float)( (xx + 0) * scale + uo), (float)( (zz + s) * scale + vo)); + t->vertexUV((float)(xx + s), (float)( yy), (float)( zz + s), (float)( (xx + s) * scale + uo), (float)( (zz + s) * scale + vo)); + t->vertexUV((float)(xx + s), (float)( yy), (float)( zz + 0), (float)( (xx + s) * scale + uo), (float)( (zz + 0) * scale + vo)); + t->vertexUV((float)(xx + 0), (float)( yy), (float)( zz + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); + } + } + t->end(); + + glColor4f(1, 1, 1, 1.0f); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + + if(app.DebugSettingsOn()) + { + + if(!(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(0.7f, 0.7f, 0.7f, 0.8f); + t->normal(0, -1, 0); + t->vertexUV(x0, y0, z0, u, v ); + t->vertexUV(x1, y0, z0, u, v ); + t->vertexUV(x1, y0, z1, u, v ); + t->vertexUV(x0, y0, z1, u, v ); + } + } + t->end(); + } + if( ( i == 1 ) || ( i == 6 ) ) + { + t->begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(1.0f, 1.0f, 1.0f, 0.8f); + t->normal(0, 1, 0); + t->vertexUV(x0, y1, z1, u, v ); + t->vertexUV(x1, y1, z1, u, v ); + t->vertexUV(x1, y1, z0, u, v ); + t->vertexUV(x0, y1, z0, u, v ); + } + } + t->end(); + } + if( ( i == 2 ) || ( i == 6 ) ) + { + t->begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(0.9f, 0.9f, 0.9f, 0.8f); + t->normal(-1, 0, 0); + t->vertexUV(x0, y0, z1, u, v ); + t->vertexUV(x0, y1, z1, u, v ); + t->vertexUV(x0, y1, z0, u, v ); + t->vertexUV(x0, y0, z0, u, v ); + } + } + t->end(); + } + if( ( i == 3 ) || ( i == 6 ) ) + { + t->begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(0.9f, 0.9f, 0.9f, 0.8f); + t->normal(1, 0, 0); + t->vertexUV(x1, y0, z0, u, v ); + t->vertexUV(x1, y1, z0, u, v ); + t->vertexUV(x1, y1, z1, u, v ); + t->vertexUV(x1, y0, z1, u, v ); + } + } + t->end(); + } + if( ( i == 4 ) || ( i == 6 ) ) + { + t->begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(0.8f, 0.8f, 0.8f, 0.8f); + t->normal(-1, 0, 0); + t->vertexUV(x0, y1, z0, u, v ); + t->vertexUV(x1, y1, z0, u, v ); + t->vertexUV(x1, y0, z0, u, v ); + t->vertexUV(x0, y0, z0, u, v ); + } + } + t->end(); + } + if( ( i == 5 ) || ( i == 6 ) ) + { + t->begin(); + for( int zt = 0; zt < D; zt++ ) + { + for( int xt = 0; xt < D; xt++ ) + { + float u = (((float) xt ) + 0.5f ) / 256.0f; + float v = (((float) zt ) + 0.5f ) / 256.0f; + float x0 = (float)xt; + float x1 = x0 + 1.0f; + float y0 = 0; + float y1 = h; + float z0 = (float)zt; + float z1 = z0 + 1.0f; + t->color(0.8f, 0.8f, 0.8f, 0.8f); + t->normal(1, 0, 0); + t->vertexUV(x0, y0, z1, u, v ); + t->vertexUV(x1, y0, z1, u, v ); + t->vertexUV(x1, y1, z1, u, v ); + t->vertexUV(x0, y1, z1, u, v ); + } + } + t->end(); + } + glEndList(); + } +} + +void LevelRenderer::renderAdvancedClouds(float alpha) +{ + // MGH - added, we were getting dark clouds sometimes on PS3, with this being setup incorrectly + glMultiTexCoord2f(GL_TEXTURE1, 0, 0); + + + // 4J - most of our viewports are now rendered with no clip planes but using stencilling to limit the area drawn to. Clouds have a relatively large fill area compared to + // the number of vertices that they have, and so enabling clipping here to try and reduce fill rate cost. + RenderManager.StateSetEnableViewportClipPlanes(true); + float yOffs = (float) (mc->cameraTargetPlayer->yOld + (mc->cameraTargetPlayer->y - mc->cameraTargetPlayer->yOld) * alpha); + Tesselator *t = Tesselator::getInstance(); + int playerIndex = mc->player->GetXboxPad(); + + int iTicks=ticks; + + if(app.DebugSettingsOn()) + { + if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<cameraTargetPlayer->xo + (mc->cameraTargetPlayer->x - mc->cameraTargetPlayer->xo) * alpha + time * 0.03f) / ss; + double zo = (mc->cameraTargetPlayer->zo + (mc->cameraTargetPlayer->z - mc->cameraTargetPlayer->zo) * alpha) / ss + 0.33f; + float yy = (float) (level[playerIndex]->dimension->getCloudHeight() - yOffs + 0.33f); + int xOffs = Mth::floor(xo / 2048); + int zOffs = Mth::floor(zo / 2048); + xo -= xOffs * 2048; + zo -= zOffs * 2048; + + // 4J - we are now conditionally rendering the clouds in two ways + // (1) if we are (by our y height) in the clouds, then we render in a mode quite like the original, with no backface culling, and + // decisions on which sides of the clouds to render based on the positions of the 8x8 blocks of cloud texels + // (2) if we aren't in the clouds, then we do a simpler form of rendering with backface culling on + // This is because the complex sort of rendering is really there so that the clouds seem more solid when you might be in them, but it has more risk of artifacts so + // we don't want to do it when not necessary + + bool noBFCMode = ( (yy > -h - 1) && (yy <= h + 1) ); + if( noBFCMode ) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + } + + MemSect(31); + textures->bindTexture(TN_ENVIRONMENT_CLOUDS); // 4J was L"/environment/clouds.png" + MemSect(0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Vec3 *cc = level[playerIndex]->getCloudColor(alpha); + float cr = (float) cc->x; + float cg = (float) cc->y; + float cb = (float) cc->z; + + if (mc->options->anaglyph3d) + { + float crr = (cr * 30 + cg * 59 + cb * 11) / 100; + float cgg = (cr * 30 + cg * 70) / (100); + float cbb = (cr * 30 + cb * 70) / (100); + + cr = crr; + cg = cgg; + cb = cbb; + } + + float uo = (float) (xo * 0); + float vo = (float) (zo * 0); + + float scale = 1 / 256.0f; + + uo = (float) (Mth::floor(xo)) * scale; + vo = (float) (Mth::floor(zo)) * scale; + // 4J - keep our UVs +ve - there's a small bug in the xbox GPU that incorrectly rounds small -ve UVs (between -1/(64*size) and 0) up to 0, which leaves gaps in our clouds... + while( uo < 1.0f ) uo += 1.0f; + while( vo < 1.0f ) vo += 1.0f; + + float xoffs = (float) (xo - Mth::floor(xo)); + float zoffs = (float) (zo - Mth::floor(zo)); + + int D = 8; + + int radius = 3; + if( activePlayers() > 2 ) radius = 2; // 4J - reduce the cloud render distance a bit for 3 & 4 player split screen + float e = 1 / 1024.0f; + glScalef(ss, 1, ss); + FrustumData* pFrustumData = Frustum::getFrustum(); + for (int pass = 0; pass < 2; pass++) + { + if (pass == 0) + { + // 4J - changed to use blend rather than color mask to avoid writing to frame buffer, to work with our command buffers + glBlendFunc(GL_ZERO, GL_ONE); + // glColorMask(false, false, false, false); + } + else + { + // 4J - changed to use blend rather than color mask to avoid writing to frame buffer, to work with our command buffers + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // glColorMask(true, true, true, true); + } + for (int xPos = -radius + 1; xPos <= radius; xPos++) + { + for (int zPos = -radius + 1; zPos <= radius; zPos++) + { + // 4J - reimplemented the clouds with full cube-per-texel geometry to get rid of seams. This is a huge amount more quads to render, so + // now using command buffers to render each section to cut CPU hit. +#if 1 + float xx = (float)(xPos * D); + float zz = (float)(zPos * D); + float xp = xx - xoffs; + float zp = zz - zoffs; + + if( !pFrustumData->cubeInFrustum(0+xp,0+yy,0+zp, 8+xp,4+yy,8+zp) ) + continue; + + + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glTranslatef(xx / 256.0f + uo, zz / 256.0f + vo, 0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslatef(xp,yy,zp); + + glColor4f(cr, cg, cb, 1.0f ); + if( noBFCMode ) + { + // This is the more complex form of render the clouds, based on the way that the original code picked which sides to render, with backface culling disabled. + // This is to give a more solid version of the clouds for when the player might be inside them. + bool draw[6] = {false,false,false,false,false,false}; + + // These rules to decide which sides to draw are the same as the original code below + if (yy > -h - 1) draw[0] = true; + if (yy <= h + 1) draw[1] = true; + if (xPos > -1) draw[2] = true; + if (xPos <= 1) draw[3] = true; + if (zPos > -1) draw[4] = true; + if (zPos <= 1) draw[5] = true; + + // Top and bottom just render when required + if( draw[0] ) glCallList(cloudList); + if( draw[1] ) glCallList(cloudList + 1); + // For x facing sides, if we are actually in the clouds and about to draw both sides of the x sides too, then + // do a little offsetting here to avoid z fighting + if( draw[0] && draw[1] && draw[2] && draw[3] ) + { + glTranslatef(e, 0.0f, 0.0f ); + glCallList(cloudList + 2); + glTranslatef(-e, 0.0f, 0.0f ); + glCallList(cloudList + 3); + } + else + { + if( draw[2] ) glCallList(cloudList + 2); + if( draw[3] ) glCallList(cloudList + 3); + } + // For z facing sides, if we are actually in the clouds and about to draw both sides of the z sides too, then + // do a little offsetting here to avoid z fighting + if( draw[0] && draw[1] && draw[4] && draw[5] ) + { + glTranslatef(0.0f, 0.0f, e ); + glCallList(cloudList + 4); + glTranslatef(0.0f, 0.0f, -e ); + glCallList(cloudList + 5); + } + else + { + if( draw[4] ) glCallList(cloudList + 4); + if( draw[5] ) glCallList(cloudList + 5); + } + } + else + { + // Simpler form of rendering that we can do most of the time, when we aren't potentially inside a cloud + glCallList(cloudList + 6); + } + glPopMatrix(); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); +#else + + t->begin(); + float xx = (float)(xPos * D); + float zz = (float)(zPos * D); + float xp = xx - xoffs; + float zp = zz - zoffs; + + + if (yy > -h - 1) + { + t->color(cr * 0.7f, cg * 0.7f, cb * 0.7f, 0.8f); + t->normal(0, -1, 0); + t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + D), (float)( (xx + 0) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + D), (float)( (xx + D) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + 0), (float)( (xx + D) * scale + uo), (float)( (zz + 0) * scale + vo)); + t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); + } + + if (yy <= h + 1) + { + t->color(cr, cg, cb, 0.8f); + t->normal(0, 1, 0); + t->vertexUV((float)(xp + 0), (float)( yy + h - e), (float)( zp + D), (float)( (xx + 0) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + h - e), (float)( zp + D), (float)( (xx + D) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + h - e), (float)( zp + 0), (float)( (xx + D) * scale + uo), (float)( (zz + 0) * scale + vo)); + t->vertexUV((float)(xp + 0), (float)( yy + h - e), (float)( zp + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); + } + + t->color(cr * 0.9f, cg * 0.9f, cb * 0.9f, 0.8f); + if (xPos > -1) + { + t->normal(-1, 0, 0); + for (int i = 0; i < D; i++) + { + t->vertexUV((float)(xp + i + 0), (float)( yy + 0), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + i + 0), (float)( yy + h), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + i + 0), (float)( yy + h), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); + t->vertexUV((float)(xp + i + 0), (float)( yy + 0), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); + } + } + + if (xPos <= 1) + { + t->normal(+1, 0, 0); + for (int i = 0; i < D; i++) + { + t->vertexUV((float)(xp + i + 1 - e), (float)( yy + 0), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + i + 1 - e), (float)( yy + h), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); + t->vertexUV((float)(xp + i + 1 - e), (float)( yy + h), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); + t->vertexUV((float)(xp + i + 1 - e), (float)( yy + 0), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); + } + } + + t->color(cr * 0.8f, cg * 0.8f, cb * 0.8f, 0.8f); + if (zPos > -1) + { + t->normal(0, 0, -1); + for (int i = 0; i < D; i++) + { + t->vertexUV((float)(xp + 0), (float)( yy + h), (float)( zp + i + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + h), (float)( zp + i + 0), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + i + 0), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + i + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + } + } + + if (zPos <= 1) + { + t->normal(0, 0, 1); + for (int i = 0; i < D; i++) + { + t->vertexUV((float)(xp + 0), (float)( yy + h), (float)( zp + i + 1 - e), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + h), (float)( zp + i + 1 - e), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + i + 1 - e), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + i + 1 - e), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); + } + } + t->end(); +#endif + } + } + } + + glColor4f(1, 1, 1, 1.0f); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + + + if(app.DebugSettingsOn()) + { + if(!(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L< > nearestClipChunks; +#endif + + ClipChunk *nearChunk = NULL; // Nearest chunk that is dirty + int veryNearCount = 0; + int minDistSq = 0x7fffffff; // Distances to this chunk + + + // Set a flag if we should only rebuild existing chunks, not create anything new + unsigned int memAlloc = RenderManager.CBuffSize(-1); + /* + static int throttle = 0; + if( ( throttle % 100 ) == 0 ) + { + app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); + } + throttle++; + */ + PIXAddNamedCounter(((float)memAlloc)/(1024.0f*1024.0f),"Command buffer allocations"); + bool onlyRebuild = ( memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS ); + EnterCriticalSection(&m_csDirtyChunks); + + // Move any dirty chunks stored in the lock free stack into global flags + int index = 0; + + do + { + // See comment on dirtyChunksLockFreeStack.Push() regarding details of this casting/subtracting -2. + index = (size_t)dirtyChunksLockFreeStack.Pop(); +#ifdef _CRITICAL_CHUNKS + int oldIndex = index; + index &= 0x0fffffff; // remove the top bit that marked the chunk as non-critical +#endif + if( index == 1 ) dirtyChunkPresent = true; // 1 is a special value passed to let this thread know that a chunk which isn't on this stack has been set to dirty + else if( index > 1 ) + { + int i2 = index - 2; + if( i2 >= DIMENSION_OFFSETS[2] ) + { + i2 -= DIMENSION_OFFSETS[2]; + int y2 = i2 & (CHUNK_Y_COUNT-1); + i2 /= CHUNK_Y_COUNT; + int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; + int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; + x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + } + setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); + +#ifdef _CRITICAL_CHUNKS + if( !(oldIndex & 0x10000000) ) // was this chunk not marked as non-critical. Ugh double negatives + { + setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); + } +#endif + + dirtyChunkPresent = true; + } + } while( index ); + + // Only bother searching round all the chunks if we have some dirty chunk(s) + if( dirtyChunkPresent ) + { + lastDirtyChunkFound = System::currentTimeMillis(); + PIXBeginNamedEvent(0,"Finding nearest chunk\n"); +#if defined __PS3__ && !defined DISABLE_SPU_CODE + // find the nearest chunk with a spu task, copy all the data over here for uploading to SPU + g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); + g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; + g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; + g_findNearestChunkDataIn.lowerOffset = (int)&((LevelChunk*)0)->lowerBlocks; // dodgy bit of class structure poking, as we don't want to try and get the whole of LevelChunk copmpiling on SPU + g_findNearestChunkDataIn.upperOffset = (int)&((LevelChunk*)0)->upperBlocks; + g_findNearestChunkDataIn.xChunks = xChunks; + g_findNearestChunkDataIn.yChunks = yChunks; + g_findNearestChunkDataIn.zChunks = zChunks; + + for(int i=0;i<4;i++) + { + g_findNearestChunkDataIn.chunks[i] = (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i].data; + g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; + g_findNearestChunkDataIn.level[i] = level[i]; + g_findNearestChunkDataIn.playerData[i].bValid = mc->localplayers[i] != NULL; + if(mc->localplayers[i] != NULL) + { + g_findNearestChunkDataIn.playerData[i].x = mc->localplayers[i]->x; + g_findNearestChunkDataIn.playerData[i].y = mc->localplayers[i]->y; + g_findNearestChunkDataIn.playerData[i].z = mc->localplayers[i]->z; + + } + if(level[i] != NULL) + { + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; + g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource))->cache; + } + + } + + // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) == sizeof(Chunk)); + C4JSpursJob_LevelRenderer_FindNearestChunk findJob(&g_findNearestChunkDataIn); + m_jobPort_FindNearestChunk->submitJob(&findJob); + m_jobPort_FindNearestChunk->waitForCompletion(); + nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; + veryNearCount = g_findNearestChunkDataIn.veryNearCount; +#else // __PS3__ + +#ifdef _LARGE_WORLDS + int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; + // 4J Stu - On XboxOne we should cut this down if in a constrained state so the saving threads get more time +#endif + // Find nearest chunk that is dirty + for( int p = 0; p < XUSER_MAX_COUNT; p++ ) + { + // It's possible that the localplayers member can be set to NULL on the main thread when a player chooses to exit the game + // So take a reference to the player object now. As it is a shared_ptr it should live as long as we need it + shared_ptr player = mc->localplayers[p]; + if( player == NULL ) continue; + if( chunks[p].data == NULL ) continue; + if( level[p] == NULL ) continue; + if( chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT ) continue; + int px = (int)player->x; + int py = (int)player->y; + int pz = (int)player->z; + +// app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} ",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, xChunks, zChunks); + + int considered = 0; + int wouldBeNearButEmpty = 0; + for( int x = 0; x < xChunks; x++ ) + { + for( int z = 0; z < zChunks; z++ ) + { + for( int y = 0; y < CHUNK_Y_COUNT; y++ ) + { + ClipChunk *pClipChunk = &chunks[p][(z * yChunks + y) * xChunks + x]; + // Get distance to this chunk - deliberately not calling the chunk's method of doing this to avoid overheads (passing entitie, type conversion etc.) that this involves + int xd = pClipChunk->xm - px; + int yd = pClipChunk->ym - py; + int zd = pClipChunk->zm - pz; + int distSq = xd * xd + yd * yd + zd * zd; + int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd; // Weighting against y to prioritise things in same x/z plane as player first + + if( globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_DIRTY ) + { + if( (!onlyRebuild) || + globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED || + ( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data + { + considered++; + // Is this chunk nearer than our nearest? +#ifdef _LARGE_WORLDS + bool isNearer = nearestClipChunks.empty(); + AUTO_VAR(itNearest, nearestClipChunks.begin()); + for(; itNearest != nearestClipChunks.end(); ++itNearest) + { + isNearer = distSqWeighted < itNearest->second; + if(isNearer) break; + } + isNearer = isNearer || (nearestClipChunks.size() < maxNearestChunks); +#else + bool isNearer = distSqWeighted < minDistSq; +#endif + +#ifdef _CRITICAL_CHUNKS + // AP - this will make sure that if a deferred grouping has started, only critical chunks go into that + // grouping, even if a non-critical chunk is closer. + if( (!veryNearCount && isNearer) || + (distSq < 20 * 20 && (globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) ) +#else + if( isNearer ) +#endif + { + // At this point we've got a chunk that we would like to consider for rendering, at least based on its proximity to the player(s). + // Its *quite* quick to generate empty render data for render chunks, but if we let the rebuilding do that then the after rebuilding we will have + // to start searching for the next nearest chunk from scratch again. Instead, its better to detect empty chunks at this stage, flag them up as not dirty + // (and empty), and carry on. The levelchunk's isRenderChunkEmpty method can be quite optimal as it can make use of the chunk's data compression to detect + // emptiness without actually testing as many data items as uncompressed data would. + Chunk *chunk = pClipChunk->chunk; + LevelChunk *lc = level[p]->getChunkAt(chunk->x,chunk->z); + if( !lc->isRenderChunkEmpty(y * 16) ) + { + nearChunk = pClipChunk; + minDistSq = distSqWeighted; +#ifdef _LARGE_WORLDS + nearestClipChunks.insert(itNearest, std::pair(nearChunk, minDistSq) ); + if(nearestClipChunks.size() > maxNearestChunks) + { + nearestClipChunks.pop_back(); + } +#endif + } + else + { + chunk->clearDirty(); + globalChunkFlags[ pClipChunk->globalIdx ] |= CHUNK_FLAG_EMPTYBOTH; + wouldBeNearButEmpty++; + } + } + +#ifdef _CRITICAL_CHUNKS + // AP - is the chunk near and also critical + if( distSq < 20 * 20 && ((globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) ) +#else + if( distSq < 20 * 20 ) +#endif + { + veryNearCount++; + } + } + } + } + } + } +// app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); + } +#endif // __PS3__ + PIXEndNamedEvent(); + } + + + + Chunk *chunk = NULL; +#ifdef _LARGE_WORLDS + if(!nearestClipChunks.empty()) + { + int index = 0; + for(AUTO_VAR(it, nearestClipChunks.begin()); it != nearestClipChunks.end(); ++it) + { + chunk = it->first->chunk; + // If this chunk is very near, then move the renderer into a deferred mode. This won't commit any command buffers + // for rendering until we call CBuffDeferredModeEnd(), allowing us to group any near changes into an atomic unit. This + // is essential so we don't temporarily create any holes in the environment whilst updating one chunk and not the neighbours. + // The "ver near" aspect of this is just a cosmetic nicety - exactly the same thing would happen further away, but we just don't + // care about it so much from terms of visual impact. + if( veryNearCount > 0 ) + { + RenderManager.CBuffDeferredModeStart(); + } + // Build this chunk & return false to continue processing + chunk->clearDirty(); + // Take a copy of the details that are required for chunk rebuilding, and rebuild That instead of the original chunk data. This is done within + // the m_csDirtyChunks critical section, which means that any chunks can't be repositioned whilst we are doing this copy. The copy will then + // be guaranteed to be consistent whilst rebuilding takes place outside of that critical section. + permaChunk[index].makeCopyForRebuild(chunk); + ++index; + } + LeaveCriticalSection(&m_csDirtyChunks); + + --index; // Bring it back into 0 counted range + + for(int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) + { + // Set the events that won't run + if( (i+1) > index) s_rebuildCompleteEvents->Set(i); + else break; + } + + for(; index >=0; --index) + { + bool bAtomic = false; + if((veryNearCount > 0)) + bAtomic = true; //MGH - if veryNearCount, then we're trying to rebuild atomically, so do it all on the main thread + + if( bAtomic || (index == 0) ) + { + //PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d %d",chunk->x, chunk->y, chunk->z); + // static __int64 totalTime = 0; + // static __int64 countTime = 0; + // __int64 startTime = System::currentTimeMillis(); + + //app.DebugPrintf("Rebuilding permaChunk %d\n", index); + + permaChunk[index].rebuild(); + + if(index !=0) + s_rebuildCompleteEvents->Set(index-1); // MGH - this rebuild happening on the main thread instead, mark the thread it should have been running on as complete + + // __int64 endTime = System::currentTimeMillis(); + // totalTime += (endTime - startTime); + // countTime++; + // printf("%d : %f\n", countTime, (float)totalTime / (float)countTime); + //PIXEndNamedEvent(); + } + // 4J Stu - Ignore this path when in constrained mode on Xbox One + else + { + // Activate thread to rebuild this chunk + s_activationEventA[index - 1]->Set(); + } + } + + // Wait for the other threads to be done as well + s_rebuildCompleteEvents->WaitForAll(INFINITE); + } +#else + if( nearChunk ) + { + chunk = nearChunk->chunk; + PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d %d",chunk->x, chunk->y, chunk->z); + // If this chunk is very near, then move the renderer into a deferred mode. This won't commit any command buffers + // for rendering until we call CBuffDeferredModeEnd(), allowing us to group any near changes into an atomic unit. This + // is essential so we don't temporarily create any holes in the environment whilst updating one chunk and not the neighbours. + // The "ver near" aspect of this is just a cosmetic nicety - exactly the same thing would happen further away, but we just don't + // care about it so much from terms of visual impact. + if( veryNearCount > 0 ) + { + RenderManager.CBuffDeferredModeStart(); + } + // Build this chunk & return false to continue processing + chunk->clearDirty(); + // Take a copy of the details that are required for chunk rebuilding, and rebuild That instead of the original chunk data. This is done within + // the m_csDirtyChunks critical section, which means that any chunks can't be repositioned whilst we are doing this copy. The copy will then + // be guaranteed to be consistent whilst rebuilding takes place outside of that critical section. + static Chunk permaChunk; + permaChunk.makeCopyForRebuild(chunk); + LeaveCriticalSection(&m_csDirtyChunks); + // static __int64 totalTime = 0; + // static __int64 countTime = 0; + // __int64 startTime = System::currentTimeMillis(); + permaChunk.rebuild(); + // __int64 endTime = System::currentTimeMillis(); + // totalTime += (endTime - startTime); + // countTime++; + // printf("%d : %f\n", countTime, (float)totalTime / (float)countTime); + PIXEndNamedEvent(); + } +#endif + else + { + // Nothing to do - clear flags that there are things to process, unless it's been a while since we found any dirty chunks in which case force a check next time through + if( ( System::currentTimeMillis() - lastDirtyChunkFound ) > FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS ) + { + dirtyChunkPresent = true; + } + else + { + dirtyChunkPresent = false; + } + LeaveCriticalSection(&m_csDirtyChunks); +#ifdef __PS3__ + Sleep(5); +#endif // __PS3__ + return false; + } + + // If there was more than one very near thing found in our initial assessment, then return true so that we will keep doing the other one(s) + // in an atomic unit + if( veryNearCount > 1 ) + { + destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount ); + return true; + } + // If the chunk we've just built was near, and it has been marked dirty at some point while we are rebuilding, also return true so + // we can rebuild the same thing atomically - if its data was changed during creating render data, it may well be invalid + if( ( veryNearCount == 1 ) && getGlobalChunkFlag(chunk->x, chunk->y, chunk->z, chunk->level, CHUNK_FLAG_DIRTY ) ) + { + destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount + 1); + return true; + } + + if( nearChunk ) destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount ); + + return false; +} + + +void LevelRenderer::renderHit(shared_ptr player, HitResult *h, int mode, shared_ptr inventoryItem, float a) +{ + Tesselator *t = Tesselator::getInstance(); + glEnable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1, 1, 1, ((float) (Mth::sin(Minecraft::currentTimeMillis() / 100.0f)) * 0.2f + 0.4f) * 0.5f); + if (mode != 0 && inventoryItem != NULL) + { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + float br = (Mth::sin(Minecraft::currentTimeMillis() / 100.0f) * 0.2f + 0.8f); + glColor4f(br, br, br, (Mth::sin(Minecraft::currentTimeMillis() / 200.0f) * 0.2f + 0.5f)); + + textures->bindTexture(TN_TERRAIN); //L"/terrain.png"); + } + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); +} + +void LevelRenderer::renderDestroyAnimation(Tesselator *t, shared_ptr player, float a) +{ + double xo = player->xOld + (player->x - player->xOld) * a; + double yo = player->yOld + (player->y - player->yOld) * a; + double zo = player->zOld + (player->z - player->zOld) * a; + + int playerIndex = mc->player->GetXboxPad(); + if (!destroyingBlocks.empty()) + { + glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); + + textures->bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + glColor4f(1, 1, 1, 0.5f); + glPushMatrix(); + + glDisable(GL_ALPHA_TEST); + + glPolygonOffset(-3.0f, -3.0f); + glEnable(GL_POLYGON_OFFSET_FILL); + + glEnable(GL_ALPHA_TEST); + t->begin(); +#ifdef __PSVITA__ + // AP : fix for bug 4952. No amount of polygon offset will push this close enough to be seen above the second tile layer when looking straight down + // so just add on a little bit of y to fix this. hacky hacky + t->offset((float)-xo, (float)-yo + 0.01f,(float) -zo); +#else + t->offset((float)-xo, (float)-yo,(float) -zo); +#endif + t->noColor(); + + AUTO_VAR(it, destroyingBlocks.begin()); + while (it != destroyingBlocks.end()) + { + BlockDestructionProgress *block = it->second; + double xd = block->getX() - xo; + double yd = block->getY() - yo; + double zd = block->getZ() - zo; + + if (xd * xd + yd * yd + zd * zd < 32 * 32) // 4J MGH - now only culling instead of removing, as the list is shared in split screen + { + int iPad = mc->player->GetXboxPad(); // 4J added + int tileId = level[iPad]->getTile(block->getX(), block->getY(), block->getZ()); + Tile *tile = tileId > 0 ? Tile::tiles[tileId] : NULL; + if (tile == NULL) tile = Tile::rock; + tileRenderer[iPad]->tesselateInWorldFixedTexture(tile, block->getX(), block->getY(), block->getZ(), breakingTextures[block->getProgress()]); // 4J renamed to differentiate from tesselateInWorld + } + ++it; + } + + t->end(); + t->offset(0, 0, 0); + glDisable(GL_ALPHA_TEST); + /* + * for (int i = 0; i < 6; i++) { tile.renderFace(t, h.x, h.y, + * h.z, i, 15 * 16 + (int) (destroyProgress * 10)); } + */ + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_ALPHA_TEST); + + glDepthMask(true); + glPopMatrix(); + } +} + +void LevelRenderer::renderHitOutline(shared_ptr player, HitResult *h, int mode, shared_ptr inventoryItem, float a) +{ + + if (mode == 0 && h->type == HitResult::TILE) + { + int iPad = mc->player->GetXboxPad(); // 4J added + + // 4J-PB - If Display HUD is false, don't render the hit outline + if ( app.GetGameSettings(iPad,eGameSetting_DisplayHUD)==0 ) return; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0, 0, 0, 0.4f); + glLineWidth(2.0f); + glDisable(GL_TEXTURE_2D); + glDepthMask(false); + float ss = 0.002f; + int tileId = level[iPad]->getTile(h->x, h->y, h->z); + + if (tileId > 0) + { + Tile::tiles[tileId]->updateShape(level[iPad], h->x, h->y, h->z); + double xo = player->xOld + (player->x - player->xOld) * a; + double yo = player->yOld + (player->y - player->yOld) * a; + double zo = player->zOld + (player->z - player->zOld) * a; + render(Tile::tiles[tileId]->getTileAABB(level[iPad], h->x, h->y, h->z)->grow(ss, ss, ss)->cloneMove(-xo, -yo, -zo)); + } + glDepthMask(true); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } +} + +void LevelRenderer::render(AABB *b) +{ + Tesselator *t = Tesselator::getInstance(); + + t->begin(GL_LINE_STRIP); + t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); + t->end(); + + t->begin(GL_LINE_STRIP); + t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); + t->end(); + + t->begin(GL_LINES); + t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); + t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z0)); + t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z1)); + t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z1)); + t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z1)); + t->end(); +} + +void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) // 4J - added level param +{ + // 4J - level is passed if this is coming from setTilesDirty, which could come from when connection is being ticked outside of normal level tick, and player won't + // be set up + if( level == NULL ) level = this->level[mc->player->GetXboxPad()]; + // EnterCriticalSection(&m_csDirtyChunks); + int _x0 = Mth::intFloorDiv(x0, CHUNK_XZSIZE); + int _y0 = Mth::intFloorDiv(y0, CHUNK_SIZE); + int _z0 = Mth::intFloorDiv(z0, CHUNK_XZSIZE); + int _x1 = Mth::intFloorDiv(x1, CHUNK_XZSIZE); + int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE); + int _z1 = Mth::intFloorDiv(z1, CHUNK_XZSIZE); + + for (int x = _x0; x <= _x1; x++) + { + for (int y = _y0; y <= _y1; y++) + { + for (int z = _z0; z <= _z1; z++) + { + // printf("Setting %d %d %d dirty\n",x,y,z); + int index = getGlobalIndexForChunk(x * 16, y * 16, z * 16, level); + // Rather than setting the flags directly, add any dirty chunks into a lock free stack - this avoids having to lock m_csDirtyChunks . + // These chunks are then added to the global flags in the render update thread. + // An XLockFreeQueue actually implements a queue of pointers to its templated type, and I don't want to have to go allocating ints here just to store the + // pointer to them in a queue. Hence actually pretending that the int Is a pointer here. Our Index has a a valid range from 0 to something quite big, + // but including zero. The lock free queue, since it thinks it is dealing with pointers, uses a NULL pointer to signify that a Pop hasn't succeeded. + // We also want to reserve one special value (of 1 ) for use when multiple chunks not individually listed are made dirty. Therefore adding 2 to our + // index value here to move our valid range from 1 to something quite big + 2 + if( index > -1 ) + { +#ifdef _CRITICAL_CHUNKS + index += 2; + + // AP - by the time we reach this function the area passed in has a 1 block border added to it to make sure geometry and lighting is updated correctly. + // Some of those blocks will only need lighting updated so it is acceptable to not have those blocks grouped in the deferral system as the mismatch + // will hardly be noticable. The blocks that need geometry updated will be adjacent to the original, non-bordered area. + // This bit of code will mark a chunk as 'non-critical' if all of the blocks inside it are NOT adjacent to the original area. This has the greatest effect + // when digging a single block. Only 6 of the blocks out of the possible 26 are actually adjacent to the original block. The other 20 only need lighting updated. + // Note I have noticed a new side effect of this system where it's possible to see into the sides of water but this is acceptable compared to seeing through + // the entire landscape. + // is the left or right most block just inside this chunk + if( ((x0 & 15) == 15 && x == _x0) || ((x1 & 15) == 0 && x == _x1) ) + { + // is the front, back, top or bottom most block just inside this chunk + if( ((z0 & 15) == 15 && z == _z0) || ((z1 & 15) == 0 && z == _z1) || + ((y0 & 15) == 15 && y == _y0) || ((y1 & 15) == 0 && y == _y1)) + { + index |= 0x10000000; + } + } + else + { + // is the front or back most block just inside this chunk + if( ((z0 & 15) == 15 && z == _z0) || ((z1 & 15) == 0 && z == _z1) ) + { + // is the top or bottom most block just inside this chunk + if( ((y0 & 15) == 15 && y == _y0) || ((y1 & 15) == 0 && y == _y1)) + { + index |= 0x10000000; + } + } + } + + dirtyChunksLockFreeStack.Push((int *)(index)); +#else + dirtyChunksLockFreeStack.Push((int *)(index + 2)); +#endif + +#ifdef _XBOX + PIXSetMarker(0,"Setting chunk %d %d %d dirty",x * 16,y * 16,z * 16); +#else + PIXSetMarkerDeprecated(0,"Setting chunk %d %d %d dirty",x * 16,y * 16,z * 16); +#endif + } + // setGlobalChunkFlag(x * 16, y * 16, z * 16, level, CHUNK_FLAG_DIRTY); + } + } + } + // LeaveCriticalSection(&m_csDirtyChunks); +} + +void LevelRenderer::tileChanged(int x, int y, int z) +{ + setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1, NULL); +} + +void LevelRenderer::tileLightChanged(int x, int y, int z) +{ + setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1, NULL); +} + +void LevelRenderer::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) // 4J - added level param +{ + setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level); +} + +bool inline clip(float *bb, float *frustum) +{ + for (int i = 0; i < 6; ++i, frustum += 4) + { + if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; + + return false; + } + + return true; +} + +#ifdef __PS3__ +int g_listArray_layer0[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); // 8000 +int g_listArray_layer1[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); +float g_zDepth_layer0[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); // 8000 +float g_zDepth_layer1[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); + +volatile bool g_useIdent = false; +volatile float g_maxDepthRender = 1000; +volatile float g_maxHeightRender = -1000; +volatile float g_offMulVal = 1; + +void LevelRenderer::cull_SPU(int playerIndex, Culler *culler, float a) +{ + if(m_bSPUCullStarted[playerIndex]) + { + return; // running already + } + + FrustumCuller *fc = (FrustumCuller *)culler; + FrustumData *fd = fc->frustum; + float fdraw[6 * 4]; + for( int i = 0; i < 6; i++ ) + { + double fx = fd->m_Frustum[i][0]; + double fy = fd->m_Frustum[i][1]; + double fz = fd->m_Frustum[i][2]; + fdraw[i * 4 + 0] = (float)fx; + fdraw[i * 4 + 1] = (float)fy; + fdraw[i * 4 + 2] = (float)fz; + fdraw[i * 4 + 3] = (float)(fd->m_Frustum[i][3] + ( fx * -fc->xOff ) + ( fy * - fc->yOff ) + ( fz * -fc->zOff )); + } + + memcpy(&g_cullDataIn[playerIndex].fdraw, fdraw, sizeof(fdraw)); + g_cullDataIn[playerIndex].numClipChunks = chunks[playerIndex].length; + g_cullDataIn[playerIndex].pClipChunks = (ClipChunk_SPU*)chunks[playerIndex].data; + g_cullDataIn[playerIndex].numGlobalChunks = getGlobalChunkCount(); + g_cullDataIn[playerIndex].pGlobalChunkFlags = globalChunkFlags; + g_cullDataIn[playerIndex].chunkLists = chunkLists; + g_cullDataIn[playerIndex].listArray_layer0 = g_listArray_layer0[playerIndex]; + g_cullDataIn[playerIndex].listArray_layer1 = g_listArray_layer1[playerIndex]; + g_cullDataIn[playerIndex].zDepth_layer0 = g_zDepth_layer0[playerIndex]; + g_cullDataIn[playerIndex].zDepth_layer1 = g_zDepth_layer1[playerIndex]; + g_cullDataIn[playerIndex].maxDepthRender = g_maxDepthRender; + g_cullDataIn[playerIndex].maxHeightRender = g_maxHeightRender; + + if(g_useIdent) + g_cullDataIn[playerIndex].clipMat = Vectormath::Aos::Matrix4::identity(); + else + { + memcpy(&g_cullDataIn[playerIndex].clipMat, &fc->frustum->modl[0], sizeof(float) * 16); + g_cullDataIn[playerIndex].clipMat[3][0] = -fc->xOff; + g_cullDataIn[playerIndex].clipMat[3][1] = -fc->yOff; + g_cullDataIn[playerIndex].clipMat[3][2] = -fc->zOff; + } + + + C4JSpursJob_LevelRenderer_cull cullJob(&g_cullDataIn[playerIndex]); + C4JSpursJob_LevelRenderer_zSort sortJob(&g_cullDataIn[playerIndex]); + + m_jobPort_CullSPU->submitJob(&cullJob); + m_jobPort_CullSPU->submitSync(); + // static int doSort = false; + // if(doSort) + { + m_jobPort_CullSPU->submitJob(&sortJob); + } + // doSort ^= 1; + m_bSPUCullStarted[playerIndex] = true; +} +void LevelRenderer::waitForCull_SPU() +{ + m_jobPort_CullSPU->waitForCompletion(); + int playerIndex = mc->player->GetXboxPad(); // 4J added + m_bSPUCullStarted[playerIndex] = false; +} +#endif // __PS3__ + +void LevelRenderer::cull(Culler *culler, float a) +{ + int playerIndex = mc->player->GetXboxPad(); // 4J added + +#if defined __PS3__ && !defined DISABLE_SPU_CODE + cull_SPU(playerIndex, culler, a); + return; +#endif // __PS3__ + + + FrustumCuller *fc = (FrustumCuller *)culler; + FrustumData *fd = fc->frustum; + float fdraw[6 * 4]; + for( int i = 0; i < 6; i++ ) + { + double fx = fd->m_Frustum[i][0]; + double fy = fd->m_Frustum[i][1]; + double fz = fd->m_Frustum[i][2]; + fdraw[i * 4 + 0] = (float)fx; + fdraw[i * 4 + 1] = (float)fy; + fdraw[i * 4 + 2] = (float)fz; + fdraw[i * 4 + 3] = (float)(fd->m_Frustum[i][3] + ( fx * -fc->xOff ) + ( fy * - fc->yOff ) + ( fz * -fc->zOff )); + } + + ClipChunk *pClipChunk = chunks[playerIndex].data; + int vis = 0; + int total = 0; + int numWrong = 0; + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) + { + unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ]; + + if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) ) + { + bool clipres = clip(pClipChunk->aabb, fdraw); + pClipChunk->visible = clipres; + if( pClipChunk->visible ) vis++; + total++; + } + else + { + pClipChunk->visible = false; + } + pClipChunk++; + } +} + +void LevelRenderer::playStreamingMusic(const wstring& name, int x, int y, int z) +{ + if (name != L"") + { + mc->gui->setNowPlaying(L"C418 - " + name); + } + mc->soundEngine->playStreaming(name, (float) x, (float) y, (float) z, 1, 1); +} + +void LevelRenderer::playSound(int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist) +{ + // 4J-PB - removed in 1.4 + + //float dd = 16; + /*if (volume > 1) fSoundClipDist *= volume; + + // 4J - find min distance to any players rather than just the current one + float minDistSq = FLT_MAX; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( mc->localplayers[i] ) + { + float distSq = mc->localplayers[i]->distanceToSqr(x, y, z ); + if( distSq < minDistSq ) + { + minDistSq = distSq; + } + } + } + + if (minDistSq < fSoundClipDist * fSoundClipDist) + { + mc->soundEngine->play(iSound, (float) x, (float) y, (float) z, volume, pitch); + } */ +} + +void LevelRenderer::playSound(shared_ptr entity,int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist) +{ +} + +// 4J-PB - original function. I've changed to an enum instead of string compares +// 4J removed - +/* +void LevelRenderer::addParticle(const wstring& name, double x, double y, double z, double xa, double ya, double za) +{ +if (mc == NULL || mc->cameraTargetPlayer == NULL || mc->particleEngine == NULL) return; + +double xd = mc->cameraTargetPlayer->x - x; +double yd = mc->cameraTargetPlayer->y - y; +double zd = mc->cameraTargetPlayer->z - z; + +double particleDistance = 16; +if (xd * xd + yd * yd + zd * zd > particleDistance * particleDistance) return; + +int playerIndex = mc->player->GetXboxPad(); // 4J added + +if (name== L"bubble") mc->particleEngine->add(shared_ptr( new BubbleParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"smoke") mc->particleEngine->add(shared_ptr( new SmokeParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"note") mc->particleEngine->add(shared_ptr( new NoteParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"portal") mc->particleEngine->add(shared_ptr( new PortalParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"explode") mc->particleEngine->add(shared_ptr( new ExplodeParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"flame") mc->particleEngine->add(shared_ptr( new FlameParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"lava") mc->particleEngine->add(shared_ptr( new LavaParticle(level[playerIndex], x, y, z) ) ); +else if (name== L"footstep") mc->particleEngine->add(shared_ptr( new FootstepParticle(textures, level[playerIndex], x, y, z) ) ); +else if (name== L"splash") mc->particleEngine->add(shared_ptr( new SplashParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"largesmoke") mc->particleEngine->add(shared_ptr( new SmokeParticle(level[playerIndex], x, y, z, xa, ya, za, 2.5f) ) ); +else if (name== L"reddust") mc->particleEngine->add(shared_ptr( new RedDustParticle(level[playerIndex], x, y, z, (float) xa, (float) ya, (float) za) ) ); +else if (name== L"snowballpoof") mc->particleEngine->add(shared_ptr( new BreakingItemParticle(level[playerIndex], x, y, z, Item::snowBall) ) ); +else if (name== L"snowshovel") mc->particleEngine->add(shared_ptr( new SnowShovelParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +else if (name== L"slime") mc->particleEngine->add(shared_ptr( new BreakingItemParticle(level[playerIndex], x, y, z, Item::slimeBall)) ) ; +else if (name== L"heart") mc->particleEngine->add(shared_ptr( new HeartParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); +} +*/ + +void LevelRenderer::addParticle(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za) +{ + addParticleInternal( eParticleType, x, y, z, xa, ya, za ); +} + +shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za) +{ + if (mc == NULL || mc->cameraTargetPlayer == NULL || mc->particleEngine == NULL) + { + return nullptr; + } + + // 4J added - do some explicit checking for NaN. The normal depth clipping seems to generally work for NaN (ie they get rejected), except on optimised PS3 code which + // reverses the logic on the comparison with particleDistanceSquared and gets the opposite result to what you might expect. + if( Double::isNaN(x) ) return nullptr; + if( Double::isNaN(y) ) return nullptr; + if( Double::isNaN(z) ) return nullptr; + + int particleLevel = mc->options->particles; + + Level *lev; + int playerIndex = mc->player->GetXboxPad(); // 4J added + lev = level[playerIndex]; + + if (particleLevel == 1) + { + // when playing at "decreased" particle level, randomly filter + // particles by setting the level to "minimal" + if (level[playerIndex]->random->nextInt(3) == 0) + { + particleLevel = 2; + } + } + + // 4J - the java code doesn't distance cull these two particle types, we need to implement this behaviour differently as our distance check is + // mixed up with other things + bool distCull = true; + if ( (eParticleType == eParticleType_hugeexplosion) || (eParticleType == eParticleType_largeexplode) || (eParticleType == eParticleType_dragonbreath) ) + { + distCull = false; + } + + // 4J - this is a bit of hack to get communication through from the level itself, but if Minecraft::animateTickLevel is NULL then + // we are to behave as normal, and if it is set, then we should use that as a pointer to the level the particle is to be created with + // rather than try to work it out from the current player. This is because in this state we are calling from a loop that is trying + // to amalgamate particle creation between all players for a particular level. Also don't do distance clipping as it isn't for a particular + // player, and distance is already taken into account before we get here anyway by the code in Level::animateTickDoWork + if( mc->animateTickLevel == NULL ) + { + double particleDistanceSquared = 16 * 16; + double xd = 0.0f; + double yd = 0.0f; + double zd = 0.0f; + + // 4J Stu - Changed this as we need to check all local players in case one of them is in range of this particle + // Fix for #13454 - art : note blocks do not show notes + bool inRange = false; + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + shared_ptr thisPlayer = mc->localplayers[i]; + if(thisPlayer != NULL && level[i] == lev) + { + xd = thisPlayer->x - x; + yd = thisPlayer->y - y; + zd = thisPlayer->z - z; + if (xd * xd + yd * yd + zd * zd <= particleDistanceSquared) inRange = true; + } + } + if( (!inRange) && distCull ) return nullptr; + } + else + { + lev = mc->animateTickLevel; + } + + if (particleLevel > 1) + { + // TODO: If any of the particles below are necessary even if + // particles are turned off, then modify this if statement + return nullptr; + } + + shared_ptr particle; + + switch(eParticleType) + { + case eParticleType_hugeexplosion: + particle = shared_ptr(new HugeExplosionSeedParticle(lev, x, y, z, xa, ya, za)); + break; + case eParticleType_largeexplode: + particle = shared_ptr(new HugeExplosionParticle(textures, lev, x, y, z, xa, ya, za)); + break; + + case eParticleType_bubble: + particle = shared_ptr( new BubbleParticle(lev, x, y, z, xa, ya, za) ); + break; + + case eParticleType_suspended: + particle = shared_ptr( new SuspendedParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_depthsuspend: + particle = shared_ptr( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_townaura: + particle = shared_ptr( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_crit: + { + shared_ptr critParticle2 = shared_ptr(new CritParticle2(lev, x, y, z, xa, ya, za)); + critParticle2->CritParticle2PostConstructor(); + particle = shared_ptr( critParticle2 ); + // request from 343 to set pink for the needler in the Halo Texture Pack + // Set particle colour from colour-table. + unsigned int cStart = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_CritStart ); + unsigned int cEnd = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_CritEnd ); + + // If the start and end colours are the same, just set that colour, otherwise random between them + if(cStart==cEnd) + { + critParticle2->SetAgeUniformly(); + particle->setColor( ( (cStart>>16)&0xFF )/255.0f, ( (cStart>>8)&0xFF )/255.0, ( cStart&0xFF )/255.0 ); + } + else + { + float fStart=((float)(cStart&0xFF)); + float fDiff=(float)((cEnd-cStart)&0xFF); + + float fCol = (fStart + (Math::random() * fDiff))/255.0f; + particle->setColor( fCol, fCol, fCol ); + } + } + break; + case eParticleType_magicCrit: + { + shared_ptr critParticle2 = shared_ptr(new CritParticle2(lev, x, y, z, xa, ya, za)); + critParticle2->CritParticle2PostConstructor(); + particle = shared_ptr(critParticle2); + particle->setColor(particle->getRedCol() * 0.3f, particle->getGreenCol() * 0.8f, particle->getBlueCol()); + particle->setNextMiscAnimTex(); + } + break; + case eParticleType_smoke: + particle = shared_ptr( new SmokeParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_endportal: // 4J - Added. + { + SmokeParticle *tmp = new SmokeParticle(lev, x, y, z, xa, ya, za); + + // 4J-JEV: Set particle colour from colour-table. + unsigned int col = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_EnderPortal ); + tmp->setColor( ( (col>>16)&0xFF )/255.0f, ( (col>>8)&0xFF )/255.0, ( col&0xFF )/255.0 ); + + particle = shared_ptr(tmp); + } + break; + case eParticleType_mobSpell: + particle = shared_ptr(new SpellParticle(lev, x, y, z, 0, 0, 0)); + particle->setColor((float) xa, (float) ya, (float) za); + break; + case eParticleType_spell: + particle = shared_ptr( new SpellParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_instantSpell: + particle = shared_ptr(new SpellParticle(lev, x, y, z, xa, ya, za)); + dynamic_pointer_cast(particle)->setBaseTex(9 * 16); + break; + case eParticleType_note: + particle = shared_ptr( new NoteParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_netherportal: + particle = shared_ptr( new NetherPortalParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_ender: + particle = shared_ptr( new EnderParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_enchantmenttable: + particle = shared_ptr(new EchantmentTableParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_explode: + particle = shared_ptr( new ExplodeParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_flame: + particle = shared_ptr( new FlameParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_lava: + particle = shared_ptr( new LavaParticle(lev, x, y, z) ); + break; + case eParticleType_footstep: + particle = shared_ptr( new FootstepParticle(textures, lev, x, y, z) ); + break; + case eParticleType_splash: + particle = shared_ptr( new SplashParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_largesmoke: + particle = shared_ptr( new SmokeParticle(lev, x, y, z, xa, ya, za, 2.5f) ); + break; + case eParticleType_reddust: + particle = shared_ptr( new RedDustParticle(lev, x, y, z, (float) xa, (float) ya, (float) za) ); + break; + case eParticleType_snowballpoof: + particle = shared_ptr( new BreakingItemParticle(lev, x, y, z, Item::snowBall, textures) ); + break; + case eParticleType_dripWater: + particle = shared_ptr( new DripParticle(lev, x, y, z, Material::water) ); + break; + case eParticleType_dripLava: + particle = shared_ptr( new DripParticle(lev, x, y, z, Material::lava) ); + break; + case eParticleType_snowshovel: + particle = shared_ptr( new SnowShovelParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_slime: + particle = shared_ptr( new BreakingItemParticle(lev, x, y, z, Item::slimeBall, textures)); + break; + case eParticleType_heart: + particle = shared_ptr( new HeartParticle(lev, x, y, z, xa, ya, za) ); + break; + case eParticleType_angryVillager: + particle = shared_ptr( new HeartParticle(lev, x, y + 0.5f, z, xa, ya, za) ); + particle->setMiscTex(1 + 16 * 5); + particle->setColor(1, 1, 1); + break; + case eParticleType_happyVillager: + particle = shared_ptr( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); + particle->setMiscTex(2 + 16 * 5); + particle->setColor(1, 1, 1); + break; + case eParticleType_dragonbreath: + particle = shared_ptr( new DragonBreathParticle(lev, x, y, z, xa, ya, za) ); + break; + default: + if( ( eParticleType >= eParticleType_iconcrack_base ) && ( eParticleType <= eParticleType_iconcrack_last ) ) + { + int id = PARTICLE_CRACK_ID(eParticleType), data = PARTICLE_CRACK_DATA(eParticleType); + particle = shared_ptr(new BreakingItemParticle(lev, x, y, z, xa, ya, za, Item::items[id], textures, data)); + } + else if( ( eParticleType >= eParticleType_tilecrack_base ) && ( eParticleType <= eParticleType_tilecrack_last ) ) + { + int id = PARTICLE_CRACK_ID(eParticleType), data = PARTICLE_CRACK_DATA(eParticleType); + particle = dynamic_pointer_cast( shared_ptr(new TerrainParticle(lev, x, y, z, xa, ya, za, Tile::tiles[id], 0, data, textures))->init(data) ); + } + } + + if (particle != NULL) + { + mc->particleEngine->add(particle); + } + + return particle; +} + +void LevelRenderer::entityAdded(shared_ptr entity) +{ + entity->prepareCustomTextures(); + // 4J - these empty string comparisons used to check for NULL references, but we don't have string pointers (currently) in entities, + // hopefully this should be equivalent + /* 4J - removed temp */ + //if (entity->customTextureUrl != L"") textures->addHttpTexture(entity->customTextureUrl, new MobSkinTextureProcessor()); + //if (entity->customTextureUrl2 != L"") textures->addHttpTexture(entity->customTextureUrl2, new MobSkinTextureProcessor()); + + // 4J-PB - adding these from global title storage + if (entity->customTextureUrl != L"") + { + textures->addMemTexture(entity->customTextureUrl, new MobSkinMemTextureProcessor()); + } + if (entity->customTextureUrl2 != L"") + { + textures->addMemTexture(entity->customTextureUrl2, new MobSkinMemTextureProcessor()); + } + + // if (entity->customTextureUrl2 != L"") textures->addHttpTexture(entity->customTextureUrl2, new MobSkinTextureProcessor()); + + +} + +void LevelRenderer::entityRemoved(shared_ptr entity) +{ + /* 4J - removed temp + if (entity->customTextureUrl != L"") textures->removeHttpTexture(entity->customTextureUrl); + if (entity->customTextureUrl2 != L"") textures->removeHttpTexture(entity->customTextureUrl2); + */ + if (entity->customTextureUrl != L"") + { + textures->removeMemTexture(entity->customTextureUrl); + } + if (entity->customTextureUrl2 != L"") + { + textures->removeMemTexture(entity->customTextureUrl2); + } +} + +void LevelRenderer::skyColorChanged() +{ + // 4J - no longer used +#if 0 + EnterCriticalSection(&m_csDirtyChunks); + for( int i = 0; i < getGlobalChunkCountForOverworld(); i++ ) + { + if( ( globalChunkFlags[i] & CHUNK_FLAG_NOTSKYLIT ) == 0 ) + { + globalChunkFlags[i] |= CHUNK_FLAG_DIRTY; + } + } + LeaveCriticalSection(&m_csDirtyChunks); +#endif +} + +void LevelRenderer::clear() +{ + MemoryTracker::releaseLists(chunkLists); +} + +void LevelRenderer::levelEvent(shared_ptr source, int type, int x, int y, int z, int data) +{ + int playerIndex = mc->player->GetXboxPad(); // 4J added + Random *random = level[playerIndex]->random; + switch (type) + { + //case LevelEvent::SOUND_WITHER_BOSS_SPAWN: + case LevelEvent::SOUND_DRAGON_DEATH: + if (mc->cameraTargetPlayer != NULL) + { + // play the sound at an offset from the player + double dx = x - mc->cameraTargetPlayer->x; + double dy = y - mc->cameraTargetPlayer->y; + double dz = z - mc->cameraTargetPlayer->z; + + double len = sqrt(dx * dx + dy * dy + dz * dz); + double sx = mc->cameraTargetPlayer->x; + double sy = mc->cameraTargetPlayer->y; + double sz = mc->cameraTargetPlayer->z; + + if (len > 0) + { + sx += (dx / len) * 2; + sy += (dy / len) * 2; + sz += (dz / len) * 2; + } + + level[playerIndex]->playLocalSound(sx, sy, sz, eSoundType_MOB_ENDERDRAGON_END, 5.0f, 1.0f); + } + break; + case LevelEvent::SOUND_CLICK_FAIL: + //level[playerIndex]->playSound(x, y, z, L"random.click", 1.0f, 1.2f); + level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_CLICK, 1.0f, 1.2f); + break; + case LevelEvent::SOUND_CLICK: + level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_CLICK, 1.0f, 1.0f); + break; + case LevelEvent::SOUND_LAUNCH: + level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_BOW, 1.0f, 1.2f); + break; + case LevelEvent::PARTICLES_SHOOT: + { + int xd = (data % 3) - 1; + int zd = (data / 3 % 3) - 1; + double xp = x + xd * 0.6 + 0.5; + double yp = y + 0.5; + double zp = z + zd * 0.6 + 0.5; + for (int i = 0; i < 10; i++) + { + double pow = random->nextDouble() * 0.2 + 0.01; + double xs = xp + xd * 0.01 + (random->nextDouble() - 0.5) * zd * 0.5; + double ys = yp + (random->nextDouble() - 0.5) * 0.5; + double zs = zp + zd * 0.01 + (random->nextDouble() - 0.5) * xd * 0.5; + double xsa = xd * pow + random->nextGaussian() * 0.01; + double ysa = -0.03 + random->nextGaussian() * 0.01; + double zsa = zd * pow + random->nextGaussian() * 0.01; + addParticle(eParticleType_smoke, xs, ys, zs, xsa, ysa, zsa); + } + break; + } + case LevelEvent::PARTICLES_EYE_OF_ENDER_DEATH: + { + double xp = x + 0.5; + double yp = y; + double zp = z + 0.5; + + ePARTICLE_TYPE particle = PARTICLE_ICONCRACK(Item::eyeOfEnder->id,0); + for (int i = 0; i < 8; i++) + { + addParticle(particle, xp, yp, zp, random->nextGaussian() * 0.15, random->nextDouble() * 0.2, random->nextGaussian() * .15); + } + for (double a = 0; a < PI * 2.0; a += PI * 0.05) + { + addParticle(eParticleType_ender, xp + cos(a) * 5, yp - .4, zp + sin(a) * 5, cos(a) * -5, 0, sin(a) * -5); + addParticle(eParticleType_ender, xp + cos(a) * 5, yp - .4, zp + sin(a) * 5, cos(a) * -7, 0, sin(a) * -7); + } + + } + break; + case LevelEvent::PARTICLES_POTION_SPLASH: + { + double xp = x; + double yp = y; + double zp = z; + + ePARTICLE_TYPE particle = PARTICLE_ICONCRACK(Item::potion->id,0); + for (int i = 0; i < 8; i++) + { + addParticle(particle, xp, yp, zp, random->nextGaussian() * 0.15, random->nextDouble() * 0.2, random->nextGaussian() * 0.15); + } + + + int colorValue = Item::potion->getColor(data); + + float red = (float) ((colorValue >> 16) & 0xff) / 255.0f; + float green = (float) ((colorValue >> 8) & 0xff) / 255.0f; + float blue = (float) ((colorValue >> 0) & 0xff) / 255.0f; + + ePARTICLE_TYPE particleName = eParticleType_spell; + if (Item::potion->hasInstantenousEffects(data)) + { + particleName = eParticleType_instantSpell; + } + + for (int i = 0; i < 100; i++) + { + double dist = random->nextDouble() * ThrownPotion::SPLASH_RANGE; + double angle = random->nextDouble() * PI * 2; + double xs = cos(angle) * dist; + double ys = 0.01 + random->nextDouble() * 0.5; + double zs = sin(angle) * dist; + + shared_ptr spellParticle = addParticleInternal(particleName, xp + xs * 0.1, yp + 0.3, zp + zs * 0.1, xs, ys, zs); + if (spellParticle != NULL) + { + float randBrightness = 0.75f + random->nextFloat() * 0.25f; + spellParticle->setColor(red * randBrightness, green * randBrightness, blue * randBrightness); + spellParticle->setPower((float) dist); + } + } + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_GLASS, 1, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + } + break; + case LevelEvent::ENDERDRAGON_FIREBALL_SPLASH: + { + double xp = x; + double yp = y; + double zp = z; + + ePARTICLE_TYPE particleName = eParticleType_dragonbreath; + + for (int i = 0; i < 200; i++) + { + double dist = random->nextDouble() * DragonFireball::SPLASH_RANGE; + double angle = random->nextDouble() * PI * 2; + double xs = cos(angle) * dist; + double ys = 0.01 + random->nextDouble() * 0.5; + double zs = sin(angle) * dist; + + shared_ptr acidParticle = addParticleInternal(particleName, xp + xs * 0.1, yp + 0.3, zp + zs * 0.1, xs, ys, zs); + if (acidParticle != NULL) + { + float randBrightness = 0.75f + random->nextFloat() * 0.25f; + acidParticle->setPower((float) dist); + } + } + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_EXPLODE, 1, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + } + break; + case LevelEvent::PARTICLES_DESTROY_BLOCK: + { + int t = data & Tile::TILE_NUM_MASK; + if (t > 0) + { + Tile *oldTile = Tile::tiles[t]; + mc->soundEngine->play(oldTile->soundType->getBreakSound(), x + 0.5f, y + 0.5f, z + 0.5f, (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f); + } + + mc->particleEngine->destroy(x, y, z, data & Tile::TILE_NUM_MASK, (data >> Tile::TILE_NUM_SHIFT) & 0xff); + break; + } + case LevelEvent::PARTICLES_MOBTILE_SPAWN: + { + for (int i = 0; i < 20; i++) + { + + double xP = x + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; + double yP = y + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; + double zP = z + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; + + level[playerIndex]->addParticle(eParticleType_smoke, xP, yP, zP, 0, 0, 0); + level[playerIndex]->addParticle(eParticleType_flame, xP, yP, zP, 0, 0, 0); + } + break; + } + case LevelEvent::SOUND_OPEN_DOOR: + if (Math::random() < 0.5) + { + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_DOOR_OPEN, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + } else { + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_DOOR_CLOSE, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + } + break; + case LevelEvent::SOUND_FIZZ: + level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_FIZZ, 0.5f, 2.6f + (random->nextFloat() - random->nextFloat()) * 0.8f); + break; + case LevelEvent::SOUND_ANVIL_BROKEN: + level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_BREAK, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + break; + case LevelEvent::SOUND_ANVIL_USED: + level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_USE, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + break; + case LevelEvent::SOUND_ANVIL_LAND: + level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_LAND, 0.3f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); + break; + case LevelEvent::SOUND_PLAY_RECORDING: + { + RecordingItem *rci = dynamic_cast(Item::items[data]); + if (rci != NULL) + { + level[playerIndex]->playStreamingMusic(rci->recording, x, y, z); + } + else + { + // 4J-PB - only play streaming music if there isn't already some playing - the CD playing may have finished, and game music started playing already + if(!mc->soundEngine->GetIsPlayingStreamingGameMusic()) + { + level[playerIndex]->playStreamingMusic(L"", x, y, z); // 4J - used to pass NULL, but using empty string here now instead + } + } + mc->localplayers[playerIndex]->updateRichPresence(); + } + break; + // 4J - new level event sounds brought forward from 1.2.3 + case LevelEvent::SOUND_GHAST_WARNING: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_GHAST_CHARGE, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f, 80.0f); + break; + case LevelEvent::SOUND_GHAST_FIREBALL: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_GHAST_FIREBALL, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f, 80.0f); + break; + case LevelEvent::SOUND_ZOMBIE_WOODEN_DOOR: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_WOOD, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + break; + case LevelEvent::SOUND_ZOMBIE_DOOR_CRASH: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_WOOD_BREAK, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + break; + case LevelEvent::SOUND_ZOMBIE_IRON_DOOR: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_METAL, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + break; + case LevelEvent::SOUND_ZOMBIE_INFECTED: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_INFECT, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); + break; + case LevelEvent::SOUND_ZOMBIE_CONVERTED: + level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_UNFECT, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); + break; + // 4J Added TU9 to fix #77475 - TU9: Content: Art: Dragon egg teleport particle effect isn't present. + case LevelEvent::END_EGG_TELEPORT: + // 4J Added to show the paricles when the End egg teleports after being attacked + EggTile::generateTeleportParticles(level[playerIndex],x,y,z,data); + break; + } + +} + +void LevelRenderer::destroyTileProgress(int id, int x, int y, int z, int progress) +{ + if (progress < 0 || progress >= 10) + { + AUTO_VAR(it, destroyingBlocks.find(id)); + if(it != destroyingBlocks.end()) + { + delete it->second; + destroyingBlocks.erase(it); + } + //destroyingBlocks.remove(id); + } + else + { + BlockDestructionProgress *entry = NULL; + + AUTO_VAR(it, destroyingBlocks.find(id)); + if(it != destroyingBlocks.end()) entry = it->second; + + if (entry == NULL || entry->getX() != x || entry->getY() != y || entry->getZ() != z) + { + entry = new BlockDestructionProgress(id, x, y, z); + destroyingBlocks.insert( unordered_map::value_type(id, entry) ); + } + + entry->setProgress(progress); + entry->updateTick(ticks); + } +} + +void LevelRenderer::registerTextures(IconRegister *iconRegister) +{ + breakingTextures = new Icon*[10]; + + for (int i = 0; i < 10; i++) + { + breakingTextures[i] = iconRegister->registerIcon(L"destroy_" + _toString(i) ); + } +} + +// Gets a dimension index (0, 1, or 2) from an id ( 0, -1, 1) +int LevelRenderer::getDimensionIndexFromId(int id) +{ + return ( 3 - id ) % 3; +} + +// 4J - added for new render list handling. Render lists used to be allocated per chunk, but these are now allocated per fixed chunk position +// in our (now finite) maps. +int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, Level *level) +{ + return getGlobalIndexForChunk(x,y,z,level->dimension->id); +} + +int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, int dimensionId) +{ + int dimIdx = getDimensionIndexFromId(dimensionId); + int xx = ( x / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 ); + int yy = y / CHUNK_SIZE; + int zz = ( z / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 ); + + if( ( xx < 0 ) || ( xx >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1; + if( ( zz < 0 ) || ( zz >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1; + if( ( yy < 0 ) || ( yy >= CHUNK_Y_COUNT ) ) return -1; + + int dimOffset = DIMENSION_OFFSETS[dimIdx]; + + int offset = dimOffset; // Offset caused by current dimension + offset += ( zz * MAX_LEVEL_RENDER_SIZE[dimIdx] + xx ) * CHUNK_Y_COUNT; // Offset by x/z pos + offset += yy; // Offset by y pos + + return offset; +} + +bool LevelRenderer::isGlobalIndexInSameDimension( int idx, Level *level) +{ + int dim = getDimensionIndexFromId(level->dimension->id); + int idxDim = 0; + if( idx >= DIMENSION_OFFSETS[2] ) idxDim = 2; + else if ( idx >= DIMENSION_OFFSETS[1] ) idxDim = 1; + return (dim == idxDim); +} + +int LevelRenderer::getGlobalChunkCount() +{ + return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT ) + + ( MAX_LEVEL_RENDER_SIZE[1] * MAX_LEVEL_RENDER_SIZE[1] * CHUNK_Y_COUNT ) + + ( MAX_LEVEL_RENDER_SIZE[2] * MAX_LEVEL_RENDER_SIZE[2] * CHUNK_Y_COUNT ); +} + +int LevelRenderer::getGlobalChunkCountForOverworld() +{ + return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT ); +} + +unsigned char LevelRenderer::getGlobalChunkFlags(int x, int y, int z, Level *level) +{ + int index = getGlobalIndexForChunk(x, y, z, level); + if( index == -1 ) + { + return 0; + } + else + { + return globalChunkFlags[ index ]; + } +} + +void LevelRenderer::setGlobalChunkFlags(int x, int y, int z, Level *level, unsigned char flags) +{ + int index = getGlobalIndexForChunk(x, y, z, level); + if( index != -1 ) + { +#ifdef _LARGE_WORLDS + EnterCriticalSection(&m_csChunkFlags); +#endif + globalChunkFlags[ index ] = flags; +#ifdef _LARGE_WORLDS + LeaveCriticalSection(&m_csChunkFlags); +#endif + } +} + +void LevelRenderer::setGlobalChunkFlag(int index, unsigned char flag, unsigned char shift) +{ + unsigned char sflag = flag << shift; + + if( index != -1 ) + { +#ifdef _LARGE_WORLDS + EnterCriticalSection(&m_csChunkFlags); +#endif + globalChunkFlags[ index ] |= sflag; +#ifdef _LARGE_WORLDS + LeaveCriticalSection(&m_csChunkFlags); +#endif + } +} + +void LevelRenderer::setGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) +{ + unsigned char sflag = flag << shift; + int index = getGlobalIndexForChunk(x, y, z, level); + if( index != -1 ) + { +#ifdef _LARGE_WORLDS + EnterCriticalSection(&m_csChunkFlags); +#endif + globalChunkFlags[ index ] |= sflag; +#ifdef _LARGE_WORLDS + LeaveCriticalSection(&m_csChunkFlags); +#endif + } +} + +void LevelRenderer::clearGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) +{ + unsigned char sflag = flag << shift; + int index = getGlobalIndexForChunk(x, y, z, level); + if( index != -1 ) + { +#ifdef _LARGE_WORLDS + EnterCriticalSection(&m_csChunkFlags); +#endif + globalChunkFlags[ index ] &= ~sflag; +#ifdef _LARGE_WORLDS + LeaveCriticalSection(&m_csChunkFlags); +#endif + } +} + +bool LevelRenderer::getGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) +{ + unsigned char sflag = flag << shift; + int index = getGlobalIndexForChunk(x, y, z, level); + if( index == -1 ) + { + return false; + } + else + { + return ( globalChunkFlags[ index ] & sflag ) == sflag; + } +} + +unsigned char LevelRenderer::incGlobalChunkRefCount(int x, int y, int z, Level *level) +{ + int index = getGlobalIndexForChunk(x, y, z, level); + if( index != -1 ) + { + unsigned char flags = globalChunkFlags[ index ]; + unsigned char refCount = (flags >> CHUNK_FLAG_REF_SHIFT ) & CHUNK_FLAG_REF_MASK; + refCount++; + flags &= ~(CHUNK_FLAG_REF_MASK<> CHUNK_FLAG_REF_SHIFT ) & CHUNK_FLAG_REF_MASK; + refCount--; + flags &= ~(CHUNK_FLAG_REF_MASK<second.end()); + for( AUTO_VAR(it2, it->second.begin()); it2 != itTEEnd; it2++ ) + { + (*it2)->upgradeRenderRemoveStage(); + } + } + LeaveCriticalSection(&m_csRenderableTileEntities); +} + +LevelRenderer::DestroyedTileManager::RecentTile::RecentTile(int x, int y, int z, Level *level) : x(x), y(y), z(z), level(level) +{ + timeout_ticks = 20; + rebuilt = false; +} + +LevelRenderer::DestroyedTileManager::RecentTile::~RecentTile() +{ + for( AUTO_VAR(it, boxes.begin()); it!= boxes.end(); it++ ) + { + delete *it; + } +} + +LevelRenderer::DestroyedTileManager::DestroyedTileManager() +{ + InitializeCriticalSection(&m_csDestroyedTiles); +} + +LevelRenderer::DestroyedTileManager::~DestroyedTileManager() +{ + DeleteCriticalSection(&m_csDestroyedTiles); + for( unsigned int i = 0; i < m_destroyedTiles.size(); i++ ) + { + delete m_destroyedTiles[i]; + } +} + + +// For game to let this manager know that a tile is about to be destroyed (must be called before it actually is) +void LevelRenderer::DestroyedTileManager::destroyingTileAt( Level *level, int x, int y, int z ) +{ + EnterCriticalSection(&m_csDestroyedTiles); + + // Store a list of AABBs that the tile to be destroyed would have made, before we go and destroy it. This + // is made slightly more complicated as the addAABBs method for tiles adds temporary AABBs and we need permanent + // ones, so make a temporary list and then copy over + + RecentTile *recentTile = new RecentTile(x, y, z, level); + AABB *box = AABB::newTemp((float)x, (float)y, (float)z, (float)(x+1), (float)(y+1), (float)(z+1)); + Tile *tile = Tile::tiles[level->getTile(x, y, z)]; + + if (tile != NULL) + { + tile->addAABBs(level, x, y, z, box, &recentTile->boxes, nullptr); + } + + // Make these temporary AABBs into permanently allocated AABBs + for( unsigned int i = 0; i < recentTile->boxes.size(); i++ ) + { + recentTile->boxes[i] = AABB::newPermanent(recentTile->boxes[i]->x0, + recentTile->boxes[i]->y0, + recentTile->boxes[i]->z0, + recentTile->boxes[i]->x1, + recentTile->boxes[i]->y1, + recentTile->boxes[i]->z1); + } + + m_destroyedTiles.push_back( recentTile ); + + LeaveCriticalSection(&m_csDestroyedTiles); +} + +// For chunk rebuilding to inform the manager that a chunk (a 16x16x16 tile render chunk) has been updated +void LevelRenderer::DestroyedTileManager::updatedChunkAt(Level *level, int x, int y, int z, int veryNearCount) +{ + EnterCriticalSection(&m_csDestroyedTiles); + + // There's 2 stages to this. This function is called when a renderer chunk has been rebuilt, but that chunk's render data might be grouped atomically with + // changes to other very near chunks. Therefore, we don't want to consider the render data to be fully updated until the chunk that it is in has been + // rebuilt, AND there aren't any very near things waiting to be rebuilt. + + // First pass through - see if any tiles are within the chunk which is being rebuilt, and mark up by setting their rebuilt flag + bool printed = false; + for( unsigned int i = 0; i < m_destroyedTiles.size(); i++) + { + if( ( m_destroyedTiles[i]->level == level ) && + ( m_destroyedTiles[i]->x >= x ) && ( m_destroyedTiles[i]->x < ( x + 16 ) ) && + ( m_destroyedTiles[i]->y >= y ) && ( m_destroyedTiles[i]->y < ( y + 16 ) ) && + ( m_destroyedTiles[i]->z >= z ) && ( m_destroyedTiles[i]->z < ( z + 16 ) ) ) + { + printed = true; + m_destroyedTiles[i]->rebuilt = true; + } + } + + // Now go through every tile that has been marked up as already being rebuilt, and fully remove it once there aren't going to be any more + // very near chunks. This might not happen on the same call to this function that rebuilt the chunk with the tile in. + if( veryNearCount <= 1 ) + { + for( unsigned int i = 0; i < m_destroyedTiles.size(); ) + { + if( m_destroyedTiles[i]->rebuilt ) + { + printed = true; + delete m_destroyedTiles[i]; + m_destroyedTiles[i] = m_destroyedTiles[m_destroyedTiles.size() - 1]; + m_destroyedTiles.pop_back(); + } + else + { + i++; + } + } + } + + LeaveCriticalSection(&m_csDestroyedTiles); +} + +// For game to get any AABBs that the user should be colliding with as render data has not yet been updated +void LevelRenderer::DestroyedTileManager::addAABBs( Level *level, AABB *box, AABBList *boxes ) +{ + EnterCriticalSection(&m_csDestroyedTiles); + + for( unsigned int i = 0; i < m_destroyedTiles.size(); i++ ) + { + if( m_destroyedTiles[i]->level == level ) + { + for( unsigned int j = 0; j < m_destroyedTiles[i]->boxes.size(); j++ ) + { + // If we find any AABBs intersecting the region we are interested in, add them to the output list, making a temp AABB copy so that we can destroy our own copy + // without worrying about the lifespan of the copy we've passed out + if( m_destroyedTiles[i]->boxes[j]->intersects( box ) ) + { + boxes->push_back(AABB::newTemp( m_destroyedTiles[i]->boxes[j]->x0, + m_destroyedTiles[i]->boxes[j]->y0, + m_destroyedTiles[i]->boxes[j]->z0, + m_destroyedTiles[i]->boxes[j]->x1, + m_destroyedTiles[i]->boxes[j]->y1, + m_destroyedTiles[i]->boxes[j]->z1 ) ); + } + } + } + } + + LeaveCriticalSection(&m_csDestroyedTiles); +} + +void LevelRenderer::DestroyedTileManager::tick() +{ + EnterCriticalSection(&m_csDestroyedTiles); + + // Remove any tiles that have timed out + for( unsigned int i = 0; i < m_destroyedTiles.size(); ) + { + if( --m_destroyedTiles[i]->timeout_ticks == 0 ) + { + delete m_destroyedTiles[i]; + m_destroyedTiles[i] = m_destroyedTiles[m_destroyedTiles.size() - 1]; + m_destroyedTiles.pop_back(); + } + else + { + i++; + } + } + + LeaveCriticalSection(&m_csDestroyedTiles); +} + +#ifdef _LARGE_WORLDS +void LevelRenderer::staticCtor() +{ + s_rebuildCompleteEvents = new C4JThread::EventArray(MAX_CHUNK_REBUILD_THREADS); + char threadName[256]; + for(unsigned int i = 0; i < MAX_CHUNK_REBUILD_THREADS; ++i) + { + sprintf(threadName,"Rebuild Chunk Thread %d\n",i); + rebuildThreads[i] = new C4JThread(rebuildChunkThreadProc,(void *)i,threadName); + + s_activationEventA[i] = new C4JThread::Event(); + + // Threads 1,3 and 5 are generally idle so use them + if((i%3) == 0) rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_A); + else if((i%3) == 1) + { + rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_B); +#ifdef __ORBIS__ + rebuildThreads[i]->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. +#endif + } + else if((i%3) == 2) rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_C); + + //ResumeThread( saveThreads[j] ); + rebuildThreads[i]->Run(); + } +} + +int LevelRenderer::rebuildChunkThreadProc(LPVOID lpParam) +{ + Vec3::CreateNewThreadStorage(); + AABB::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Tesselator::CreateNewThreadStorage(1024*1024); + RenderManager.InitialiseContext(); + Chunk::CreateNewThreadStorage(); + Tile::CreateNewThreadStorage(); + + int index = (size_t)lpParam; + + while(true) + { + s_activationEventA[index]->WaitForSignal(INFINITE); + + //app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); + permaChunk[index + 1].rebuild(); + + // Inform the producer thread that we are done with this chunk + s_rebuildCompleteEvents->Set(index); + } + + return 0; +} +#endif + +// This is called when chunks require rebuilding, but they haven't been added individually to the dirtyChunksLockFreeStack. Once in this +// state, the rebuilding thread will keep assuming there are dirty chunks until it has had a full pass through the chunks and found no dirty ones +void LevelRenderer::nonStackDirtyChunksAdded() +{ + dirtyChunksLockFreeStack.Push((int *)1); +} \ No newline at end of file diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h new file mode 100644 index 0000000..41eb359 --- /dev/null +++ b/Minecraft.Client/LevelRenderer.h @@ -0,0 +1,277 @@ +#pragma once +#include "..\Minecraft.World\LevelListener.h" +#include "..\Minecraft.World\Definitions.h" +#include "OffsettedRenderList.h" +#include "..\Minecraft.World\JavaIntHash.h" +#include "..\Minecraft.World\Level.h" +#include +#ifdef __PS3__ +#include "C4JSpursJob.h" +#endif +class MultiPlayerLevel; +class Textures; +class Chunk; +class Minecraft; +class TileRenderer; +class Culler; +class Entity; +class TileEntity; +class Mob; +class Vec3; +class Particle; +class BlockDestructionProgress; +class IconRegister; +class Tesselator; +using namespace std; + +// AP - this is a system that works out which chunks actually need to be grouped together via the deferral system when doing chunk::rebuild. Doing this will reduce the number +// of chunks built in a single group and reduce the chance of seeing through the landscape when digging near the edges/corners of a chunk. +// I've added another chunk flag to mark a chunk critical so it swipes a bit from the reference count value (goes to 3 bits to 2). This works on Vita because it doesn't have +// split screen reference counting. +#ifdef __PSVITA__ +#define _CRITICAL_CHUNKS +#endif + +class LevelRenderer : public LevelListener +{ + friend class Chunk; +public: + static const int CHUNK_XZSIZE = 16; +#ifdef _LARGE_WORLDS + static const int CHUNK_SIZE = 16; +#else + static const int CHUNK_SIZE = 16; +#endif + static const int CHUNK_Y_COUNT = Level::maxBuildHeight / CHUNK_SIZE; +#if defined _XBOX_ONE + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 512 * 1024 * 1024; // 4J - added +#elif defined __ORBIS__ + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before) +#elif defined __PS3__ + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024; // 4J - added +#else + static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024; // 4J - added +#endif +public: + LevelRenderer(Minecraft *mc, Textures *textures); +private: + void renderStars(); + void createCloudMesh(); // 4J added +public: + void setLevel(int playerIndex, MultiPlayerLevel *level); + void allChanged(); + void allChanged(int playerIndex); + + // 4J-PB added + void AddDLCSkinsToMemTextures(); +public: + void renderEntities(Vec3 *cam, Culler *culler, float a); + wstring gatherStats1(); + wstring gatherStats2(); +private: + void resortChunks(int xc, int yc, int zc); +public: + int render(shared_ptr player, int layer, double alpha, bool updateChunks); +private: + int renderChunks(int from, int to, int layer, double alpha); +public: + int activePlayers(); // 4J - added +public: + void renderSameAsLast(int layer, double alpha); + void tick(); + void renderSky(float alpha); + void renderHaloRing(float alpha); + void renderClouds(float alpha); + bool isInCloud(double x, double y, double z, float alpha); + void renderAdvancedClouds(float alpha); + bool updateDirtyChunks(); + +public: + void renderHit(shared_ptr player, HitResult *h, int mode, shared_ptr inventoryItem, float a); + void renderDestroyAnimation(Tesselator *t, shared_ptr player, float a); + void renderHitOutline(shared_ptr player, HitResult *h, int mode, shared_ptr inventoryItem, float a); + void render(AABB *b); + void setDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level); // 4J - added level param + void tileChanged(int x, int y, int z); + void tileLightChanged(int x, int y, int z); + void setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level); // 4J - added level param + +#ifdef __PS3__ + void cull_SPU(int playerIndex, Culler *culler, float a); + void waitForCull_SPU(); + C4JSpursJobQueue::Port* m_jobPort_CullSPU; + C4JSpursJobQueue::Port* m_jobPort_FindNearestChunk; + bool m_bSPUCullStarted[4]; +#endif // __PS3__ + void cull(Culler *culler, float a); + void playStreamingMusic(const wstring& name, int x, int y, int z); + void playSound(int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist=16.0f); + void playSound(shared_ptr entity,int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist=16.0f); + void addParticle(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za); // 4J added + shared_ptr addParticleInternal(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za); // 4J added + void entityAdded(shared_ptr entity); + void entityRemoved(shared_ptr entity); + void playerRemoved(shared_ptr entity) {} // 4J added - for when a player is removed from the level's player array, not just the entity storage + void skyColorChanged(); + void clear(); + void levelEvent(shared_ptr source, int type, int x, int y, int z, int data); + void destroyTileProgress(int id, int x, int y, int z, int progress); + void registerTextures(IconRegister *iconRegister); + + typedef unordered_map >, IntKeyHash, IntKeyEq> rteMap; +private: + + // debug + int m_freezeticks; // used to freeze the clouds + + // 4J - this block of declarations was scattered round the code but have gathered everything into one place + rteMap renderableTileEntities; // 4J - changed - was vector, now hashed by chunk so we can find them + CRITICAL_SECTION m_csRenderableTileEntities; + MultiPlayerLevel *level[4]; // 4J - now one per player + Textures *textures; + // vector *sortedChunks[4]; // 4J - removed - not sorting our chunks anymore + ClipChunkArray chunks[4]; // 4J - now one per player + int lastPlayerCount[4]; // 4J - added + int xChunks, yChunks, zChunks; + int chunkLists; + Minecraft *mc; + TileRenderer *tileRenderer[4]; // 4J - now one per player + int ticks; + int starList, skyList, darkList, haloRingList; + int cloudList; // 4J added + int xMinChunk, yMinChunk, zMinChunk; + int xMaxChunk, yMaxChunk, zMaxChunk; + int lastViewDistance; + int noEntityRenderFrames; + int totalEntities; + int renderedEntities; + int culledEntities; + int chunkFixOffs; + vector _renderChunks; + int frame; + int repeatList; + double xOld[4]; // 4J - now one per player + double yOld[4]; // 4J - now one per player + double zOld[4]; // 4J - now one per player + + int totalChunks, offscreenChunks, occludedChunks, renderedChunks, emptyChunks; + static const int RENDERLISTS_LENGTH = 4; // 4J - added + OffsettedRenderList renderLists[RENDERLISTS_LENGTH]; + + unordered_map destroyingBlocks; + Icon **breakingTextures; + +public: + void fullyFlagRenderableTileEntitiesToBeRemoved(); // 4J added + + CRITICAL_SECTION m_csDirtyChunks; + bool m_nearDirtyChunk; + + + // 4J - Destroyed Tile Management - these things added so we can track tiles which have been recently destroyed, and + // provide temporary collision for them until the render data has been updated to reflect this change + class DestroyedTileManager + { + private: + class RecentTile + { + public: + int x; + int y; + int z; + Level *level; + AABBList boxes; + int timeout_ticks; + bool rebuilt; + RecentTile(int x, int y, int z, Level *level); + ~RecentTile(); + }; + CRITICAL_SECTION m_csDestroyedTiles; + vector m_destroyedTiles; + public: + void destroyingTileAt( Level *level, int x, int y, int z ); // For game to let this manager know that a tile is about to be destroyed (must be called before it actually is) + void updatedChunkAt( Level * level, int x, int y, int z, int veryNearCount ); // For chunk rebuilding to inform the manager that a chunk (a 16x16x16 tile render chunk) has been updated + void addAABBs( Level *level, AABB *box, AABBList *boxes ); // For game to get any AABBs that the user should be colliding with as render data has not yet been updated + void tick(); + DestroyedTileManager(); + ~DestroyedTileManager(); + }; + DestroyedTileManager *destroyedTileManager; + + float destroyProgress; + + // 4J - added for new render list handling + // This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side + // so that we can render the "infinite" sea at the edges + static const int MAX_LEVEL_RENDER_SIZE[3]; + static const int DIMENSION_OFFSETS[3]; + // This is the TOTAL area of columns of chunks to be allocated for render round the players. So for one player, it would be a region of + // sqrt(PLAYER_RENDER_AREA) x sqrt(PLAYER_RENDER_AREA) +#ifdef _LARGE_WORLDS + static const int PLAYER_VIEW_DISTANCE = 18; // Straight line distance from centre to extent of visible world + static const int PLAYER_RENDER_AREA = (PLAYER_VIEW_DISTANCE * PLAYER_VIEW_DISTANCE * 4); +#else + static const int PLAYER_RENDER_AREA = 400; +#endif + + static int getDimensionIndexFromId(int id); + static int getGlobalIndexForChunk(int x, int y, int z, Level *level); + static int getGlobalIndexForChunk(int x, int y, int z, int dimensionId); + static bool isGlobalIndexInSameDimension( int idx, Level *level); + static int getGlobalChunkCount(); + static int getGlobalChunkCountForOverworld(); + + // Get/set/clear individual flags + bool getGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift = 0); + void setGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift = 0); + void setGlobalChunkFlag(int index, unsigned char flag, unsigned char shift = 0); + void clearGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift = 0); + + // Get/set whole byte of flags + unsigned char getGlobalChunkFlags(int x, int y, int z, Level *level); + void setGlobalChunkFlags(int x, int y, int z, Level *level, unsigned char flags); + + // Reference counting + unsigned char incGlobalChunkRefCount(int x, int y, int z, Level *level); + unsigned char decGlobalChunkRefCount(int x, int y, int z, Level *level); + + // Actual storage for flags + unsigned char *globalChunkFlags; + + // The flag definitions + static const int CHUNK_FLAG_COMPILED = 0x01; + static const int CHUNK_FLAG_DIRTY = 0x02; + static const int CHUNK_FLAG_EMPTY0 = 0x04; + static const int CHUNK_FLAG_EMPTY1 = 0x08; + static const int CHUNK_FLAG_EMPTYBOTH = 0x0c; + static const int CHUNK_FLAG_NOTSKYLIT = 0x10; +#ifdef _CRITICAL_CHUNKS + static const int CHUNK_FLAG_CRITICAL = 0x20; + static const int CHUNK_FLAG_CUT_OUT = 0x40; + static const int CHUNK_FLAG_REF_MASK = 0x01; + static const int CHUNK_FLAG_REF_SHIFT = 7; +#else + static const int CHUNK_FLAG_REF_MASK = 0x07; + static const int CHUNK_FLAG_REF_SHIFT = 5; +#endif + + XLockFreeStack dirtyChunksLockFreeStack; + + bool dirtyChunkPresent; + __int64 lastDirtyChunkFound; + static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 250; + +#ifdef _LARGE_WORLDS + static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4; + static const int MAX_CHUNK_REBUILD_THREADS = MAX_CONCURRENT_CHUNK_REBUILDS - 1; + static Chunk permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; + static C4JThread *rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; + static C4JThread::EventArray *s_rebuildCompleteEvents; + static C4JThread::Event *s_activationEventA[MAX_CHUNK_REBUILD_THREADS]; + static void staticCtor(); + static int rebuildChunkThreadProc(LPVOID lpParam); + + CRITICAL_SECTION m_csChunkFlags; +#endif + void nonStackDirtyChunksAdded(); +}; diff --git a/Minecraft.Client/Lighting.cpp b/Minecraft.Client/Lighting.cpp new file mode 100644 index 0000000..50529d5 --- /dev/null +++ b/Minecraft.Client/Lighting.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "Lighting.h" +#include "..\Minecraft.World\FloatBuffer.h" +#include "..\Minecraft.World\Vec3.h" + +FloatBuffer *Lighting::lb = new FloatBuffer(16); + + +void Lighting::turnOff() +{ + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + glDisable(GL_LIGHT1); + glDisable(GL_COLOR_MATERIAL); +} + +void Lighting::turnOn() +{ + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + float a = 0.4f; + float d = 0.6f; + float s = 0.0f; + + Vec3 *l = Vec3::newTemp(0.2f, 1.0f, -0.7f)->normalize(); + glLight(GL_LIGHT0, GL_POSITION, getBuffer(l->x, l->y, l->z, 0)); + glLight(GL_LIGHT0, GL_DIFFUSE, getBuffer(d, d, d, 1)); + glLight(GL_LIGHT0, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); + glLight(GL_LIGHT0, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); + + l = Vec3::newTemp(-0.2f, 1.0f, 0.7f)->normalize(); + glLight(GL_LIGHT1, GL_POSITION, getBuffer(l->x, l->y, l->z, 0)); + glLight(GL_LIGHT1, GL_DIFFUSE, getBuffer(d, d, d, 1)); + glLight(GL_LIGHT1, GL_AMBIENT, getBuffer(0.0f, 0.0f, 0.0f, 1.0f)); + glLight(GL_LIGHT1, GL_SPECULAR, getBuffer(s, s, s, 1.0f)); + + glShadeModel(GL_FLAT); + glLightModel(GL_LIGHT_MODEL_AMBIENT, getBuffer(a, a, a, 1)); + +} + +FloatBuffer *Lighting::getBuffer(double a, double b, double c, double d) +{ + return getBuffer((float) a, (float) b, (float) c, (float) d); +} + +FloatBuffer *Lighting::getBuffer(float a, float b, float c, float d) +{ + lb->clear(); + lb->put(a)->put(b)->put(c)->put(d); + lb->flip(); + return lb; +} + +void Lighting::turnOnGui() +{ + glPushMatrix(); + glRotatef(-30, 0, 1, 0); + glRotatef(165, 1, 0, 0); + turnOn(); + glPopMatrix(); +} \ No newline at end of file diff --git a/Minecraft.Client/Lighting.h b/Minecraft.Client/Lighting.h new file mode 100644 index 0000000..c06fd53 --- /dev/null +++ b/Minecraft.Client/Lighting.h @@ -0,0 +1,16 @@ +#pragma once +class FloatBuffer; + +class Lighting +{ +private: + static FloatBuffer *lb; + +public: + static void turnOff(); + static void turnOn(); + static void turnOnGui(); +private: + static FloatBuffer *getBuffer(double a, double b, double c, double d); + static FloatBuffer *getBuffer(float a, float b, float c, float d); +}; diff --git a/Minecraft.Client/LightningBoltRenderer.cpp b/Minecraft.Client/LightningBoltRenderer.cpp new file mode 100644 index 0000000..d02ea7f --- /dev/null +++ b/Minecraft.Client/LightningBoltRenderer.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include "LightningBoltRenderer.h" +#include "Tesselator.h" +#include "..\Minecraft.World\net.minecraft.world.entity.global.h" + +void LightningBoltRenderer::render(shared_ptr _bolt, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr bolt = dynamic_pointer_cast(_bolt); + + Tesselator *t = Tesselator::getInstance(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + + double xOffs[8]; + double zOffs[8]; + double xOff = 0; + double zOff = 0; + { + Random *random = new Random(bolt->seed); + for (int h = 7; h >= 0; h--) + { + xOffs[h] = xOff; + zOffs[h] = zOff; + xOff += random->nextInt(11) - 5; + zOff += random->nextInt(11) - 5; + } + } + + for (int r = 0; r < 4; r++) + { + Random *random = new Random(bolt->seed); + for (int p = 0; p < 3; p++) + { + int hs = 7; + int ht = 0; + if (p > 0) hs = 7 - p; + if (p > 0) ht = hs - 2; + double xo0 = xOffs[hs] - xOff; + double zo0 = zOffs[hs] - zOff; + for (int h = hs; h >= ht; h--) + { + double xo1 = xo0; + double zo1 = zo0; + if (p == 0) + { + xo0 += random->nextInt(11) - 5; + zo0 += random->nextInt(11) - 5; + } + else + { + xo0 += random->nextInt(31) - 15; + zo0 += random->nextInt(31) - 15; + } + + t->begin(GL_TRIANGLE_STRIP); + float br = 0.5f; + t->color(0.9f * br, 0.9f * br, 1 * br, 0.3f); + + double rr1 = (0.1 + r * 0.2); + if (p == 0) rr1 *= (h * 0.1 + 1); + + double rr2 = (0.1 + r * 0.2); + if (p == 0) rr2 *= ((h-1) * 0.1 + 1); + + for (int i = 0; i < 5; i++) + { + double xx1 = x + 0.5 - rr1; + double zz1 = z + 0.5 - rr1; + if (i == 1 || i == 2) xx1 += rr1 * 2; + if (i == 2 || i == 3) zz1 += rr1 * 2; + + double xx2 = x + 0.5 - rr2; + double zz2 = z + 0.5 - rr2; + if (i == 1 || i == 2) xx2 += rr2 * 2; + if (i == 2 || i == 3) zz2 += rr2 * 2; + + t->vertex((float)(xx2 + xo0), (float)( y + (h) * 16), (float)( zz2 + zo0)); + t->vertex((float)(xx1 + xo1), (float)( y + (h + 1) * 16), (float)( zz1 + zo1)); + + } + + t->end(); + } + } + } + + + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + +} \ No newline at end of file diff --git a/Minecraft.Client/LightningBoltRenderer.h b/Minecraft.Client/LightningBoltRenderer.h new file mode 100644 index 0000000..5a2b33a --- /dev/null +++ b/Minecraft.Client/LightningBoltRenderer.h @@ -0,0 +1,8 @@ +#pragma once +#include "EntityRenderer.h" + +class LightningBoltRenderer : public EntityRenderer +{ +public: + virtual void render(shared_ptr bolt, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/LocalPlayer.cpp b/Minecraft.Client/LocalPlayer.cpp new file mode 100644 index 0000000..ea66b9e --- /dev/null +++ b/Minecraft.Client/LocalPlayer.cpp @@ -0,0 +1,1616 @@ +#include "stdafx.h" +#include "LocalPlayer.h" +#include "User.h" +#include "Input.h" +#include "StatsCounter.h" +#include "ParticleEngine.h" +#include "TakeAnimationParticle.h" +#include "Options.h" +#include "TextEditScreen.h" +#include "ContainerScreen.h" +#include "CraftingScreen.h" +#include "FurnaceScreen.h" +#include "TrapScreen.h" + +#include "MultiPlayerLocalPlayer.h" +#include "CreativeMode.h" +#include "GameRenderer.h" +#include "ItemInHandRenderer.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.World\net.minecraft.world.damagesource.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.food.h" +#include "..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\ItemEntity.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +#include "..\Minecraft.World\com.mojang.nbt.h" +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.World\Mth.h" +#include "AchievementPopup.h" +#include "CritParticle.h" + +// 4J : WESTY : Added for new achievements. +#include "..\Minecraft.World\item.h" +#include "..\Minecraft.World\mapitem.h" +#include "..\Minecraft.World\tile.h" + +// 4J Stu - Added for tutorial callbacks +#include "Minecraft.h" + +#include "..\Minecraft.World\Minecart.h" +#include "..\Minecraft.World\Boat.h" +#include "..\Minecraft.World\Pig.h" + +#include "..\Minecraft.World\StringHelpers.h" + +#include "Options.h" +#include "..\Minecraft.World\Dimension.h" + +#ifndef _DURANGO +#include "..\Minecraft.World\CommonStats.h" +#endif + + + +LocalPlayer::LocalPlayer(Minecraft *minecraft, Level *level, User *user, int dimension) : Player(level) +{ + flyX = flyY = flyZ = 0.0f; // 4J added + m_awardedThisSession = 0; + + sprintTriggerTime = 0; + sprintTriggerRegisteredReturn = false; + twoJumpsRegistered = false; + sprintTime = 0; + m_uiInactiveTicks=0; + + yBob = xBob = yBobO = xBobO = 0.0f; + + this->minecraft = minecraft; + this->dimension = dimension; + + if (user != NULL && user->name.length() > 0) + { + customTextureUrl = L"http://s3.amazonaws.com/MinecraftSkins/" + user->name + L".png"; + } + if( user != NULL ) + { + this->name = user->name; + m_UUID = name; + //wprintf(L"Created LocalPlayer with name %ls\n", name.c_str() ); + // check to see if this player's xuid is in the list of special players + MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid()); + if(pMojangData) + { + customTextureUrl=pMojangData->wchSkin; + } + + } + input = NULL; + m_iPad = -1; + m_iScreenSection=C4JRender::VIEWPORT_TYPE_FULLSCREEN; // assume singleplayer default + m_bPlayerRespawned=false; + ullButtonsPressed=0LL; + ullDpad_last = ullDpad_this = ullDpad_filtered = 0; + + // 4J-PB - moved in from the minecraft structure + //ticks=0; + missTime=0; + lastClickTick[0] = 0; + lastClickTick[1] = 0; + isRaining=false; + + m_bIsIdle = false; + m_iThirdPersonView=0; + + // 4J Stu - Added for telemetry + SetSessionTimerStart(); + + // 4J - added for auto repeat in creative mode + lastClickState = lastClick_invalid; + lastClickTolerance = 0.0f; + + m_bHasAwardedStayinFrosty = false; +} + +LocalPlayer::~LocalPlayer() +{ + if( this->input != NULL ) + delete input; +} + +// 4J - added noEntityCubes parameter +void LocalPlayer::move(double xa, double ya, double za, bool noEntityCubes) +{ + if (ClientConstants::DEADMAU5_CAMERA_CHEATS) + { + if (shared_from_this() == minecraft->player && minecraft->options->isFlying) + { + noPhysics = true; + float tmp = walkDist; // update + calculateFlight((float) xa, (float) ya, (float) za); + fallDistance = 0.0f; + yd = 0.0f; + Player::move(flyX, flyY, flyZ, noEntityCubes); + onGround = true; + walkDist = tmp; + } + else + { + noPhysics = false; + Player::move(xa, ya, za, noEntityCubes); + } + } + else + { + Player::move(xa, ya, za, noEntityCubes); + } + +} + +void LocalPlayer::calculateFlight(float xa, float ya, float za) +{ + xa = xa * minecraft->options->flySpeed; + ya = 0; + za = za * minecraft->options->flySpeed; + + flyX = smoothFlyX.getNewDeltaValue(xa, .35f * minecraft->options->sensitivity); + flyY = smoothFlyY.getNewDeltaValue(ya, .35f * minecraft->options->sensitivity); + flyZ = smoothFlyZ.getNewDeltaValue(za, .35f * minecraft->options->sensitivity); + +} + +void LocalPlayer::serverAiStep() +{ + Player::serverAiStep(); + + if( abilities.flying && abilities.mayfly ) + { + // snap y rotation for flying to nearest 90 degrees in world space + float fMag = sqrtf(input->xa * input->xa + input->ya * input->ya); + // Don't bother for tiny inputs + if( fMag >= 0.1f ) + { + // Get angle (in player rotated space) of input controls + float yRotInput = atan2f(input->ya, input->xa) * (180.0f / PI); + // Now get in world space + float yRotFinal = yRotInput + yRot; + // Snap this to nearest 90 degrees + float yRotSnapped = floorf((yRotFinal / 45.0f) + 0.5f) * 45.0f; + // Find out how much we had to move to do this snap + float yRotDiff = yRotSnapped - yRotFinal; + // Apply the same difference to the player rotated space angle + float yRotInputAdjust = yRotInput + yRotDiff; + + // Calculate final x/y player-space movement required + this->xxa = cos(yRotInputAdjust * ( PI / 180.0f) ) * fMag; + this->yya = sin(yRotInputAdjust * ( PI / 180.0f) ) * fMag; + } + else + { + this->xxa = input->xa; + this->yya = input->ya; + } + } + else + { + this->xxa = input->xa; + this->yya = input->ya; + } + this->jumping = input->jumping; + + yBobO = yBob; + xBobO = xBob; + xBob += (xRot - xBob) * 0.5; + yBob += (yRot - yBob) * 0.5; + + // TODO 4J - Remove + //if (input->jumping) + // mapPlayerChunk(8); +} + +bool LocalPlayer::isEffectiveAI() +{ + return true; +} + +void LocalPlayer::aiStep() +{ + if (sprintTime > 0) + { + sprintTime--; + if (sprintTime == 0) + { + setSprinting(false); + } + } + if (sprintTriggerTime > 0) sprintTriggerTime--; + if (minecraft->gameMode->isCutScene()) + { + x = z = 0.5; + x = 0; + z = 0; + yRot = tickCount / 12.0f; + xRot = 10; + y = 68.5; + return; + } + oPortalTime = portalTime; + if (isInsidePortal) + { + if (!level->isClientSide) + { + if (riding != NULL) this->ride(nullptr); + } + if (minecraft->screen != NULL) minecraft->setScreen(NULL); + + if (portalTime == 0) + { + minecraft->soundEngine->playUI(eSoundType_PORTAL_TRIGGER, 1, random->nextFloat() * 0.4f + 0.8f); + } + portalTime += 1 / 80.0f; + if (portalTime >= 1) + { + portalTime = 1; + } + isInsidePortal = false; + } + else if (hasEffect(MobEffect::confusion) && getEffect(MobEffect::confusion)->getDuration() > (SharedConstants::TICKS_PER_SECOND * 3)) + { + portalTime += 1 / 150.0f; + if (portalTime > 1) + { + portalTime = 1; + } + } + else + { + if (portalTime > 0) portalTime -= 1 / 20.0f; + if (portalTime < 0) portalTime = 0; + } + + if (changingDimensionDelay > 0) changingDimensionDelay--; + bool wasJumping = input->jumping; + float runTreshold = 0.8f; + + bool wasRunning = input->ya >= runTreshold; + //input->tick( dynamic_pointer_cast( shared_from_this() ) ); + // 4J-PB - make it a localplayer + input->tick( this ); + if (isUsingItem()) + { + input->xa *= 0.2f; + input->ya *= 0.2f; + sprintTriggerTime = 0; + } + // this.heightOffset = input.sneaking?1.30f:1.62f; // 4J - this was already commented out + if (input->sneaking) // 4J - removed - TODO replace + { + if (ySlideOffset < 0.2f) ySlideOffset = 0.2f; + } + + checkInTile(x - bbWidth * 0.35, bb->y0 + 0.5, z + bbWidth * 0.35); + checkInTile(x - bbWidth * 0.35, bb->y0 + 0.5, z - bbWidth * 0.35); + checkInTile(x + bbWidth * 0.35, bb->y0 + 0.5, z - bbWidth * 0.35); + checkInTile(x + bbWidth * 0.35, bb->y0 + 0.5, z + bbWidth * 0.35); + + bool enoughFoodToSprint = getFoodData()->getFoodLevel() > FoodConstants::MAX_FOOD * FoodConstants::FOOD_SATURATION_LOW; + + // 4J Stu - If we can fly, then we should be able to sprint without requiring food. This is particularly a problem for people who save a survival + // world with low food, then reload it in creative. + if(abilities.mayfly || isAllowedToFly() ) enoughFoodToSprint = true; + + // 4J - altered this slightly to make sure that the joypad returns to below returnTreshold in between registering two movements up to runThreshold + if (onGround && !isSprinting() && enoughFoodToSprint && !isUsingItem() && !hasEffect(MobEffect::blindness)) + { + if( !wasRunning && input->ya >= runTreshold ) + { + if (sprintTriggerTime == 0) + { + sprintTriggerTime = 7; + sprintTriggerRegisteredReturn = false; + } + else + { + if( sprintTriggerRegisteredReturn ) + { + setSprinting(true); + sprintTriggerTime = 0; + sprintTriggerRegisteredReturn = false; + } + } + } + else if( ( sprintTriggerTime > 0 ) && ( input->ya == 0.0f ) ) // ya of 0.0f here signifies that we have returned to the deadzone + { + sprintTriggerRegisteredReturn = true; + } + } + if (isSneaking()) sprintTriggerTime = 0; + // 4J-PB - try not stopping sprint on collision + //if (isSprinting() && (input->ya < runTreshold || horizontalCollision || !enoughFoodToSprint)) + if (isSprinting() && (input->ya < runTreshold || !enoughFoodToSprint)) + { + setSprinting(false); + } + + // 4J Stu - Fix for #52705 - Customer Encountered: Player can fly in bed while being in Creative mode. + if (!isSleeping() && (abilities.mayfly || isAllowedToFly() )) + { + // 4J altered to require jump button to released after being tapped twice to trigger move between flying / not flying + if (!wasJumping && input->jumping) + { + if (jumpTriggerTime == 0) + { + jumpTriggerTime = 10; // was 7 + twoJumpsRegistered = false; + } + else + { + twoJumpsRegistered = true; + } + } + else if( ( !input->jumping ) && ( jumpTriggerTime > 0 ) && twoJumpsRegistered ) + { +#ifndef _CONTENT_PACKAGE + printf("flying was %s\n", abilities.flying ? "on" : "off"); +#endif + abilities.flying = !abilities.flying; +#ifndef _CONTENT_PACKAGE + printf("flying is %s\n", abilities.flying ? "on" : "off"); +#endif + jumpTriggerTime = 0; + twoJumpsRegistered = false; + if( abilities.flying ) input->sneaking = false; // 4J added - would we ever intentially want to go into flying mode whilst sneaking? + } + } + else if(abilities.flying) + { +#ifdef _DEBUG_MENUS_ENABLED + if(!abilities.debugflying) +#endif + { + abilities.flying = false; + } + } + + + if (abilities.flying) + { + // yd = 0; + // 4J - note that the 0.42 added for going down is to make it match with what happens when you jump - jumping itself adds 0.42 to yd in Mob::jumpFromGround + if (ullButtonsPressed & (1LL<jumping) + { + noJumpDelay = 0; + yd += 0.15; + } + + // snap y rotation to nearest 90 degree axis aligned value + float yRotSnapped = floorf((yRot / 90.0f) + 0.5f) * 90.0f; + + if(InputManager.GetJoypadMapVal(m_iPad) == 0) + { + if( ullDpad_filtered & (1LL<options->isFlying ) + { + Vec3* viewVector = getViewVector(1.0f); + + // 4J-PB - To let the player build easily while flying, we need to change this + +#ifdef _DEBUG_MENUS_ENABLED + if(abilities.debugflying) + { + flyX = (float)viewVector->x * input->ya; + flyY = (float)viewVector->y * input->ya; + flyZ = (float)viewVector->z * input->ya; + } + else +#endif + { + if( isSprinting() ) + { + // Accelrate up to full speed if we are sprinting, moving in the direction of the view vector + flyX = (float)viewVector->x * input->ya; + flyY = (float)viewVector->y * input->ya; + flyZ = (float)viewVector->z * input->ya; + + float scale = ((float)(SPRINT_DURATION - sprintTime))/10.0f; + scale = scale * scale; + if ( scale > 1.0f ) scale = 1.0f; + flyX *= scale; + flyY *= scale; + flyZ *= scale; + } + else + { + flyX = 0.0f; + flyY = 0.0f; + flyZ = 0.0f; + if( ullDpad_filtered & (1LL< PLAYER_IDLE_TIME ) + { + ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_IDLE,false); + m_bIsIdle = true; + } + else if ( m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) < PLAYER_IDLE_TIME ) + { + // Are we offline or online, and how many players are there + if(g_NetworkManager.GetPlayerCount()>1) + { + // only do it for this player here - each player will run this code + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + } + else + { + if(g_NetworkManager.IsLocalGame()) + { + ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + } + else + { + ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + } + } + updateRichPresence(); + m_bIsIdle = false; + } +} + +void LocalPlayer::changeDimension(int i) +{ + if (!level->isClientSide) + { + if (dimension == 1 && i == 1) + { + awardStat(GenericStats::winGame(), GenericStats::param_noArgs()); + //minecraft.setScreen(new WinScreen()); +#ifndef _CONTENT_PACKAGE + app.DebugPrintf("LocalPlayer::changeDimension from 1 to 1 but WinScreen has not been implemented.\n"); + __debugbreak(); +#endif + } + else + { + awardStat(GenericStats::theEnd(), GenericStats::param_theEnd()); + + minecraft->soundEngine->playUI(eSoundType_PORTAL_TRAVEL, 1, random->nextFloat() * 0.4f + 0.8f); + } + } +} + +float LocalPlayer::getFieldOfViewModifier() +{ + float targetFov = 1.0f; + + // modify for movement + if (abilities.flying) targetFov *= 1.1f; + targetFov *= ((walkingSpeed * getWalkingSpeedModifier()) / defaultWalkSpeed + 1) / 2; + + // modify for bow =) + if (isUsingItem() && getUseItem()->id == Item::bow->id) + { + int ticksHeld = getTicksUsingItem(); + float scale = (float) ticksHeld / BowItem::MAX_DRAW_DURATION; + if (scale > 1) + { + scale = 1; + } + else + { + scale *= scale; + } + targetFov *= 1.0f - scale * .15f; + } + + return targetFov; +} + +void LocalPlayer::addAdditonalSaveData(CompoundTag *entityTag) +{ + Player::addAdditonalSaveData(entityTag); + entityTag->putInt(L"Score", score); +} + +void LocalPlayer::readAdditionalSaveData(CompoundTag *entityTag) +{ + Player::readAdditionalSaveData(entityTag); + score = entityTag->getInt(L"Score"); +} + +void LocalPlayer::closeContainer() +{ + Player::closeContainer(); + minecraft->setScreen(NULL); + + // 4J - Close any xui here + // Fix for #9164 - CRASH: MP: Title crashes upon opening a chest and having another user destroy it. + ui.PlayUISFX(eSFX_Back); + ui.CloseUIScenes( m_iPad ); +} + +void LocalPlayer::openTextEdit(shared_ptr sign) +{ + bool success = app.LoadSignEntryMenu(GetXboxPad(), sign ); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft->setScreen(new TextEditScreen(sign)); +} + +bool LocalPlayer::openContainer(shared_ptr container) +{ + bool success = app.LoadContainerMenu(GetXboxPad(), inventory, container ); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft->setScreen(new ContainerScreen(inventory, container)); + return success; +} + +bool LocalPlayer::startCrafting(int x, int y, int z) +{ + bool success = app.LoadCrafting3x3Menu(GetXboxPad(), dynamic_pointer_cast( shared_from_this() ), x, y, z ); + if( success ) ui.PlayUISFX(eSFX_Press); + //app.LoadXuiCraftMenu(0,inventory, level, x, y, z); + //minecraft->setScreen(new CraftingScreen(inventory, level, x, y, z)); + return success; +} + +bool LocalPlayer::startEnchanting(int x, int y, int z) +{ + bool success = app.LoadEnchantingMenu(GetXboxPad(), inventory, x, y, z, level ); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft.setScreen(new EnchantmentScreen(inventory, level, x, y, z)); + return success; +} + +bool LocalPlayer::startRepairing(int x, int y, int z) +{ + bool success = app.LoadRepairingMenu(GetXboxPad(), inventory, level, x, y, z ); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft.setScreen(new RepairScreen(inventory, level, x, y, z)); + return success; +} + +bool LocalPlayer::openFurnace(shared_ptr furnace) +{ + bool success = app.LoadFurnaceMenu(GetXboxPad(),inventory, furnace); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft->setScreen(new FurnaceScreen(inventory, furnace)); + return success; +} + +bool LocalPlayer::openBrewingStand(shared_ptr brewingStand) +{ + bool success = app.LoadBrewingStandMenu(GetXboxPad(),inventory, brewingStand); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft.setScreen(new BrewingStandScreen(inventory, brewingStand)); + return success; +} + +bool LocalPlayer::openTrap(shared_ptr trap) +{ + bool success = app.LoadTrapMenu(GetXboxPad(),inventory, trap); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft->setScreen(new TrapScreen(inventory, trap)); + return success; +} + +bool LocalPlayer::openTrading(shared_ptr traderTarget) +{ + bool success = app.LoadTradingMenu(GetXboxPad(),inventory, traderTarget, level); + if( success ) ui.PlayUISFX(eSFX_Press); + //minecraft.setScreen(new MerchantScreen(inventory, traderTarget, level)); + return success; +} + +void LocalPlayer::crit(shared_ptr e) +{ + shared_ptr critParticle = shared_ptr( new CritParticle((Level *)minecraft->level, e) ); + critParticle->CritParticlePostConstructor(); + minecraft->particleEngine->add(critParticle); +} + +void LocalPlayer::magicCrit(shared_ptr e) +{ + shared_ptr critParticle = shared_ptr( new CritParticle((Level *)minecraft->level, e, eParticleType_magicCrit) ); + critParticle->CritParticlePostConstructor(); + minecraft->particleEngine->add(critParticle); +} + +void LocalPlayer::take(shared_ptr e, int orgCount) +{ + minecraft->particleEngine->add( shared_ptr( new TakeAnimationParticle((Level *)minecraft->level, e, shared_from_this(), -0.5f) ) ); +} + +void LocalPlayer::chat(const wstring& message) +{ +} + +bool LocalPlayer::isSneaking() +{ + return input->sneaking && !m_isSleeping; +} + +void LocalPlayer::hurtTo(int newHealth, ETelemetryChallenges damageSource) +{ + int dmg = getHealth() - newHealth; + if (dmg <= 0) + { + setHealth(newHealth); + if (dmg < 0) + { + invulnerableTime = invulnerableDuration / 2; + } + } + else + { + lastHurt = dmg; + setHealth(getHealth()); + invulnerableTime = invulnerableDuration; + actuallyHurt(DamageSource::genericSource,dmg); + hurtTime = hurtDuration = 10; + } + + + if( this->health <= 0) + { + int deathTime = (int)(level->getTime() % Level::TICKS_PER_DAY)/1000; + int carriedId = inventory->getSelected() == NULL ? 0 : inventory->getSelected()->id; + TelemetryManager->RecordPlayerDiedOrFailed(GetXboxPad(), 0, y, 0, 0, carriedId, 0, damageSource); + + // if there are any xuiscenes up for this player, close them + if(ui.GetMenuDisplayed(GetXboxPad())) + { + ui.CloseUIScenes(GetXboxPad()); + } + } + +} + +void LocalPlayer::respawn() +{ + // Select the right payer to respawn + minecraft->respawnPlayer(GetXboxPad(), 0, 0); +} + +void LocalPlayer::animateRespawn() +{ +// Player.animateRespawn(this, level); +} + +void LocalPlayer::displayClientMessage(int messageId) +{ + minecraft->gui->displayClientMessage(messageId, GetXboxPad()); +} + +void LocalPlayer::awardStat(Stat *stat, byteArray param) +{ +#ifdef _DURANGO + // 4J-JEV: Maybe we want to fine tune this later? #TODO + if ( !ProfileManager.IsGuest(GetXboxPad()) + && app.CanRecordStatsAndAchievements() + && ProfileManager.IsFullVersion() + ) + { + stat->handleParamBlob(dynamic_pointer_cast(shared_from_this()), param); + } + delete [] param.data; +#else + int count = CommonStats::readParam(param); + delete [] param.data; + + if (!app.CanRecordStatsAndAchievements()) return; + if (stat == NULL) return; + + if (stat->isAchievement()) + { + Achievement *ach = (Achievement *) stat; + // 4J-PB - changed to attempt to award everytime - the award may need a storage device, so needs a primary player, and the player may not have been a primary player when they first 'got' the award + // so let the award manager figure it out + //if (!minecraft->stats[m_iPad]->hasTaken(ach)) + { + // 4J-PB - Don't display the java popup + //minecraft->achievementPopup->popup(ach); + + // 4J Stu - Added this function in the libraries as some achievements don't get awarded to all players + // e.g. Splitscreen players cannot get theme/avatar/gamerpic and Trial players cannot get any + // This causes some extreme flooding of some awards + if(ProfileManager.CanBeAwarded(m_iPad, ach->getAchievementID() ) ) + { + // 4J Stu - We don't (currently) care about the gamerscore, so setting to a default of 0 points + TelemetryManager->RecordAchievementUnlocked(m_iPad,ach->getAchievementID(),0); + + // 4J Stu - Some awards cause a menu to popup. This can be bad, especially if you are surrounded by mobs! + // We cannot pause the game unless in offline single player, but lets at least do it then + if( g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && ProfileManager.GetAwardType(ach->getAchievementID() ) != eAwardType_Achievement ) + { + ui.CloseUIScenes(m_iPad); + ui.NavigateToScene(m_iPad,eUIScene_PauseMenu); + } + } + + // 4J-JEV: To stop spamming trophies. + unsigned long long achBit = ((unsigned long long)1) << ach->getAchievementID(); + if ( !(achBit & m_awardedThisSession) ) + { + ProfileManager.Award(m_iPad, ach->getAchievementID()); + if (ProfileManager.IsFullVersion()) + m_awardedThisSession |= achBit; + } + } + minecraft->stats[m_iPad]->award(stat, level->difficulty, count); + } + else + { + // 4J : WESTY : Added for new achievements. + StatsCounter* pStats = minecraft->stats[m_iPad]; + pStats->award(stat, level->difficulty, count); + + // 4J-JEV: Check achievements for unlocks. + + // LEADER OF THE PACK + if ( stat == GenericStats::tamedEntity(eTYPE_WOLF) ) + { + // Check to see if we have befriended 5 wolves! Is this really the best place to do this??!! + if ( pStats->getTotalValue(GenericStats::tamedEntity(eTYPE_WOLF)) >= 5 ) + { + awardStat(GenericStats::leaderOfThePack(), GenericStats::param_noArgs()); + } + } + + // MOAR TOOLS + { + Stat *toolStats[4][5]; + toolStats[0][0] = GenericStats::itemsCrafted(Item::shovel_wood->id); + toolStats[0][1] = GenericStats::itemsCrafted(Item::shovel_stone->id); + toolStats[0][2] = GenericStats::itemsCrafted(Item::shovel_iron->id); + toolStats[0][3] = GenericStats::itemsCrafted(Item::shovel_diamond->id); + toolStats[0][4] = GenericStats::itemsCrafted(Item::shovel_gold->id); + toolStats[1][0] = GenericStats::itemsCrafted(Item::pickAxe_wood->id); + toolStats[1][1] = GenericStats::itemsCrafted(Item::pickAxe_stone->id); + toolStats[1][2] = GenericStats::itemsCrafted(Item::pickAxe_iron->id); + toolStats[1][3] = GenericStats::itemsCrafted(Item::pickAxe_diamond->id); + toolStats[1][4] = GenericStats::itemsCrafted(Item::pickAxe_gold->id); + toolStats[2][0] = GenericStats::itemsCrafted(Item::hatchet_wood->id); + toolStats[2][1] = GenericStats::itemsCrafted(Item::hatchet_stone->id); + toolStats[2][2] = GenericStats::itemsCrafted(Item::hatchet_iron->id); + toolStats[2][3] = GenericStats::itemsCrafted(Item::hatchet_diamond->id); + toolStats[2][4] = GenericStats::itemsCrafted(Item::hatchet_gold->id); + toolStats[3][0] = GenericStats::itemsCrafted(Item::hoe_wood->id); + toolStats[3][1] = GenericStats::itemsCrafted(Item::hoe_stone->id); + toolStats[3][2] = GenericStats::itemsCrafted(Item::hoe_iron->id); + toolStats[3][3] = GenericStats::itemsCrafted(Item::hoe_diamond->id); + toolStats[3][4] = GenericStats::itemsCrafted(Item::hoe_gold->id); + + bool justCraftedTool = false; + for (int i=0; i<4; i++) + { + for (int j=0; j<5; j++) + { + if ( stat == toolStats[i][j] ) + { + justCraftedTool = true; + break; + } + } + } + + if (justCraftedTool) + { + bool awardNow = true; + for (int i=0; i<4; i++) + { + bool craftedThisTool = false; + for (int j=0; j<5; j++) + { + if ( pStats->getTotalValue(toolStats[i][j]) > 0 ) + craftedThisTool = true; + } + + if (!craftedThisTool) + { + awardNow = false; + break; + } + } + + if (awardNow) + { + awardStat(GenericStats::MOARTools(), GenericStats::param_noArgs()); + } + } + + } + +#ifdef _XBOX + // AWARD: Have we killed 10 creepers? + if ( pStats->getTotalValue( GenericStats::killsCreeper() ) >= 10 ) + { + awardStat( GenericStats::kill10Creepers(), GenericStats::param_noArgs()); + } + + // AWARD : Have we been playing for 100 game days? + if ( pStats->getTotalValue( GenericStats::timePlayed() ) >= ( Level::TICKS_PER_DAY * 100 ) ) + { + awardStat( GenericStats::play100Days(), GenericStats::param_noArgs()); + } + // AWARD : Have we mined 100 blocks? + if ( pStats->getTotalValue( GenericStats::totalBlocksMined() ) >= 100 ) + { + awardStat( GenericStats::mine100Blocks(), GenericStats::param_noArgs()); + } +#endif + +#ifdef _EXTENDED_ACHIEVEMENTS + + // AWARD : Porkchop, cook and eat a porkchop. + { + Stat *cookPorkchop, *eatPorkchop; + cookPorkchop = GenericStats::itemsCrafted(Item::porkChop_cooked_Id); + eatPorkchop = GenericStats::itemsUsed(Item::porkChop_cooked_Id); + + if ( stat == cookPorkchop || stat == eatPorkchop ) + { + int numCookPorkchop, numEatPorkchop; + numCookPorkchop = pStats->getTotalValue(cookPorkchop); + numEatPorkchop = pStats->getTotalValue(eatPorkchop); + + app.DebugPrintf( + "[AwardStat] Check unlock 'Porkchop': " + "pork_cooked=%i, pork_eaten=%i.\n", + numCookPorkchop, numEatPorkchop + ); + + if ( (0 < numCookPorkchop) && (0 < numEatPorkchop) ) + { + awardStat( GenericStats::porkChop(), GenericStats::param_porkChop() ); + } + } + } + + // AWARD : Passing the Time, play for 100 minecraft days. + { + Stat *timePlayed = GenericStats::timePlayed(); + + if ( stat == timePlayed ) + { + int iPlayedTicks, iRequiredTicks; + iPlayedTicks = pStats->getTotalValue(timePlayed); + iRequiredTicks = Level::TICKS_PER_DAY * 100; + + /* app.DebugPrintf( + "[AwardStat] Check unlock 'Passing the Time': " + "total_ticks=%i, req=%i.\n", + iPlayedTicks, iRequiredTicks + ); */ + + if (iPlayedTicks >= iRequiredTicks) + { + awardStat( GenericStats::passingTheTime(), GenericStats::param_passingTheTime() ); + } + } + } + + // AWARD : The Haggler, Acquire 30 emeralds. + { + Stat *emeraldMined, *emeraldBought; + emeraldMined = GenericStats::blocksMined(Tile::emeraldOre_Id); + emeraldBought = GenericStats::itemsBought(Item::emerald_Id); + + if ( stat == emeraldMined || stat == emeraldBought ) + { + int numEmeraldMined, numEmeraldBought, totalSum; + numEmeraldMined = pStats->getTotalValue(emeraldMined); + numEmeraldBought = pStats->getTotalValue(emeraldBought); + totalSum = numEmeraldMined + numEmeraldBought; + + app.DebugPrintf( + "[AwardStat] Check unlock 'The Haggler': " + "emerald_mined=%i, emerald_bought=%i, sum=%i.\n", + numEmeraldMined, numEmeraldBought, totalSum + ); + + if (totalSum >= 30) awardStat( GenericStats::theHaggler(), GenericStats::param_theHaggler() ); + } + } + + // AWARD : Pot Planter, craft and place a flowerpot. + { + Stat *craftFlowerpot, *placeFlowerpot; + craftFlowerpot = GenericStats::itemsCrafted(Item::flowerPot_Id); + placeFlowerpot = GenericStats::blocksPlaced(Tile::flowerPot_Id); + + if ( stat == craftFlowerpot || stat == placeFlowerpot ) + { + if ( (pStats->getTotalValue(craftFlowerpot) > 0) && (pStats->getTotalValue(placeFlowerpot) > 0) ) + { + awardStat( GenericStats::potPlanter(), GenericStats::param_potPlanter() ); + } + } + } + + // AWARD : It's a Sign, craft and place a sign. + { + Stat *craftSign, *placeWallsign, *placeSignpost; + craftSign = GenericStats::itemsCrafted(Item::sign_Id); + placeWallsign = GenericStats::blocksPlaced(Tile::wallSign_Id); + placeSignpost = GenericStats::blocksPlaced(Tile::sign_Id); + + if ( stat == craftSign || stat == placeWallsign || stat == placeSignpost ) + { + int numCraftedSigns, numPlacedWallSign, numPlacedSignpost; + numCraftedSigns = pStats->getTotalValue(craftSign); + numPlacedWallSign = pStats->getTotalValue(placeWallsign); + numPlacedSignpost = pStats->getTotalValue(placeSignpost); + + app.DebugPrintf( + "[AwardStat] Check unlock 'It's a Sign': " + "crafted=%i, placedWallSigns=%i, placedSignposts=%i.\n", + numCraftedSigns, numPlacedWallSign, numPlacedSignpost + ); + + if ( (numCraftedSigns>0) && ((numPlacedWallSign+numPlacedSignpost)>0) ) + { + awardStat( GenericStats::itsASign(), GenericStats::param_itsASign()); + } + } + } + + // AWARD : Rainbow Collection, collect all different colours of wool. + { + bool justPickedupWool = false; + + for (int i=0; i<16; i++) + if ( stat == GenericStats::itemsCollected(Tile::cloth_Id, i) ) + justPickedupWool = true; + + if (justPickedupWool) + { + unsigned int woolCount = 0; + + for (unsigned int i = 0; i < 16; i++) + { + if (pStats->getTotalValue(GenericStats::itemsCollected(Tile::cloth_Id, i)) > 0) + woolCount++; + } + + if (woolCount >= 16) awardStat( GenericStats::rainbowCollection(), GenericStats::param_rainbowCollection() ); + } + } + + // AWARD : Adventuring Time, visit at least 17 biomes + { + bool justEnteredBiome = false; + + for (int i=0; i<23; i++) + if ( stat == GenericStats::enteredBiome(i) ) + justEnteredBiome = true; + + if (justEnteredBiome) + { + unsigned int biomeCount = 0; + + for (unsigned int i = 0; i < 23; i++) + { + if (pStats->getTotalValue(GenericStats::enteredBiome(i)) > 0) + biomeCount++; + } + + if (biomeCount >= 17) awardStat( GenericStats::adventuringTime(), GenericStats::param_adventuringTime() ); + } + } +#endif + } +#endif +} + +bool LocalPlayer::isSolidBlock(int x, int y, int z) +{ + return level->isSolidBlockingTile(x, y, z); +} + +bool LocalPlayer::checkInTile(double x, double y, double z) +{ + int xTile = Mth::floor(x); + int yTile = Mth::floor(y); + int zTile = Mth::floor(z); + + double xd = x - xTile; + double zd = z - zTile; + + if (isSolidBlock(xTile, yTile, zTile) || isSolidBlock(xTile, yTile + 1, zTile)) + { + bool west = !isSolidBlock(xTile - 1, yTile, zTile) && !isSolidBlock(xTile - 1, yTile + 1, zTile); + bool east = !isSolidBlock(xTile + 1, yTile, zTile) && !isSolidBlock(xTile + 1, yTile + 1, zTile); + bool north = !isSolidBlock(xTile, yTile, zTile - 1) && !isSolidBlock(xTile, yTile + 1, zTile - 1); + bool south = !isSolidBlock(xTile, yTile, zTile + 1) && !isSolidBlock(xTile, yTile + 1, zTile + 1); + + int dir = -1; + double closest = 9999; + if (west && xd < closest) + { + closest = xd; + dir = 0; + } + if (east && 1 - xd < closest) + { + closest = 1 - xd; + dir = 1; + } + if (north && zd < closest) + { + closest = zd; + dir = 4; + } + if (south && 1 - zd < closest) + { + closest = 1 - zd; + dir = 5; + } + + float speed = 0.1f; + if (dir == 0) this->xd = -speed; + if (dir == 1) this->xd = +speed; + if (dir == 4) this->zd = -speed; + if (dir == 5) this->zd = +speed; + } + + return false; + +} + +void LocalPlayer::setSprinting(bool value) +{ + Player::setSprinting(value); + if (value == false) sprintTime = 0; + else sprintTime = SPRINT_DURATION; +} + +void LocalPlayer::setExperienceValues(float experienceProgress, int totalExp, int experienceLevel) +{ + this->experienceProgress = experienceProgress; + this->totalExperience = totalExp; + this->experienceLevel = experienceLevel; +} + +bool LocalPlayer::hasPermission(EGameCommand command) +{ + return level->getLevelData()->getAllowCommands(); +} + +void LocalPlayer::onCrafted(shared_ptr item) +{ + if( minecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)minecraft->localgameModes[m_iPad]; + gameMode->getTutorial()->onCrafted(item); + } +} + +void LocalPlayer::setAndBroadcastCustomSkin(DWORD skinId) +{ + setCustomSkin(skinId); +} + +void LocalPlayer::setAndBroadcastCustomCape(DWORD capeId) +{ + setCustomCape(capeId); +} + +// 4J TODO - Remove +#include "..\Minecraft.World\LevelChunk.h" +void LocalPlayer::mapPlayerChunk(const unsigned int flagTileType) +{ + int cx = this->xChunk; + int cz = this->zChunk; + + int pZ = ((int) floor(this->z)) %16; + int pX = ((int) floor(this->x)) %16; + + cout<<"player in chunk ("<x<<","<y<<","<z<<")\n"; + + for (int v = -1; v < 2; v++) + for (unsigned int z = 0; z < 16; z++) + { + for (int u = -1; u < 2; u++) + for (unsigned int x = 0; x < 16; x++) + { + LevelChunk *cc = level->getChunk(cx+u, cz+v); + if ( x==pX && z==pZ && u==0 && v==0) + cout << "O"; + else for (unsigned int y = 127; y > 0; y--) + { + int t = cc->getTile(x,y,z); + if (flagTileType != 0 && t == flagTileType) { cout << "@"; break; } + else if (t != 0 && t < 10) { cout << t; break; } + else if (t > 0) { cout << "#"; break; } + } + } + cout << "\n"; + } + + cout << "\n"; +} + + +void LocalPlayer::handleMouseDown(int button, bool down) +{ + // 4J Stu - We should not accept any input while asleep, except the above to wake up + if(isSleeping() && level != NULL && level->isClientSide) + { + return; + } + if (!down) missTime = 0; + if (button == 0 && missTime > 0) return; + + if (down && minecraft->hitResult != NULL && minecraft->hitResult->type == HitResult::TILE && button == 0) + { + int x = minecraft->hitResult->x; + int y = minecraft->hitResult->y; + int z = minecraft->hitResult->z; + + // 4J - addition to stop layer mining out of the top or bottom of the world + // 4J Stu - Allow this for The End + if( ( ( y == 0 ) || ( ( y == 127 ) && level->dimension->hasCeiling ) ) && level->dimension->id != 1 ) return; + + minecraft->gameMode->continueDestroyBlock(x, y, z, minecraft->hitResult->f); + + if(mayBuild(x,y,z)) + { + minecraft->particleEngine->crack(x, y, z, minecraft->hitResult->f); + swing(); + } + } + else + { + minecraft->gameMode->stopDestroyBlock(); + } +} + +bool LocalPlayer::creativeModeHandleMouseClick(int button, bool buttonPressed) +{ + if( buttonPressed ) + { + if( lastClickState == lastClick_oldRepeat ) + { + return false; + } + + // Are we in an auto-repeat situation? - If so only tell the game that we've clicked if we move more than a unit away from our last + // click position in any axis + if( lastClickState != lastClick_invalid ) + { + // If we're in disabled mode already (set when sprinting) then don't do anything - if we're sprinting, we don't auto-repeat at all. + // With auto repeat on, we can quickly place fires causing photosensitivity issues due to rapid flashing + if( lastClickState == lastClick_disabled ) return false; + // If we've started sprinting, go into this mode & also don't do anything + // Ignore repeate when sleeping + if( isSprinting() ) + { + lastClickState = lastClick_disabled; + return false; + } + + // Get distance from last click point in each axis + float dX = (float)x - lastClickX; + float dY = (float)y - lastClickY; + float dZ = (float)z - lastClickZ; + bool newClick = false; + + float ddx = dX - lastClickdX; + float ddy = dY - lastClickdY; + float ddz = dZ - lastClickdZ; + + if( lastClickState == lastClick_moving ) + { + float deltaChange = sqrtf(ddx * ddx + ddy * ddy + ddz * ddz ); + if( deltaChange < 0.01f ) + { + lastClickState = lastClick_stopped; + lastClickTolerance = 0.0f; + } + } + else if( lastClickState == lastClick_stopped ) + { + float deltaChange = sqrtf(ddx * ddx + ddy * ddy + ddz * ddz ); + if( deltaChange >= 0.01f ) + { + lastClickState = lastClick_moving; + lastClickTolerance = 0.0f; + } + else + { + lastClickTolerance += 0.1f; + if( lastClickTolerance > 0.7f ) + { + lastClickTolerance = 0.0f; + lastClickState = lastClick_init; + } + } + } + + lastClickdX = dX; + lastClickdY = dY; + lastClickdZ = dZ; + + // If we have moved more than one unit in any one axis, then register a new click + // The new click position is normalised at one unit in the direction of movement, so that we don't gradually drift away if we detect the movement a fraction over + // the unit distance each time + + if( fabsf(dX) >= 1.0f ) + { + dX= ( dX < 0.0f ) ? ceilf(dX) : floorf(dX); + newClick = true; + } + else if( fabsf(dY) >= 1.0f ) + { + dY= ( dY < 0.0f ) ? ceilf(dY) : floorf(dY); + newClick = true; + } + else if( fabsf(dZ) >= 1.0f ) + { + dZ= ( dZ < 0.0f ) ? ceilf(dZ) : floorf(dZ); + newClick = true; + } + + if( ( !newClick ) && ( lastClickTolerance > 0.0f ) ) + { + float fTarget = 1.0f - lastClickTolerance; + + if( fabsf(dX) >= fTarget ) newClick = true; + if( fabsf(dY) >= fTarget ) newClick = true; + if( fabsf(dZ) >= fTarget ) newClick = true; + } + + if( newClick ) + { + lastClickX += dX; + lastClickY += dY; + lastClickZ += dZ; + + // Get a more accurate pick from the position where the new click should ideally have come from, rather than + // where we happen to be now (ie a rounded number of units from the last Click position) + double oldX = x; + double oldY = y; + double oldZ = z; + x = lastClickX; + y = lastClickY; + z = lastClickZ; + + minecraft->gameRenderer->pick(1); + + x = oldX; + y = oldY; + z = oldZ; + + handleMouseClick(button); + + if( lastClickState == lastClick_stopped ) + { + lastClickState = lastClick_init; + lastClickTolerance = 0.0f; + } + else + { + lastClickState = lastClick_moving; + lastClickTolerance = 0.0f; + } + } + } + else + { + // First click - just record position & handle + lastClickX = (float)x; + lastClickY = (float)y; + lastClickZ = (float)z; + // If we actually placed an item, then move into the init state as we are going to be doing the special creative mode auto repeat + bool itemPlaced = handleMouseClick(button); + // If we're sprinting or riding, don't auto-repeat at all. With auto repeat on, we can quickly place fires causing photosensitivity issues due to rapid flashing + // Also ignore repeats when the player is sleeping + if( isSprinting() || isRiding() || isSleeping() ) + { + lastClickState = lastClick_disabled; + } + else + { + if( itemPlaced ) + { + lastClickState = lastClick_init; + lastClickTolerance = 0.0f; + } + else + { + // Didn't place an item - might actually be activating a switch or door or something - just do a standard auto repeat in this case + lastClickState = lastClick_oldRepeat; + } + } + return true; + } + } + else + { + lastClickState = lastClick_invalid; + } + return false; + +} + +bool LocalPlayer::handleMouseClick(int button) +{ + bool returnItemPlaced = false; + + if (button == 0 && missTime > 0) return false; + if (button == 0) + { + //app.DebugPrintf("handleMouseClick - Player %d is swinging\n",GetXboxPad()); + swing(); + } + + bool mayUse = true; + + // 4J-PB - Adding a special case in here for sleeping in a bed in a multiplayer game - we need to wake up, and we don't have the inbedchatscreen with a button + + if(button==1 && (isSleeping() && level != NULL && level->isClientSide)) + { + if(lastClickState == lastClick_oldRepeat) return false; + + + shared_ptr mplp = dynamic_pointer_cast( shared_from_this() ); + + if(mplp && mplp->connection) mplp->StopSleeping(); + + } + // 4J Stu - We should not accept any input while asleep, except the above to wake up + if(isSleeping() && level != NULL && level->isClientSide) + { + return false; + } + + shared_ptr oldItem = inventory->getSelected(); + + if (minecraft->hitResult == NULL) + { + if (button == 0 && minecraft->localgameModes[GetXboxPad()]->hasMissTime()) missTime = 10; + } + else if (minecraft->hitResult->type == HitResult::ENTITY) + { + if (button == 0) + { + minecraft->gameMode->attack(minecraft->localplayers[GetXboxPad()], minecraft->hitResult->entity); + } + if (button == 1) + { + // 4J-PB - if we milk a cow here, and end up with a bucket of milk, the if (mayUse && button == 1) further down will + // then empty our bucket if we're pointing at a tile + // It looks like interact really should be returning a result so we can check this, but it's possibly just the + // milk bucket that causes a problem + + if(minecraft->hitResult->entity->GetType()==eTYPE_COW) + { + // If I have an empty bucket in my hand, it's going to be filled with milk, so turn off mayUse + shared_ptr item = inventory->getSelected(); + if(item && (item->id==Item::bucket_empty_Id)) + { + mayUse=false; + } + } + if( minecraft->gameMode->interact(minecraft->localplayers[GetXboxPad()], minecraft->hitResult->entity) ) + { + mayUse = false; + } + } + } + else if (minecraft->hitResult->type == HitResult::TILE) + { + int x = minecraft->hitResult->x; + int y = minecraft->hitResult->y; + int z = minecraft->hitResult->z; + int face = minecraft->hitResult->f; + + if (button == 0) + { + // 4J - addition to stop layer mining out of the top or bottom of the world + // 4J Stu - Allow this for The End + if( !( ( y == 0 ) || ( ( y == 127 ) && level->dimension->hasCeiling ) ) || level->dimension->id == 1 ) + { + minecraft->gameMode->startDestroyBlock(x, y, z, minecraft->hitResult->f); + } + } + else + { + shared_ptr item = oldItem; + int oldCount = item != NULL ? item->count : 0; + bool usedItem = false; + if (minecraft->gameMode->useItemOn(minecraft->localplayers[GetXboxPad()], level, item, x, y, z, face, minecraft->hitResult->pos, false, &usedItem)) + { + // Presume that if we actually used the held item, then we've placed it + if( usedItem ) + { + returnItemPlaced = true; + } + mayUse = false; + //app.DebugPrintf("Player %d is swinging\n",GetXboxPad()); + swing(); + } + if (item == NULL) + { + return false; + } + + if (item->count == 0) + { + inventory->items[inventory->selected] = nullptr; + } + else if (item->count != oldCount || minecraft->localgameModes[GetXboxPad()]->hasInfiniteItems()) + { + minecraft->gameRenderer->itemInHandRenderer->itemPlaced(); + } + } + } + + if (mayUse && button == 1) + { + shared_ptr item = inventory->getSelected(); + if (item != NULL) + { + if (minecraft->gameMode->useItem(minecraft->localplayers[GetXboxPad()], level, item)) + { + minecraft->gameRenderer->itemInHandRenderer->itemUsed(); + } + } + } + return returnItemPlaced; +} + +void LocalPlayer::updateRichPresence() +{ + if((m_iPad!=-1)/* && !ui.GetMenuDisplayed(m_iPad)*/ ) + { + shared_ptr selectedItem = inventory->getSelected(); + if(selectedItem != NULL && selectedItem->id == Item::fishingRod_Id) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_FISHING); + } + else if(selectedItem != NULL && selectedItem->id == Item::map_Id) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_MAP); + } + else if(riding != NULL && dynamic_pointer_cast(riding) != NULL) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_MINECART); + } + else if(riding != NULL && dynamic_pointer_cast(riding) != NULL) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BOATING); + } + else if(riding != NULL && dynamic_pointer_cast(riding) != NULL) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_PIG); + } + else if( this->dimension == -1 ) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_NETHER); + } + else if( minecraft->soundEngine->GetIsPlayingStreamingCDMusic() ) + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_CD); + } + else + { + app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BLANK); + } + } +} + +// 4J Stu - Added for telemetry +void LocalPlayer::SetSessionTimerStart(void) +{ + m_sessionTimeStart=app.getAppTime(); + m_dimensionTimeStart=m_sessionTimeStart; +} + +float LocalPlayer::getSessionTimer(void) +{ + return app.getAppTime()-m_sessionTimeStart; +} + +float LocalPlayer::getAndResetChangeDimensionTimer() +{ + float appTime = app.getAppTime(); + float returnVal = appTime - m_dimensionTimeStart; + m_dimensionTimeStart = appTime; + return returnVal; +} + +void LocalPlayer::handleCollectItem(shared_ptr item) +{ + if(item != NULL) + { + unsigned int itemCountAnyAux = 0; + unsigned int itemCountThisAux = 0; + for (unsigned int k = 0; k < inventory->items.length; ++k) + { + if (inventory->items[k] != NULL) + { + // do they have the item + if(inventory->items[k]->id == item->id) + { + unsigned int quantity = inventory->items[k]->GetCount(); + + itemCountAnyAux += quantity; + + if( inventory->items[k]->getAuxValue() == item->getAuxValue() ) + { + itemCountThisAux += quantity; + } + } + } + } + TutorialMode *gameMode = (TutorialMode *)minecraft->localgameModes[m_iPad]; + gameMode->getTutorial()->onTake(item, itemCountAnyAux, itemCountThisAux); + } + + if(ui.IsContainerMenuDisplayed(m_iPad)) + { + ui.HandleInventoryUpdated(m_iPad); + } +} + +void LocalPlayer::SetPlayerAdditionalModelParts(vectorpAdditionalModelParts) +{ + m_pAdditionalModelParts=pAdditionalModelParts; +} + diff --git a/Minecraft.Client/LocalPlayer.h b/Minecraft.Client/LocalPlayer.h new file mode 100644 index 0000000..b1e8dd2 --- /dev/null +++ b/Minecraft.Client/LocalPlayer.h @@ -0,0 +1,199 @@ +#pragma once +#include "..\Minecraft.World\SmoothFloat.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\Pos.h" +class Level; +class User; +class CompoundTag; +class FurnaceTileEntity; +class DispenserTileEntity; +class SignTileEntity; +class Container; +class Input; +class Stat; +class Minecraft; + +using namespace std; + +// Time in seconds before the players presence is update to Idle +#define PLAYER_IDLE_TIME 300 + +class LocalPlayer : public Player +{ +public: + static const int SPRINT_DURATION = 20 * 30; + + Input *input; +protected: + Minecraft *minecraft; + int sprintTriggerTime; + bool sprintTriggerRegisteredReturn; // 4J added + bool twoJumpsRegistered; // 4J added + + unsigned int m_uiInactiveTicks; // To measure time for idle anims + + unsigned long long m_awardedThisSession; + + // 4J - Last time we checked for achievement uunlocks. + //long long m_lastAchievementUpdate; + +public: + int sprintTime; + + float yBob, xBob; + float yBobO, xBobO; + + LocalPlayer(Minecraft *minecraft, Level *level, User *user, int dimension); + virtual ~LocalPlayer(); + + void move(double xa, double ya, double za, bool noEntityCubes=false); // 4J - added noEntityCubes parameter + + + int m_iScreenSection; // assuming 4player splitscreen for now, or -1 for single player + __uint64 ullButtonsPressed; // Stores the button presses, since the inputmanager can be ticked faster than the minecraft + // player tick, and a button press and release combo can be missed in the minecraft::tick + + __uint64 ullDpad_last; + __uint64 ullDpad_this; + __uint64 ullDpad_filtered; + + // 4J-PB - moved these in from the minecraft structure, since they are per player things for splitscreen + //int ticks; + int missTime; + int lastClickTick[2]; + bool isRaining ; + int m_iThirdPersonView; + + bool m_bHasAwardedStayinFrosty; + +private: + float flyX, flyY, flyZ; + +protected: + // 4J-PB - player's xbox pad + int m_iPad; + + bool m_bIsIdle; + +private: + // local player fly + // -------------------------------------------------------------------------- + // smooth camera settings + + SmoothFloat smoothFlyX; + SmoothFloat smoothFlyY; + SmoothFloat smoothFlyZ; + + void calculateFlight(float xa, float ya, float za); + +public: + virtual void serverAiStep(); + +protected: + bool isEffectiveAI(); + +public: + virtual void aiStep(); + virtual void changeDimension(int i); + virtual float getFieldOfViewModifier(); + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *entityTag); + virtual void closeContainer(); + virtual void openTextEdit(shared_ptr sign); + virtual bool openContainer(shared_ptr container); // 4J added bool return + virtual bool startCrafting(int x, int y, int z); // 4J added bool return + virtual bool startEnchanting(int x, int y, int z); // 4J added bool return + virtual bool startRepairing(int x, int y, int z); + virtual bool openFurnace(shared_ptr furnace); // 4J added bool return + virtual bool openBrewingStand(shared_ptr brewingStand); // 4J added bool return + virtual bool openTrap(shared_ptr trap); // 4J added bool return + virtual bool openTrading(shared_ptr traderTarget); + virtual void crit(shared_ptr e); + virtual void magicCrit(shared_ptr e); + virtual void take(shared_ptr e, int orgCount); + virtual void chat(const wstring& message); + virtual bool isSneaking(); + //virtual bool isIdle(); + virtual void hurtTo(int newHealth, ETelemetryChallenges damageSource); + virtual void respawn(); + virtual void animateRespawn(); + virtual void displayClientMessage(int messageId); + virtual void awardStat(Stat *stat, byteArray param); + virtual int ThirdPersonView() { return m_iThirdPersonView;} + // 4J - have changed 3rd person view to be 0 if not enabled, 1 for mode like original, 2 reversed mode + virtual void SetThirdPersonView(int val) {m_iThirdPersonView=val;} + + void ResetInactiveTicks() { m_uiInactiveTicks=0;} + unsigned int GetInactiveTicks() { return m_uiInactiveTicks;} + void IncrementInactiveTicks() { if(m_uiInactiveTicks<255) m_uiInactiveTicks++;} + + void mapPlayerChunk(unsigned int); + // 4J-PB - xbox pad for this player + void SetXboxPad(int iPad) {m_iPad=iPad;} + int GetXboxPad() {return m_iPad;} + void SetPlayerRespawned(bool bVal) {m_bPlayerRespawned=bVal;} + bool GetPlayerRespawned() {return m_bPlayerRespawned;} + + // 4J-PB - Moved these in here from the minecraft structure since they are local player related + void handleMouseDown(int button, bool down); + bool handleMouseClick(int button); + + // 4J - added for improved autorepeat + bool creativeModeHandleMouseClick(int button, bool buttonPressed); + float lastClickX; + float lastClickY; + float lastClickZ; + float lastClickdX; + float lastClickdY; + float lastClickdZ; + enum eLastClickState + { + lastClick_invalid, + lastClick_init, + lastClick_moving, + lastClick_stopped, + lastClick_oldRepeat, + lastClick_disabled + }; + float lastClickTolerance; + int lastClickState; + + // 4J Stu - Added to allow callback to tutorial to stay within Minecraft.Client + virtual void onCrafted(shared_ptr item); + + virtual void setAndBroadcastCustomSkin(DWORD skinId); + virtual void setAndBroadcastCustomCape(DWORD capeId); + +private: + bool isSolidBlock(int x, int y, int z); + bool m_bPlayerRespawned; + +protected: + bool checkInTile(double x, double y, double z); + +public: + void setSprinting(bool value); + void setExperienceValues(float experienceProgress, int totalExp, int experienceLevel); + + bool hasPermission(EGameCommand command); + + void updateRichPresence(); + + // 4J Stu - Added for telemetry + float m_sessionTimeStart; + float m_dimensionTimeStart; + +public: + void SetSessionTimerStart(void); + float getSessionTimer(void); + + float getAndResetChangeDimensionTimer(); + + virtual void handleCollectItem(shared_ptr item); + void SetPlayerAdditionalModelParts(vectorpAdditionalModelParts); + +private: + vector m_pAdditionalModelParts; +}; + + diff --git a/Minecraft.Client/MemTexture.cpp b/Minecraft.Client/MemTexture.cpp new file mode 100644 index 0000000..f587e82 --- /dev/null +++ b/Minecraft.Client/MemTexture.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "MemTexture.h" + +MemTexture::MemTexture(const wstring& _url, PBYTE pbData,DWORD dwBytes, MemTextureProcessor *processor) +{ + // 4J - added + count = 1; + id = -1; + isLoaded = false; + ticksSinceLastUse = 0; + + // 4J - TODO - actually implement + + // load the texture, and process it + //loadedImage=Textures::getTexture() + // 4J - remember to add deletes in here for any created BufferedImages when implemented + loadedImage = new BufferedImage(pbData,dwBytes); + if(processor==NULL) + { + + } + else + { + //loadedImage=processor.process(ImageIO.read(huc.getInputStream())); + } + + +} + +MemTexture::~MemTexture() +{ + delete loadedImage; +} \ No newline at end of file diff --git a/Minecraft.Client/MemTexture.h b/Minecraft.Client/MemTexture.h new file mode 100644 index 0000000..d11d68b --- /dev/null +++ b/Minecraft.Client/MemTexture.h @@ -0,0 +1,17 @@ +#pragma once +class BufferedImage; +class MemTextureProcessor; +using namespace std; + +class MemTexture { +public: + BufferedImage *loadedImage; + int count; + int id; + bool isLoaded; + int ticksSinceLastUse; + static const int UNUSED_TICKS_TO_FREE = 20; + + MemTexture(const wstring& _name, PBYTE pbData, DWORD dwBytes, MemTextureProcessor *processor); + ~MemTexture(); +}; \ No newline at end of file diff --git a/Minecraft.Client/MemTextureProcessor.h b/Minecraft.Client/MemTextureProcessor.h new file mode 100644 index 0000000..8e945e1 --- /dev/null +++ b/Minecraft.Client/MemTextureProcessor.h @@ -0,0 +1,8 @@ +#pragma once +class BufferedImage; + +class MemTextureProcessor +{ +public: + virtual BufferedImage *process(BufferedImage *read) = 0; +}; \ No newline at end of file diff --git a/Minecraft.Client/MemoryTracker.cpp b/Minecraft.Client/MemoryTracker.cpp new file mode 100644 index 0000000..c1652d3 --- /dev/null +++ b/Minecraft.Client/MemoryTracker.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "MemoryTracker.h" +#include "..\Minecraft.World\IntBuffer.h" +#include "..\Minecraft.World\ByteBuffer.h" +#include "..\Minecraft.World\FloatBuffer.h" + +unordered_map MemoryTracker::GL_LIST_IDS; +vector MemoryTracker::TEXTURE_IDS; + +int MemoryTracker::genLists(int count) +{ + int id = glGenLists(count); + GL_LIST_IDS.insert( pair(id,count) ); + return id; +} + +int MemoryTracker::genTextures() +{ + int id = glGenTextures(); + TEXTURE_IDS.push_back(id); + return id; +} + +void MemoryTracker::releaseLists(int id) +{ + AUTO_VAR(it, GL_LIST_IDS.find(id)); + if( it != GL_LIST_IDS.end() ) + { + glDeleteLists(id, it->second); + GL_LIST_IDS.erase(it); + } +} + +void MemoryTracker::releaseTextures() +{ + for (int i = 0; i < TEXTURE_IDS.size(); i++) + { + glDeleteTextures(TEXTURE_IDS.at(i)); + } + TEXTURE_IDS.clear(); +} + +void MemoryTracker::release() +{ + //for (Map.Entry entry : GL_LIST_IDS.entrySet()) + for(AUTO_VAR(it, GL_LIST_IDS.begin()); it != GL_LIST_IDS.end(); ++it) + { + glDeleteLists(it->first, it->second); + } + GL_LIST_IDS.clear(); + + releaseTextures(); +} + +ByteBuffer *MemoryTracker::createByteBuffer(int size) +{ + // 4J - was ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) + ByteBuffer *bb = ByteBuffer::allocate(size); + return bb; +} + +IntBuffer *MemoryTracker::createIntBuffer(int size) +{ + return createByteBuffer(size << 2)->asIntBuffer(); +} + +FloatBuffer *MemoryTracker::createFloatBuffer(int size) +{ + return createByteBuffer(size << 2)->asFloatBuffer(); +} \ No newline at end of file diff --git a/Minecraft.Client/MemoryTracker.h b/Minecraft.Client/MemoryTracker.h new file mode 100644 index 0000000..9567fac --- /dev/null +++ b/Minecraft.Client/MemoryTracker.h @@ -0,0 +1,28 @@ +#pragma once +#include "MemoryTracker.h" +class ByteBuffer; +class IntBuffer; +class FloatBuffer; +using namespace std; + +/** Original comment + * This class is used so we can release all memory (allocated on the graphics card on shutdown) + */ +// 4J - all member functions in here were synchronized +class MemoryTracker +{ +private: + static unordered_map GL_LIST_IDS; + static vector TEXTURE_IDS; + +public: + static int genLists(int count); + static int genTextures(); + static void releaseLists(int id); + static void releaseTextures(); + static void release(); + // 4J - note - have removed buffer types from here that we aren't using + static ByteBuffer *createByteBuffer(int size); + static IntBuffer *createIntBuffer(int size); + static FloatBuffer *createFloatBuffer(int size); +}; diff --git a/Minecraft.Client/MinecartModel.cpp b/Minecraft.Client/MinecartModel.cpp new file mode 100644 index 0000000..8a1ce0d --- /dev/null +++ b/Minecraft.Client/MinecartModel.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include "MinecartModel.h" +#include "ModelPart.h" + +MinecartModel::MinecartModel() : Model() +{ + cubes[0] = new ModelPart(this, 0, 10); + cubes[1] = new ModelPart(this, 0, 0); + cubes[2] = new ModelPart(this, 0, 0); + cubes[3] = new ModelPart(this, 0, 0); + cubes[4] = new ModelPart(this, 0, 0); + cubes[5] = new ModelPart(this, 44, 10); + + int w = 20; + int d = 8; + int h = 16; + int yOff = 4; + + cubes[0]->addBox((float)(-w / 2), (float)(-h / 2), -1, w, h, 2, 0); + cubes[0]->setPos(0, (float)(0 + yOff), 0); + + cubes[5]->addBox((float)(-w / 2 + 1), (float)(-h / 2 + 1), -1, w - 2, h - 2, 1, 0); + cubes[5]->setPos(0, (float)(0 + yOff), 0); + + cubes[1]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[1]->setPos((float)(-w / 2 + 1), (float)(0 + yOff), 0); + + cubes[2]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[2]->setPos((float)(+w / 2 - 1), (float)(0 + yOff), 0); + + cubes[3]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[3]->setPos(0, (float)(0 + yOff), (float)(-h / 2 + 1)); + + cubes[4]->addBox((float)(-w / 2 + 2), (float)(-d - 1), -1, w - 4, d, 2, 0); + cubes[4]->setPos(0, (float)(0 + yOff), (float)(+h / 2 - 1)); + + cubes[0]->xRot = PI / 2; + cubes[1]->yRot = PI / 2 * 3; + cubes[2]->yRot = PI / 2 * 1; + cubes[3]->yRot = PI / 2 * 2; + cubes[5]->xRot = -PI / 2; + + // 4J added - compile now to avoid random performance hit first time cubes are rendered + for (int i = 0; i < MINECART_LENGTH; i++) + { + cubes[i]->compile(1.0f/16.0f); + } +} + +void MinecartModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) +{ + cubes[5]->y = 4 - bob; + for (int i = 0; i < MINECART_LENGTH; i++) + { + cubes[i]->render(scale, usecompiled); + } +} diff --git a/Minecraft.Client/MinecartModel.h b/Minecraft.Client/MinecartModel.h new file mode 100644 index 0000000..de92556 --- /dev/null +++ b/Minecraft.Client/MinecartModel.h @@ -0,0 +1,13 @@ +#pragma once +#include "Model.h" + +class MinecartModel : public Model +{ +public: + static const int MINECART_LENGTH=6; + + ModelPart *cubes[MINECART_LENGTH]; + + MinecartModel(); + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); +}; diff --git a/Minecraft.Client/MinecartRenderer.cpp b/Minecraft.Client/MinecartRenderer.cpp new file mode 100644 index 0000000..0cae834 --- /dev/null +++ b/Minecraft.Client/MinecartRenderer.cpp @@ -0,0 +1,106 @@ +#include "stdafx.h" +#include "MinecartRenderer.h" +#include "MinecartModel.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" + +MinecartRenderer::MinecartRenderer() +{ + this->shadowRadius = 0.5f; + model = new MinecartModel(); +} + +void MinecartRenderer::render(shared_ptr _cart, double x, double y, double z, float rot, float a) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr cart = dynamic_pointer_cast(_cart); + + glPushMatrix(); + + __int64 seed = cart->entityId * 493286711l; + seed = seed * seed * 4392167121l + seed * 98761; + + float xo = ((((seed >> 16) & 0x7) + 0.5f) / 8.0f - 0.5f) * 0.004f; + float yo = ((((seed >> 20) & 0x7) + 0.5f) / 8.0f - 0.5f) * 0.004f; + float zo = ((((seed >> 24) & 0x7) + 0.5f) / 8.0f - 0.5f) * 0.004f; + + glTranslatef(xo, yo, zo); + + double xx = cart->xOld + (cart->x - cart->xOld) * a; + double yy = cart->yOld + (cart->y - cart->yOld) * a; + double zz = cart->zOld + (cart->z - cart->zOld) * a; + + double r = 0.3f; + + Vec3 *p = cart->getPos(xx, yy, zz); + + float xRot = cart->xRotO + (cart->xRot - cart->xRotO) * a; + + if (p != NULL) + { + Vec3 *p0 = cart->getPosOffs(xx, yy, zz, r); + Vec3 *p1 = cart->getPosOffs(xx, yy, zz, -r); + if (p0 == NULL) p0 = p; + if (p1 == NULL) p1 = p; + + x += p->x - xx; + y += (p0->y + p1->y) / 2 - yy; + z += p->z - zz; + + Vec3 *dir = p1->add(-p0->x, -p0->y, -p0->z); + if (dir->length() == 0) + { + } + else + { + dir = dir->normalize(); + rot = (float) (atan2(dir->z, dir->x) * 180 / PI); + xRot = (float) (atan(dir->y) * 73); + } + } + glTranslatef((float) x, (float) y, (float) z); + + glRotatef(180 - rot, 0, 1, 0); + glRotatef(-xRot, 0, 0, 1); + float hurt = cart->getHurtTime() - a; + float dmg = cart->getDamage() - a; + if (dmg < 0) dmg = 0; + if (hurt > 0) + { + glRotatef(Mth::sin(hurt) * hurt * dmg / 10 * cart->getHurtDir(), 1, 0, 0); + } + + if (cart->type != Minecart::RIDEABLE) + { + glPushMatrix(); + bindTexture(TN_TERRAIN); // 4J was L"/terrain.png" + float ss = 12 / 16.0f; + glScalef(ss, ss, ss); + + // 4J - changes here brought forward from 1.2.3 + if (cart->type == Minecart::CHEST) + { + glTranslatef(0 / 16.0f, 8 / 16.0f, 0 / 16.0f); + TileRenderer *tr = new TileRenderer(); + tr->renderTile(Tile::chest, 0, cart->getBrightness(a)); + delete tr; + } + else if (cart->type == Minecart::FURNACE) + { + glTranslatef(0, 6 / 16.0f, 0); + TileRenderer *tr = new TileRenderer(); + tr->renderTile(Tile::furnace, 0, cart->getBrightness(a)); + delete tr; + } + glPopMatrix(); + glColor4f(1, 1, 1, 1); + } + + bindTexture(TN_ITEM_CART); // 4J - was L"/item/cart.png" + glScalef(-1, -1, 1); + // model.render(0, 0, cart->getLootContent() * 7.1f - 0.1f, 0, 0, 1 / +// 16.0f); + model->render(cart, 0, 0, -0.1f, 0, 0, 1 / 16.0f, true); + glPopMatrix(); + +} \ No newline at end of file diff --git a/Minecraft.Client/MinecartRenderer.h b/Minecraft.Client/MinecartRenderer.h new file mode 100644 index 0000000..0335ba8 --- /dev/null +++ b/Minecraft.Client/MinecartRenderer.h @@ -0,0 +1,12 @@ +#pragma once +#include "EntityRenderer.h" + +class MinecartRenderer : public EntityRenderer +{ +protected: + Model *model; + +public: + MinecartRenderer(); + void render(shared_ptr _cart, double x, double y, double z, float rot, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj new file mode 100644 index 0000000..2137298 --- /dev/null +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -0,0 +1,36842 @@ + + + + + ContentPackage_NO_TU + Durango + + + ContentPackage_NO_TU + ORBIS + + + ContentPackage_NO_TU + PS3 + + + ContentPackage_NO_TU + PSVita + + + ContentPackage_NO_TU + x64 + + + ContentPackage_NO_TU + Xbox 360 + + + CONTENTPACKAGE_SYMBOLS + Durango + + + CONTENTPACKAGE_SYMBOLS + ORBIS + + + CONTENTPACKAGE_SYMBOLS + PS3 + + + CONTENTPACKAGE_SYMBOLS + PSVita + + + CONTENTPACKAGE_SYMBOLS + x64 + + + CONTENTPACKAGE_SYMBOLS + Xbox 360 + + + ContentPackage_Vita + Durango + + + ContentPackage_Vita + ORBIS + + + ContentPackage_Vita + PS3 + + + ContentPackage_Vita + PSVita + + + ContentPackage_Vita + x64 + + + ContentPackage_Vita + Xbox 360 + + + ContentPackage + Durango + + + ContentPackage + ORBIS + + + ContentPackage + PS3 + + + ContentPackage + PSVita + + + ContentPackage + x64 + + + ContentPackage + Xbox 360 + + + Debug + Durango + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + PSVita + + + Debug + x64 + + + Debug + Xbox 360 + + + ReleaseForArt + Durango + + + ReleaseForArt + ORBIS + + + ReleaseForArt + PS3 + + + ReleaseForArt + PSVita + + + ReleaseForArt + x64 + + + ReleaseForArt + Xbox 360 + + + Release + Durango + + + Release + ORBIS + + + Release + PS3 + + + Release + PSVita + + + Release + x64 + + + Release + Xbox 360 + + + + en-US + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3} + SAK + SAK + SAK + SAK + Xbox360Proj + title + + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + + + Application + MultiByte + WithExceptsWithRtti + SNC + + + Application + MultiByte + WithExceptsWithRtti + SNC + + + Application + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + v110 + + + Application + Unicode + v110 + false + + + Application + MultiByte + v110 + + + Application + MultiByte + v110 + + + Application + Unicode + v110 + true + + + Application + Unicode + v110 + true + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + true + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + true + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + true + WithExceptsWithRtti + NoTocRestore2 + + + Application + MultiByte + true + WithExceptsWithRtti + + + Application + MultiByte + true + WithExceptsWithRtti + + + Application + MultiByte + true + + + Application + MultiByte + true + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + Unicode + true + v110 + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Application + MultiByte + true + v110 + + + Clang + + + Clang + + + Clang + + + Clang + + + Clang + + + Clang + + + Clang + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)$(ProjectName).xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)$(ProjectName).xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + false + + + true + $(OutDir)$(ProjectName)_D.xex + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(MINECRAFT_CONSOLES_DIR)\Minecraft.Client\PSVita\Assert;$(ProjectDir);$(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + false + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + true + $(OutDir)$(ProjectName)_D.xex + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(MINECRAFT_CONSOLES_DIR)\Minecraft.Client\PSVita\Assert;$(ProjectDir);$(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + + + true + $(OutDir)$(ProjectName)_D.xex + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(MINECRAFT_CONSOLES_DIR)\Minecraft.Client\PSVita\Assert;$(ProjectDir);$(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)Durango\DurangoExtras;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + true + $(ProjectName) + $(Platform)_$(Configuration)\ + $(SolutionDir)$(Platform)_$(Configuration)\ + false + + + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)Durango\DurangoExtras;$(ProjectDir)\x64headers;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectName) + $(Platform)_$(Configuration)\ + $(SolutionDir)$(Platform)_$(Configuration)\ + + + true + $(OutDir)$(ProjectName)_D.xex + $(ProjectDir)Durango\DurangoExtras;$(ProjectDir)\x64headers;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectName) + $(Platform)_$(Configuration)\ + $(SolutionDir)$(Platform)_$(Configuration)\ + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + .elf + + + true + $(OutDir)$(ProjectName)_D.xex + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(MINECRAFT_CONSOLES_DIR)\Minecraft.Client\PSVita\Assert;$(ProjectDir);$(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + .elf + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + .elf + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)\..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + .self + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + .self + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)..\Minecraft.World\x64headers;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\Xbox\Sentient\Include;$(IncludePath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)Durango\DurangoExtras;$(ProjectDir)\x64headers;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectName) + $(SolutionDir)$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + true + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + + + false + $(OutDir)default$(TargetExt) + $(OutDir)default.xex + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Xbox\Sentient\Include;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + + + $(ProjectDir)\..\Minecraft.Client\Orbis\Assert;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir)\..\Minecraft.Client\Orbis\Assert;$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common; + + + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common; + + + $(ProjectDir)\..\Minecraft.Client\Orbis\Assert;$(ProjectDir);$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir)\..\Minecraft.Client\Orbis\Assert;$(ProjectDir);$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir)\..\Minecraft.Client\Orbis\Assert;$(ProjectDir);$(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)Orbis\OrbisExtras;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + false + false + + + + Use + Level3 + ProgramDatabase + Disabled + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;_XBOX;%(PreprocessorDefinitions) + Disabled + $(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + $(IntDir)/%(RelativeDir)/ + + + true + $(OutDir)$(ProjectName).pdb + xavatar2d.lib;xapilibd.lib;d3d9d.lib;d3dx9d.lib;xgraphicsd.lib;xboxkrnl.lib;xnetd.lib;xaudiod2.lib;xactd3.lib;x3daudiod.lib;xmcored.lib;xbdm.lib;vcompd.lib;xuirund.lib;xuirenderd.lib;xuihtmld.lib;xonline.lib;xhvd2.lib;qnetxaudio2d.lib;xpartyd.lib;..\Minecraft.World\Debug\Minecraft.World.lib;xbox\4JLibs\libs\4J_Input_d.lib;xbox\4JLibs\libs\4J_Storage_d.lib;xbox\4JLibs\libs\4J_Profile_d.lib;xbox\4JLibs\libs\4J_Render_d.lib;xsocialpostd.lib;xrnmd.lib;xbox\Sentient\libs\SenCoreD.lib;xbox\Sentient\libs\SenNewsD.lib;xbox\Sentient\libs\SenUGCD.lib;xbox\Sentient\libs\SenBoxArtD.lib;nuiapid.lib;STd.lib;NuiFitnessApid.lib;NuiHandlesd.lib;NuiSpeechd.lib;xhttpd.lib;xauthd.lib;xgetserviceendpointd.lib;xavd.lib;xjsond.lib;xbox\4JLibs\libs\4J_XTMS_d.lib;%(AdditionalDependencies) + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Common\res;$(RemoteRoot)=XboxMedia\AvatarAwards;$(RemoteRoot)\Tutorial=Common\Tutorial\Tutorial;$(RemoteRoot)=XboxMedia\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=XboxMedia\XZP\TMSFiles.xzp;$(RemoteRoot)\DummyTexturePack=Common\DummyTexturePack + + + + + Use + Level3 + ProgramDatabase + Full + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;%(PreprocessorDefinitions);PROFILE + Disabled + $(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + true + true + true + $(IntDir)/%(RelativeDir)/ + + + true + $(OutDir)$(ProjectName).pdb + xavatar2.lib;xapilibi.lib;d3d9i.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xparty.lib;xbox\4JLibs\libs\4J_Input_r.lib;xbox\4JLibs\libs\4J_Storage_r.lib;xbox\4JLibs\libs\4J_Profile_r.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\Release\Minecraft.World.lib;xbdm.lib;xsocialpost.lib;xrnm.lib;xbox\Sentient\libs\SenCore.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xbox\4JLibs\libs\4J_XTMS_r.lib;%(AdditionalDependencies) + xapilib.lib + true + false + UseLinkTimeCodeGeneration + true + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + false + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Common\res;$(RemoteRoot)=XboxMedia\AvatarAwards;$(RemoteRoot)\Tutorial=Common\Tutorial\Tutorial;$(RemoteRoot)=XboxMedia\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=XboxMedia\XZP\TMSFiles.xzp;$(RemoteRoot)\DummyTexturePack=Common\DummyTexturePack + + + + + Use + Level3 + ProgramDatabase + Full + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;%(PreprocessorDefinitions);PROFILE + Disabled + $(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + true + true + true + $(IntDir)/%(RelativeDir)/ + + + true + $(OutDir)$(ProjectName).pdb + xavatar2.lib;xapilibi.lib;d3d9i.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xparty.lib;xbox\4JLibs\libs\4J_Input_r.lib;xbox\4JLibs\libs\4J_Storage_r.lib;xbox\4JLibs\libs\4J_Profile_r.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\Release\Minecraft.World.lib;xbdm.lib;xsocialpost.lib;xrnm.lib;xbox\Sentient\libs\SenCore.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xtms.lib;%(AdditionalDependencies) + xapilib.lib + true + false + UseLinkTimeCodeGeneration + true + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + false + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Common\res;$(RemoteRoot)=XboxMedia\AvatarAwards;$(RemoteRoot)\Tutorial=Common\Tutorial\Tutorial;$(RemoteRoot)=XboxMedia\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=XboxMedia\XZP\TMSFiles.xzp;$(RemoteRoot)\DummyTexturePack=Common\DummyTexturePack + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;%(PreprocessorDefinitions) + Disabled + PS3\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + true + GenerateWarnings + Level0 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + + + true + $(OutDir)$(ProjectName).pdb + $(OutDir)Minecraft.World.a;ps3\4JLibs\libs\4j_Render_d.a;ps3\4JLibs\libs\4j_Input_d.a;ps3\4JLibs\libs\4j_Storage_d.a;ps3\4JLibs\libs\4j_Profile_d.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\mssspurs.o;ps3\Miles\lib\audps3.a;ps3\Miles\lib\BinkAPS3.A;ps3\Miles\lib\spu\mssppu_spurs.a;PS3\Iggy\lib\libiggy_ps3.a;ps3\Edge\lib\libedgezlib_dbg.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;PS3\PS3Extras\HeapInspector\Server\PS3\Debug_RTTI_EH\libHeapInspectorServer.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libnet_stub.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmddbg;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;%(AdditionalDependencies) + StripFuncsAndData + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;__PSVITA__;%(PreprocessorDefinitions) + Disabled + PSVita\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + true + GenerateWarnings + Level0 + 1700;613;1011;1786;2623;2624;1628 + -Xpch_override=1 %(AdditionalOptions) + Cpp11 + true + + + true + $(OutDir)$(ProjectName).pdb + -lSceDbg_stub;-lSceGxm_stub;-lSceAppUtil_stub;-lSceCommonDialog_stub;-lSceDisplay_stub;-lSceTouch_stub;-lSceCtrl_stub;-lSceAudio_stub;-lSceDbgFont;-lSceRazorCapture_stub_weak;-lSceSysmodule_stub;-lSceDeflt;-lScePng;$(OutDir)Minecraft.World.a;libSceRtc_stub.a;libSceFios2_stub_weak.a;libSceCes.a;libScePerf_stub.a;libScePerf_stub_weak.a;libSceUlt_stub.a;libSceUlt_stub_weak.a;libSceNpManager_stub_weak.a;libSceNpCommon_stub_weak.a;libSceNpCommerce2_stub.a;libSceHttp_stub.a;libSceNpTrophy_stub.a;libSceNpScore_stub.a;libSceRudp_stub_weak.a;libSceVoice_stub.a;libSceNetAdhocMatching_stub.a;libScePspnetAdhoc_stub.a;libScePower_stub.a;libSceAppUtil_stub.a;libSceAppMgr_stub.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2.a;..\Minecraft.Client\PSVita\Miles\lib\binkapsp2.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2midi.a;..\Minecraft.Client\PSVita\Miles\lib\fltpsp2.a;..\Minecraft.Client\Common\Network\Sony\sceRemoteStorage\psvita\lib\sceRemoteStorage.a + StripFuncsAndData + --strip-duplicates + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + xcopy /I /Y "$(SCE_PSP2_SDK_DIR)\target\sce_module" "$(TargetDir)\sce_module\" +if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;%(PreprocessorDefinitions) + Disabled + PS3\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + true + GenerateWarnings + Levels + Branchless2 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + $(ProjectDir)\..\Minecraft.Client\PS3\Assert + true + Yes + + + true + $(OutDir)$(ProjectName).pdb + $(OutDir)Minecraft.World.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\mssspurs.o;ps3\Miles\lib\audps3.a;ps3\Miles\lib\BinkAPS3.A;ps3\Miles\lib\spu\mssppu_spurs.a;PS3\Iggy\lib\libiggy_ps3.a;ps3\Edge\lib\libedgezlib.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;PS3\PS3Extras\HeapInspector\Server\PS3\Debug_RTTI_EH\libHeapInspectorServer.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libnet_stub.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmd;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;%(AdditionalDependencies) + StripFuncsAndData + --no-toc-restore --strip-duplicates + None + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;%(PreprocessorDefinitions) + Disabled + PS3\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + true + GenerateWarnings + Levels + Branchless2 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + $(ProjectDir)\..\Minecraft.Client\PS3\Assert + true + Yes + + + true + $(OutDir)$(ProjectName).pdb + $(OutDir)Minecraft.World.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\mssspurs.o;ps3\Miles\lib\audps3.a;ps3\Miles\lib\BinkAPS3.A;ps3\Miles\lib\spu\mssppu_spurs.a;PS3\Iggy\lib\libiggy_ps3.a;ps3\Edge\lib\libedgezlib.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;PS3\PS3Extras\HeapInspector\Server\PS3\Debug_RTTI_EH\libHeapInspectorServer.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libnet_stub.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmd;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;%(AdditionalDependencies) + StripFuncsAndData + --no-toc-restore --strip-duplicates + None + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;__PSVITA__;%(PreprocessorDefinitions) + Disabled + PSVita\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + true + GenerateWarnings + Levels + Branchless2 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Yes + Cpp11 + true + + + true + $(OutDir)$(ProjectName).pdb + -lSceDbg_stub;-lSceGxm_stub;-lSceAppUtil_stub;-lSceCommonDialog_stub;-lSceDisplay_stub;-lSceTouch_stub;-lSceCtrl_stub;-lSceAudio_stub;-lSceDbgFont;-lSceRazorCapture_stub_weak;-lSceSysmodule_stub;-lSceDeflt;-lScePng;$(OutDir)Minecraft.World.a;libSceRtc_stub.a;libSceFios2_stub_weak.a;libSceCes.a;libScePerf_stub.a;libScePerf_stub_weak.a;libSceUlt_stub.a;libSceUlt_stub_weak.a;libSceNpManager_stub_weak.a;libSceNpCommon_stub_weak.a;libSceHttp_stub.a;libSceNpTrophy_stub.a;libSceNpScore_stub.a;libSceRudp_stub_weak.a;libSceVoice_stub.a;libSceNetAdhocMatching_stub.a;libScePspnetAdhoc_stub.a;libScePower_stub.a;libSceAppUtil_stub.a;libSceAppMgr_stub.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2.a;..\Minecraft.Client\PSVita\Miles\lib\binkapsp2.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2midi.a;..\Minecraft.Client\PSVita\Miles\lib\fltpsp2.a;..\Minecraft.Client\Common\Network\Sony\sceRemoteStorage\psvita\lib\sceRemoteStorage.a + StripFuncsAndData + --strip-duplicates + None + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + xcopy /I /Y "$(SCE_PSP2_SDK_DIR)\target\sce_module" "$(TargetDir)\sce_module\" +if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;__PSVITA__;%(PreprocessorDefinitions) + Disabled + PSVita\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + true + false + GenerateWarnings + Level3 + Branchless2 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + false + Yes + Cpp11 + true + + + true + $(OutDir)$(ProjectName).pdb + -lSceGxm_stub;-lSceAppUtil_stub;-lSceCommonDialog_stub;-lSceDisplay_stub;-lSceTouch_stub;-lSceCtrl_stub;-lSceAudio_stub;-lSceSysmodule_stub;-lSceDeflt;-lScePng;$(OutDir)Minecraft.World.a;libSceRtc_stub.a;libSceFios2_stub_weak.a;libSceCes.a;libScePerf_stub.a;libScePerf_stub_weak.a;libSceUlt_stub.a;libSceUlt_stub_weak.a;libSceHttp_stub.a;libSceNet_stub.a;libSceSsl_stub.a;libSceNetCtl_stub.a;libSceNpManager_stub.a;libSceNpBasic_stub.a;libSceNpCommon_stub.a;libSceNpUtility_stub.a;libSceNpMatching2_stub.a;libSceNpScore_stub.a;libSceNpToolkit.a;libSceNpToolkitUtils.a;libSceNpTrophy_stub.a;libSceRudp_stub_weak.a;libSceVoice_stub.a;libSceNetAdhocMatching_stub.a;libScePspnetAdhoc_stub.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2.a;..\Minecraft.Client\PSVita\Miles\lib\binkapsp2.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2midi.a;..\Minecraft.Client\PSVita\Miles\lib\fltpsp2.a;libSceAppMgr_stub.a;libSceSysmodule_stub.a;libSceCommonDialog_stub.a;libSceCtrl_stub.a;libSceGxm_stub.a;libSceDisplay_stub.a;libSceSystemGesture_stub.a;libSceTouch_stub.a;libSceFios2_stub.a;libSceAppUtil_stub.a;libSceNearUtil_stub.a;libScePower_stub.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Input.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Profile.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Render.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Storage.a;..\Minecraft.Client\Common\Network\Sony\sceRemoteStorage\psvita\lib\sceRemoteStorage.a + StripFuncsAndData + --strip-duplicates + None + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + + + xcopy /I /Y "$(SCE_PSP2_SDK_DIR)\target\sce_module" "$(TargetDir)\sce_module\" +if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" + + + + + Use + Level3 + ProgramDatabase + Disabled + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + Windows64\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + + + true + $(OutDir)$(ProjectName).pdb + d3d11.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;%(AdditionalDependencies);XInput9_1_0.lib;..\Minecraft.Client\Windows64\Miles\Lib\mss64.lib + NotSet + false + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + Use + Level3 + ProgramDatabase + Disabled + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDebugDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;%(PreprocessorDefinitions) + Disabled + Durango\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + EnableFastChecks + false + true + true + $(ForcedInc) + $(SlashAI) + false + false + + + true + $(OutDir)$(ProjectName).pdb + ws2_32.lib;pixEvt.lib;d3d11_x.lib;combase.lib;kernelx.lib;uuid.lib;xaudio2.lib;..\Minecraft.World\Durango_Debug\Minecraft.World.lib;EtwPlus.lib;..\Minecraft.Client\Durango\DurangoExtras\xcompress.lib + NotSet + true + Console + true + + + false + false + Default + kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res +xcopy /q /y /i /s /e $(ProjectDir)Common\media\font\*.ttf $(LayoutDir)Image\Loose\Common\media\font +xcopy /q /y $(ProjectDir)Durango\*.png $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)Common\media\MediaDurango.arc $(LayoutDir)Image\Loose\Common\media +xcopy /q /y /i /s /e $(ProjectDir)Durango\Sound $(LayoutDir)Image\Loose\Sound +xcopy /q /y /i /s /e $(ProjectDir)music $(LayoutDir)Image\Loose\music +xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +copy /B /Y $(ProjectDir)Durango\DurangoExtras\xcompress.dll $(LayoutDir)Image\Loose\ +xcopy /q /y $(ProjectDir)Durango\DLCImages\*.png $(LayoutDir)Image\Loose\DLCImages\ +xcopy /q /y $(ProjectDir)Durango\DLCXbox1.cmp $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + + + Copying files for deployment + + + Package.appxmanifest + + + call $(ProjectDir)\Build\XboxOne\AppxPrebuild.cmd $(ProjectDir) + + + /VM %(AdditionalOptions) + + + call $(ProjectDir)\DurangoBuild\AppxPrebuild.cmd $(ProjectDir) + $(ProjectDir)\Durango\Autogenerated.appxmanifest + Creating Autogenerated.appxmanifest + $(ProjectDir)\Durango\manifest.xml + true + + + + + Use + Level3 + ProgramDatabase + Full + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + Windows64\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + + + true + $(OutDir)$(ProjectName).pdb + d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) + NotSet + false + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + Use + Level3 + ProgramDatabase + Full + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + Windows64\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + + + true + $(OutDir)$(ProjectName).pdb + d3d11.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) + NotSet + false + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + + + Use + Level3 + ProgramDatabase + MaxSpeed + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;PROFILE;NDEBUG;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;%(PreprocessorDefinitions) + Disabled + Durango\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + true + true + $(ForcedInc) + false + false + + + true + $(OutDir)$(ProjectName).pdb + ws2_32.lib;pixEvt.lib;d3d11_x.lib;combase.lib;kernelx.lib;uuid.lib;xaudio2.lib;..\Minecraft.World\Durango_Release\Minecraft.World.lib;EtwPlus.lib;..\Minecraft.Client\Durango\DurangoExtras\xcompress.lib + NotSet + true + Console + + + true + true + + + kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib + Default + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res +xcopy /q /y /i /s /e $(ProjectDir)Common\media\font\*.ttf $(LayoutDir)Image\Loose\Common\media\font +xcopy /q /y $(ProjectDir)Durango\*.png $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)Common\media\MediaDurango.arc $(LayoutDir)Image\Loose\Common\media +xcopy /q /y /i /s /e $(ProjectDir)Durango\Sound $(LayoutDir)Image\Loose\Sound +xcopy /q /y /i /s /e $(ProjectDir)music $(LayoutDir)Image\Loose\music +xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +copy /B /Y $(ProjectDir)Durango\DurangoExtras\xcompress.dll $(LayoutDir)Image\Loose\ +xcopy /q /y $(ProjectDir)Durango\DLCImages\*.png $(LayoutDir)Image\Loose\DLCImages\ +xcopy /q /y $(ProjectDir)Durango\DLCXbox1.cmp $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + + + Copying files for deployment + + + Package.appxmanifest + + + call $(ProjectDir)\Build\XboxOne\AppxPrebuild.cmd $(ProjectDir) + + + + + Use + Level3 + ProgramDatabase + MaxSpeed + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;PROFILE;NDEBUG;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;%(PreprocessorDefinitions) + Disabled + Durango\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + true + Default + false + Speed + true + true + $(ForcedInc) + false + false + + + true + $(OutDir)$(ProjectName).pdb + ws2_32.lib;pixEvt.lib;d3d11_x.lib;combase.lib;kernelx.lib;uuid.lib;xaudio2.lib;..\Minecraft.World\Durango_Release\Minecraft.World.lib;EtwPlus.lib;..\Minecraft.Client\Durango\DurangoExtras\xcompress.lib + NotSet + true + Console + + + true + true + + + kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib + Default + + + $(ProjectDir)xbox\xex-dev.xml + + + 1480659447 + + + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + true + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech;$(RemoteRoot)=Xbox\XZP\TMSFiles.xzp + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res +xcopy /q /y /i /s /e $(ProjectDir)Common\media\font\*.ttf $(LayoutDir)Image\Loose\Common\media\font +xcopy /q /y $(ProjectDir)Durango\*.png $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)Common\media\MediaDurango.arc $(LayoutDir)Image\Loose\Common\media +xcopy /q /y /i /s /e $(ProjectDir)Durango\Sound $(LayoutDir)Image\Loose\Sound +xcopy /q /y /i /s /e $(ProjectDir)music $(LayoutDir)Image\Loose\music +copy /B /Y $(ProjectDir)Durango\DurangoExtras\xcompress.dll $(LayoutDir)Image\Loose\ + + + Copying files for deployment + + + Package.appxmanifest + + + + + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + $(ProjectDir);%(AdditionalIncludeDirectories) + $(IntDir)/%(RelativeDir)/ + + + true + true + true + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xbox\4JLibs\libs\4J_XTMS_r.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + $(ProjectDir);%(AdditionalIncludeDirectories) + + + true + true + true + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xtms.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + $(ProjectDir);%(AdditionalIncludeDirectories) + + + true + true + true + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xtms.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _FINAL_BUILD;_CONTENT_PACKAGE;NDEBUG;_ITERATOR_DEBUG_LEVEL=0;_XBOX;%(PreprocessorDefinitions) + true + true + Disabled + Default + $(ProjectDir);%(AdditionalIncludeDirectories) + $(IntDir)/%(RelativeDir)/ + + + true + true + true + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage_NO_TU\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;xbox\4JLibs\libs\4J_XTMS_r.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)XboxMedia\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PS3\Iggy\include;%(AdditionalIncludeDirectories) + Levels + true + Branchless2 + Yes + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a;ps3\4JLibs\libs\4j_Render.a;ps3\4JLibs\libs\4j_Input.a;ps3\4JLibs\libs\4j_Storage.a;ps3\4JLibs\libs\4j_Profile.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\audps3.a;ps3\Miles\lib\spu\mssppu_spurs.a;ps3\Miles\lib\BinkAPS3.A;PS3\Iggy\lib\libiggy_ps3.a;ps3\Miles\lib\mssspurs.o;ps3\Edge\lib\libedgezlib.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libnet_stub.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmd;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;-lsysutil_avconf_ext_stub;%(AdditionalDependencies) + xapilib.lib + false + false + ELFFile + FullMapFile + --no-toc-restore --strip-duplicates --ppuguid %(AdditionalOptions) + StripSymsAndDebug + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;__PSVITA__;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PSVita\Iggy\include;%(AdditionalIncludeDirectories) + Level3 + false + Branchless2 + Yes + Cpp11 + true + true + + + true + $(OutDir)$(ProjectName).pdb + -lSceGxm_stub;-lSceAppUtil_stub;-lSceCommonDialog_stub;-lSceDisplay_stub;-lSceTouch_stub;-lSceCtrl_stub;-lSceAudio_stub;-lSceSysmodule_stub;-lSceDeflt;-lScePng;$(OutDir)Minecraft.World.a;libSceRtc_stub.a;libSceFios2_stub_weak.a;libSceCes.a;libScePerf_stub.a;libScePerf_stub_weak.a;libSceUlt_stub.a;libSceUlt_stub_weak.a;libSceHttp_stub.a;libSceNet_stub.a;libSceSsl_stub.a;libSceNetCtl_stub.a;libSceNpManager_stub.a;libSceNpBasic_stub.a;libSceNpCommon_stub.a;libSceNpUtility_stub.a;libSceNpMatching2_stub.a;libSceNpScore_stub.a;libSceNpToolkit.a;libSceNpToolkitUtils.a;libSceNpTrophy_stub.a;libSceRudp_stub_weak.a;libSceVoice_stub.a;libSceNetAdhocMatching_stub.a;libScePspnetAdhoc_stub.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2.a;..\Minecraft.Client\PSVita\Miles\lib\binkapsp2.a;..\Minecraft.Client\PSVita\Miles\lib\msspsp2midi.a;..\Minecraft.Client\PSVita\Miles\lib\fltpsp2.a;libSceAppMgr_stub.a;libSceSysmodule_stub.a;libSceCommonDialog_stub.a;libSceCtrl_stub.a;libSceGxm_stub.a;libSceDisplay_stub.a;libSceSystemGesture_stub.a;libSceTouch_stub.a;libSceFios2_stub.a;libSceAppUtil_stub.a;libSceNearUtil_stub.a;libScePower_stub.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Input.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Profile.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Render.a;..\Minecraft.Client\PSVita\4JLibs\libs\4J_Storage.a;..\Minecraft.Client\Common\Network\Sony\sceRemoteStorage\psvita\lib\sceRemoteStorage.a + StripFuncsAndData + --strip-duplicates + None + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /I /Y "$(SCE_PSP2_SDK_DIR)\target\sce_module" "$(TargetDir)\sce_module\" +if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PS3\Iggy\include;%(AdditionalIncludeDirectories) + Levels + true + Branchless2 + Yes + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a;ps3\4JLibs\libs\4j_Render.a;ps3\4JLibs\libs\4j_Input.a;ps3\4JLibs\libs\4j_Storage.a;ps3\4JLibs\libs\4j_Profile.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\audps3.a;ps3\Miles\lib\spu\mssppu_spurs.a;ps3\Miles\lib\BinkAPS3.A;PS3\Iggy\lib\libiggy_ps3.a;ps3\Miles\lib\mssspurs.o;ps3\Edge\lib\libedgezlib.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libnet_stub.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmd;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;-lsysutil_avconf_ext_stub;%(AdditionalDependencies) + xapilib.lib + false + false + ELFFile + FullMapFile + --no-toc-restore --strip-duplicates --ppuguid %(AdditionalOptions) + None + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;__PSVITA__;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PS3\Iggy\include;%(AdditionalIncludeDirectories) + Levels + true + Branchless2 + Yes + Cpp11 + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a + xapilib.lib + false + false + ELFFile + FullMapFile + --strip-duplicates + None + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _RELEASE_FOR_ART;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PS3\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + Level2 + false + Branchless2 + $(ProjectDir)\..\Minecraft.Client\PS3\Assert + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a;ps3\4JLibs\libs\4j_Render_r.a;ps3\4JLibs\libs\4j_Input_r.a;ps3\4JLibs\libs\4j_Storage_r.a;ps3\4JLibs\libs\4j_Profile_r.a;ps3\Miles\lib\mssps3.a;ps3\Miles\lib\mssspurs.o;ps3\Miles\lib\audps3.a;ps3\Miles\lib\BinkAPS3.A;ps3\Miles\lib\spu\mssppu_spurs.a;PS3\Iggy\lib\libiggy_ps3.a;Common\Network\Sony\sceRemoteStorage\ps3\lib\sceRemoteStorage.a;PS3\PS3Extras\HeapInspector\Server\PS3\Release_RTTI_EH\libHeapInspectorServer.a;libsntuner.a;libpngdec_stub.a;libpngenc_stub.a;libjpgdec_stub.a;libjpgenc_stub.a;libnet_stub.a;libedgezlib_dbg.a;libsysutil_savedata_stub.a;libsysutil_userinfo_stub.a;libsysutil_np_trophy_stub.a;libsysutil_game_stub.a;libsysutil_avc2_stub.a;libsysutil_np_commerce2_stub.a;libsysutil_avconf_ext_stub.a;libhttp_stub.a;libhttp_util_stub.a;libssl_stub.a;libsysutil_screenshot_stub.a;libsysutil_np_tus_stub.a;-lresc_stub;-lgcm_cmd;-lgcm_sys_stub;-lsysmodule_stub;-lm;-lsysutil_stub;-lio_stub;-ldbgfont_gcm;-lpthread;-lpadfilter;-lcgb;-laudio_stub;-lfs_stub;-lspurs_stub;-lspurs_jq_stub;-lrtc_stub;-lsysutil_oskdialog_ext_stub;-ll10n_stub;-lsysutil_np_stub;-lsysutil_np2_stub;-lnetctl_stub;-lnet_stub;-lrudp_stub;%(AdditionalDependencies) + xapilib.lib + false + false + FSELFFile + None + + + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_RELEASE_FOR_ART;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;__PSVITA__;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + PS3\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + Level2 + false + Branchless2 + Cpp11 + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a + xapilib.lib + false + false + FSELFFile + None + --strip-duplicates + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + StripFuncsAndData + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;__PSVITA__;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + true + Disabled + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Cpp11 + + + true + true + false + $(OutDir)default.pdb + true + $(OutDir)Minecraft.World.a + xapilib.lib + false + false + StripFuncsAndData + --strip-duplicates + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + + + Level3 + Use + MaxSpeed + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_FINAL_BUILD;_CONTENT_PACKAGE;NDEBUG;__WRL_NO_DEFAULT_LIB__;_XM_AVX_INTRINSICS_;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + Disabled + Default + Durango\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + true + false + $(ForcedInc) + + + true + true + false + $(OutDir)$(ProjectName).pdb + false + ws2_32.lib;d3d11_x.lib;combase.lib;kernelx.lib;uuid.lib;xaudio2.lib;..\Minecraft.World\Durango_ContentPackage\Minecraft.World.lib;EtwPlus.lib;..\Minecraft.Client\Durango\DurangoExtras\xcompress.lib + kernel32.lib;oldnames.lib;runtimeobject.lib;ole32.lib + true + false + Console + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res +xcopy /q /y /i /s /e $(ProjectDir)Common\media\font\*.ttf $(LayoutDir)Image\Loose\Common\media\font +xcopy /q /y $(ProjectDir)Durango\*.png $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)Common\media\MediaDurango.arc $(LayoutDir)Image\Loose\Common\media +xcopy /q /y /i /s /e $(ProjectDir)Durango\Sound $(LayoutDir)Image\Loose\Sound +xcopy /q /y /i /s /e $(ProjectDir)music $(LayoutDir)Image\Loose\music +copy /B /Y $(ProjectDir)Durango\DurangoExtras\xcompress.dll $(LayoutDir)Image\Loose\ +xcopy /q /y $(ProjectDir)Durango\DLCImages\*.png $(LayoutDir)Image\Loose\DLCImages\ +xcopy /q /y $(ProjectDir)Durango\DLCXbox1.cmp $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + + + Copying files for deployment + + + Autogenerated.appxmanifest + + + call $(ProjectDir)\Build\XboxOne\AppxPrebuild.cmd $(ProjectDir) + + + _UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res + + + Copying files for deployment + + + call $(ProjectDir)\DurangoBuild\AppxPrebuild.cmd $(ProjectDir) + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;..\Minecraft.Client\Durango\DurangoExtras\xcompress.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res +xcopy /q /y /i /s /e $(ProjectDir)Common\media\font\*.ttf $(LayoutDir)Image\Loose\Common\media\font +xcopy /q /y $(ProjectDir)Durango\*.png $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)Common\media\MediaDurango.arc $(LayoutDir)Image\Loose\Common\media +xcopy /q /y /i /s /e $(ProjectDir)Durango\Sound $(LayoutDir)Image\Loose\Sound +xcopy /q /y /i /s /e $(ProjectDir)music $(LayoutDir)Image\Loose\music +xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +copy /B /Y $(ProjectDir)Durango\DurangoExtras\xcompress.dll $(LayoutDir)Image\Loose\ +xcopy /q /y $(ProjectDir)Durango\DLCImages\*.png $(LayoutDir)Image\Loose\DLCImages\ +xcopy /q /y $(ProjectDir)Durango\DLCXbox1.cmp $(LayoutDir)Image\Loose +xcopy /q /y $(ProjectDir)DurangoMedia\DLC $(LayoutDir)Image\Loose\DLC +xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU + + + Copying files for deployment + + + call $(ProjectDir)\DurangoBuild\AppxPrebuild.cmd $(ProjectDir) + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res + + + Copying files for deployment + + + call $(ProjectDir)\DurangoBuild\AppxPrebuild.cmd $(ProjectDir) + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;NDEBUG;_XBOX;_CONTENT_PACKAGE;%(PreprocessorDefinitions); + true + true + Disabled + Default + + + true + true + false + $(OutDir)default.pdb + true + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xapilib.lib + false + false + + + $(ProjectDir)xbox\xex.xml + 1480659447 + 584111F7=$(ProjectDir)xbox\GameConfig\Minecraft.spa,RO;media=$(ProjectDir)xbox\XZP\Minecraft.xzp,RO + + + CopyToHardDrive + $(RemoteRoot)=$(ImagePath);$(RemoteRoot)\res=Xbox\res;$(RemoteRoot)=Xbox\AvatarAwards;$(RemoteRoot)\Tutorial=Xbox\Tutorial\Tutorial;$(RemoteRoot)=Xbox\584111F70AAAAAAA;$(RemoteRoot)=Xbox\kinect\speech + + + xcopy /q /y /i /s /e $(ProjectDir)Common\res $(LayoutDir)Image\Loose\Common\res + + + Copying files for deployment + + + + + WarningsOff + true + Use + $(OutDir)$(ProjectName).pch + true + true + Level2 + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED + + + ..\Minecraft.World\ORBIS_Release\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render.a;Orbis\4JLibs\libs\4j_Input_r.a;Orbis\4JLibs\libs\4J_Storage_r.a;Orbis\4JLibs\libs\4J_Profile_r.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceNpSnsFacebookDialog_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceRemoteplay_stub_weak;-lSceSaveDataDialog_stub_weak;-lSceErrorDialog_stub_weak;-lSceMsgDialog_stub_weak;-lSceGameLiveStreaming_stub_weak;%(AdditionalDependencies) + true + + + false + + + + + WarningsOff + true + Use + $(OutDir)$(ProjectName).pch + true + true + Level2 + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED + + + ..\Minecraft.World\ORBIS_Release\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render.a;Orbis\4JLibs\libs\4j_Input_r.a;Orbis\4JLibs\libs\4J_Storage_r.a;Orbis\4JLibs\libs\4J_Profile_r.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceNpSnsFacebookDialog_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceRemoteplay_stub_weak;%(AdditionalDependencies) + true + + + false + + + + + Use + $(OutDir)$(ProjectName).pch + true + Level3 + true + true + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD + false + true + + + false + + + ..\ORBIS_ContentPackage\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render.a;Orbis\4JLibs\libs\4j_Input.a;Orbis\4JLibs\libs\4J_Storage.a;Orbis\4JLibs\libs\4J_Profile.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceRemoteplay_stub_weak;-lSceSaveDataDialog_stub_weak;-lSceNpSnsFacebookDialog_stub_weak;-lSceErrorDialog_stub_weak;-lSceMsgDialog_stub_weak;-lSceGameLiveStreaming_stub_weak + + + None + + + StripFuncsAndData + + + + + Use + $(OutDir)$(ProjectName).pch + true + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD + Level3 + true + true + false + true + + + false + + + ..\ORBIS_ContentPackage\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render.a;Orbis\4JLibs\libs\4j_Input.a;Orbis\4JLibs\libs\4J_Storage.a;Orbis\4JLibs\libs\4J_Profile.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceRemoteplay_stub_weak;-lSceSaveDataDialog_stub_weak + StripFuncsAndData + + + + + Use + $(OutDir)$(ProjectName).pch + true + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ART_BUILD + WarningsOff + Levels + + + false + + + StripSymsAndDebug + + + StripFuncsAndData + ..\Minecraft.World\ORBIS_ReleaseForArt\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render.a;Orbis\4JLibs\libs\4j_Input_r.a;Orbis\4JLibs\libs\4J_Storage_r.a;Orbis\4JLibs\libs\4J_Profile_r.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceNpSnsFacebookDialog_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceRemoteplay_stub_weak;-lSceSaveDataDialog_stub_weak;-lSceErrorDialog_stub_weak;-lSceMsgDialog_stub_weak + + + + + Use + $(OutDir)$(ProjectName).pch + true + + + false + + + + + Use + $(OutDir)$(ProjectName).pch + true + true + WarningsOff + true + true + Orbis\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_DEBUG;%(PreprocessorDefinitions) + + + ..\Minecraft.World\ORBIS_Debug\Minecraft.World.a;Orbis\4JLibs\libs\4j_Render_d.a;Orbis\4JLibs\libs\4j_Input_d.a;Orbis\4JLibs\libs\4J_Storage_d.a;Orbis\4JLibs\libs\4J_Profile_d.a;Orbis\Iggy\lib\libiggy_orbis.a;Orbis\Miles\lib\mssorbis.a;Orbis\Miles\lib\binkaorbis.a;Common\Network\Sony\sceRemoteStorage\ps4\lib\sceRemoteStorage.a;-lSceGnmDriver_stub_weak;-lSceGnmx;-lSceGnm;-lSceGpuAddress;-lSceCes;-lSceVideoOut_stub_weak;-lScePad_stub_weak;-lScePngDec_stub_weak;-lScePngEnc_stub_weak;-lSceFios2_stub_weak;-lSceUlt_stub_weak;-lSceShaderBinary;-lSceUserService_stub_weak;-lSceSysmodule_stub_weak;-lScePerf_stub_weak;-lSceImeDialog_stub_weak;-lScePosix_stub_weak;-lSceAudioOut_stub_weak;-lSceSaveData_stub_weak;-lSceRtc_stub_weak;-lSceSystemService_stub_weak;-lSceNetCtl_stub_weak;-lSceNpCommon_stub_weak;-lSceNpManager_stub_weak;-lSceNpToolkit_rtti;-lSceNpToolkitUtils_rtti;-lSceNpWebApi_stub_weak;-lSceNpAuth_stub_weak;-lSceNpTrophy_stub_weak;-lSceInvitationDialog_stub_weak;-lSceGameCustomDataDialog_stub_weak;-lSceNpCommerce_stub_weak;-lSceNet_stub_weak;-lSceHttp_stub_weak;-lSceSsl_stub_weak;-lSceNpMatching2_stub_weak;-lSceNpTus_stub_weak;-lSceNpUtility_stub_weak;-lSceNpScore_stub_weak;-lSceCommonDialog_stub_weak;-lSceNpSns_stub_weak;-lSceRudp_stub_weak;-lSceAppContent_stub_weak;-lSceVoice_stub_weak;-lSceAudioIn_stub_weak;-lSceNpSnsFacebookDialog_stub_weak;-lSceRemotePlay_stub_weak;-lSceSaveDataDialog_stub_weak;-lSceErrorDialog_stub_weak;-lSceMsgDialog_stub_weak;-lSceGameLiveStreaming_stub_weak + + + false + + + + + + XML + Designer + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + true + false + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + Designer + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + false + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + + + + + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + true + false + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + + + + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + false + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + false + false + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + false + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + true + false + true + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + false + false + true + true + true + true + true + true + false + true + false + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + + + + + + + + + + + + false + false + false + false + false + false + + + + + + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + true + false + false + true + true + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + false + true + true + true + true + true + true + true + true + true + false + false + false + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + + true + true + true + true + true + true + true + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + true + true + false + false + false + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + false + + + NotUsing + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + true + true + true + true + true + true + true + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + true + true + true + true + true + true + true + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + true + true + true + true + true + true + true + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + true + true + true + true + true + true + true + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + true + true + true + true + true + true + true + false + true + false + true + false + true + false + true + false + true + false + false + true + true + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + false + false + false + NotUsing + false + + + + + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + true + true + true + true + true + true + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + true + false + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + Use + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + true + true + true + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + Disabled + Disabled + Disabled + Disabled + false + false + + + + + + + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + + + + + + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + false + false + false + false + false + false + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + false + false + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + false + false + false + false + false + false + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + false + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + false + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + false + true + false + true + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + false + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + false + false + true + true + true + true + true + true + false + true + false + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + false + false + false + false + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + + + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + false + false + false + false + false + false + Create + Create + Create + Create + Create + Create + Create + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(IntDir)%(Filename)$(ObjectExt) + $(IntDir)%(Filename)$(ObjectExt) + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + false + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + NotUsing + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + -Xpch_override=1 + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + true + true + true + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + + + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + + + true + true + true + + + true + true + true + + + true + true + true + true + true + true + true + false + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + false + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + false + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + + + + true + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + false + false + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + false + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + false + false + true + true + true + true + true + + + + + + + + + Durango\Network\windows.xbox.networking.realtimesession.winmd + true + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.filters b/Minecraft.Client/Minecraft.Client.vcxproj.filters new file mode 100644 index 0000000..1d4d301 --- /dev/null +++ b/Minecraft.Client/Minecraft.Client.vcxproj.filters @@ -0,0 +1,6023 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {e23474e2-447c-41a9-82be-e32747f5b196} + + + {d7b60dd5-624a-46b3-b81d-f5f74550f613} + + + {68105641-375c-4565-9945-7890df6d82d9} + + + {8be4617d-3699-46a4-8769-28edb23c89f0} + + + {0b94741f-653f-48c2-874f-6aa69e7e9622} + + + {20606602-63d3-460c-b33e-d3e747a3d8db} + + + {4afb96fe-3fcb-4bd1-89a1-adfea86c73fb} + + + {304b5ee1-bfdb-489f-8e24-0a4e61177ca1} + + + {225f9542-472d-45c1-9046-eb2a46ab029c} + + + {14e20ee1-fe3b-481b-acce-7a634ee9c1d6} + + + {486b537f-d140-4a23-8409-fe3bc4184009} + + + {91ef92f9-432b-4b8f-9f16-4efd211003a1} + + + {66656f96-a5da-48c6-a7f9-79ab343dbd2f} + + + {3423fd63-b0d7-4f50-b0ca-549386c6cf57} + + + {d097d6ee-2ae4-48d8-8b5a-9b48882bdb2c} + + + {1d0a6eec-14cd-4e6d-8a0a-f5f8f0ab5240} + + + {269fab49-d870-4358-baef-32ed6ac9eca7} + + + {fef42379-3d37-4ef3-aa73-b19aaa77e3cc} + + + {bce4041a-9336-45e7-bd40-ed057ed96ee8} + + + {9756cb73-3f40-4fcb-9bab-5a3ce3c4d2f6} + + + {bb820acc-a8eb-4e36-8b4e-9517263ed51b} + + + {3c3aca1d-0e3e-43f1-b4cd-f8dfce2d29e7} + + + {9f1bf1ed-5366-4a29-b3f3-296725a7b01c} + + + {3e905494-b5dc-4084-a1fe-cbd91b9af667} + + + {afb98298-0033-42ec-98a5-93f8d347ee0d} + + + {1953b4f7-41ea-430b-ad2d-e3d7c352b647} + + + {39ab4d1f-8199-4ec7-948e-3d42ad8c8573} + + + {73bbdc5b-04f3-42f8-bf3b-769335e19178} + + + {385338b7-fa77-4c46-a7f2-89c82dc6e192} + + + {0f94b57d-88f8-4a20-b4e4-d1fa95d8f439} + + + {abe2942f-f984-4930-9e2d-9c9c2b35ac74} + + + {a2be9911-8785-4f6a-932e-e03321ee466b} + + + {7cb56f76-52cf-4303-8631-e1471fdc09a0} + + + {098e2985-9c15-450f-baa2-78604e7c1f54} + + + {6c286ad1-f871-408a-be6e-db44e7edcd2c} + + + {7c254dd0-f36f-4001-83cc-1634a2c792c2} + + + {2a26afce-4160-4fb0-8d01-e394a669dae6} + + + {9229f78c-152c-47d5-858a-fd054b856a1c} + + + {81ab078c-fc67-460c-befd-616dbe4bc3bc} + + + {db324829-af2c-428d-9710-8ad20ecc3fd0} + + + {758ac0be-6bd7-42c0-9b09-fdd452c0e134} + + + {c2dcdce8-b00f-4094-b0de-dad838d49525} + + + {8fd2f4e7-b93a-4067-93b8-a7ebac6d4a9c} + + + {93a41380-e12e-4f7a-bb7b-459f7169faed} + + + {4eb1ba28-620f-4136-979c-4dc91c44b666} + + + {5ac21685-36c0-4cd1-8861-e6a0e4a37c62} + + + {1b4710ff-c513-4a11-9d34-ff36fe1b4246} + + + {f4877497-fdf4-48a8-ada4-e6042f632e7a} + + + {45f40847-5b95-4dca-82f2-7616d7a35e54} + + + {1a98ef4c-6c9d-4a22-93d7-89f0fc3320fd} + + + {3be02e3c-c628-4315-a507-a9fe7733af01} + + + {c6dffb6d-2cf6-4c3e-89a3-fb05229b98aa} + + + {7d088a48-eeda-4783-94f7-c0d09b06f347} + + + {a466219c-afde-4184-8b84-91df32e5b892} + + + {40d6ff43-3d13-42ab-99ae-ebc9d585110f} + + + {047a3693-2040-404d-a386-2e5795b231d3} + + + {2509ddc5-330c-45da-a6f9-d37b858acd34} + + + {b2935b29-33d3-4d57-a145-753a646e5de4} + + + {26661545-d0a0-438a-a775-31cec1fb7849} + + + {5f5f5678-57b0-4f7f-b7dc-1ddd01ea2774} + + + {a19d2d41-9a2f-4631-941b-c3bfa7c2fdfd} + + + {bcdb8322-b7e6-482b-a3da-eb3f84dac713} + + + {e2959475-d5c8-4874-a782-fb5266e4441c} + + + {794dfcdb-98c6-4939-b04d-86b9657d4ff6} + + + {775f3088-bc52-43a3-b9ec-7f3f58508240} + + + {ebe1835b-76a8-408d-b3ee-70ffa4db7907} + + + {45bddf1c-e6d6-4a78-9b7d-73d7511e070d} + + + {16186163-4c73-4fa8-85c7-57d2b34e3fe9} + + + {b33b6793-e585-487e-8626-0096242f8e04} + + + {1264d92e-fa06-40ef-846c-4ce2a99e8ccc} + + + {b28c2ec8-a257-41ca-aad2-cf2ced04e4fa} + + + {7369fca1-3096-4b7d-a93e-924587f23108} + + + {e0eabf73-2721-46f9-bc46-4e0292bb53d3} + + + {bf450dfd-c9e8-4120-8a4c-3860b606637e} + + + {4412cd12-307d-407c-8d4a-34df3274c892} + + + {91fbb0f7-3d94-4786-aa07-c9c57a6db9a9} + + + {afb9404f-f23d-46b1-b4d5-4b1096d5bf40} + + + {716a30f7-f9dd-43bf-9228-646dff4c58b1} + + + {3531c304-b08e-48ae-860c-773f6702ec4d} + + + {924f367a-618c-429e-9866-f60821f21d4a} + + + {e3e43b8f-e455-4222-a92e-f6567a41e326} + + + {61ac879d-17b0-402b-b29f-88c60a1161c7} + + + {4f5c7e99-5cbc-4db4-99c4-37db45537198} + + + {33341824-5702-4a56-b75c-9dac57e49349} + + + {4dbeff57-70bc-4b4c-b5d0-4c6834968d85} + + + {4be5c8d2-8944-4e8f-9d79-b1abc4b66f8f} + + + {ba24985e-3b16-45af-963e-9f2edca20b1a} + + + {77957a66-a869-4b9b-bbda-e7f43e01096f} + + + {fa09ab64-0a3f-429b-93cb-149ee490767b} + + + {dec59bc5-d9d3-4be5-b449-3df3b430eb39} + + + {0bcca89e-0d2d-407b-b1e4-878465404901} + + + {395a09e4-1ff9-458c-8fb8-a4cb28aa4881} + + + {056ec81c-c93f-4c56-9bcb-697cda24a612} + + + {d3d4cc74-edfa-4bbb-8e66-7252dbbc131b} + + + {1511a94f-13bc-49e0-bf75-7cdf98f1e77f} + + + {f0b2e12a-e042-49bc-a5fa-78d1cf79e5d3} + + + {d2020762-d261-4c89-bbb9-0c7113012882} + + + {88ebd63d-2bbc-438a-a810-9b26fcfdd908} + + + {2cf98618-28c5-46df-9ff7-3d331ee4a275} + + + {3c643f18-092d-4870-a206-8dc906748a64} + + + {11cc2598-d569-47ad-8843-7a8296878be9} + + + {33371180-d4ec-4439-8a95-059babcc1db9} + + + {c6d264ea-d4ac-4f3f-81f7-0d91fdc27713} + + + {bde45e25-7dce-4a39-a2bf-dad234708b07} + + + {9685dbaa-ed65-453c-ba57-ec01e59022ae} + + + {92ead381-f2b8-4c6d-a3ca-c6fbc7753361} + + + {2031e778-56ff-4126-b09d-4ec59453b21c} + + + {98e39923-fe62-42d5-8650-746c2d61efd2} + + + {094cddb4-1ac5-424b-80e3-e3b0e9bb3b05} + + + {914f66a5-b1a7-4615-9adc-287d28158eee} + + + {36ba326b-c3a1-473e-8cb4-054e34c276a8} + + + {f9dae5df-fabf-41f9-9b13-8d32e5b5baa5} + + + {e634a43c-ee4c-4adc-8847-c667fdc73c5f} + + + {71d6ccac-7a6e-4399-987b-06b606056f59} + + + {05765c7e-26d6-4760-b0f6-7aa9f374d163} + + + {02363026-02fd-4efc-a115-6ae3dc652546} + + + {d71c6707-d6ba-4ab5-a505-a916e007e60d} + + + {eb5eb5f3-0ea7-4658-a8fb-634eb289941d} + + + {46d5754b-1818-4685-a16d-f7415f61868c} + + + {541f67ae-2627-40af-8316-d76ee9bb6985} + + + {ccfdb851-7965-4551-88bb-4312ddbf830a} + + + {2b9abc76-798a-4aae-ba50-2dfc8f78ae81} + + + {35491a01-dd6f-4313-b857-5e3eb323b44f} + + + {bcac2142-c160-4a73-96c5-cbdf681a16f0} + + + {290b2f1c-dcd8-4ebc-9d6d-fa6de190117e} + + + {a7ec80a7-ea10-438c-a10f-7eeef759c32d} + + + {9a2c49f6-2f9d-4e9d-a4ea-a0a04ecba75f} + + + {24e96065-3dd4-4150-bde2-128d133fd2c4} + + + {10961b95-cb43-4a00-b999-04b66a1a0b43} + + + {6aaa8af3-3df6-43f4-9346-9adfe45ca3a7} + + + {aba0f713-fcfb-417e-9616-c8474225de71} + + + {94298ae6-25e0-4cc9-8c5a-efd53e156baa} + + + {6ec99327-b465-4e61-b064-023a09bdf907} + + + {2095b7df-1779-4788-b004-3479d5ab59d8} + + + {4c9eb137-a48c-44a4-be08-ef1745834ece} + + + {2bae7445-385f-4b0e-a3ec-11c1c584f930} + + + {7c655cf2-f74e-4e6a-9114-405f5bc28a56} + + + {a392080f-8e8b-42be-832a-a35869dba580} + + + {42dca5dc-e462-4537-9929-847a044eb116} + + + {0da3a534-f8c9-4d0c-a73f-dfeb402b27c1} + + + {2e1858a4-a24b-49d8-b19c-c24b45f75a4f} + + + {096eb9da-ee6c-46ba-a0f4-dd8d1748b6a1} + + + {50dc7509-93df-4e0a-8a9a-cea040e92180} + + + {67544d93-633f-46a8-9cdf-8ae646a745d1} + + + {08da2d2a-3276-4109-b190-05fbc4709398} + + + {cef89641-7631-4c30-855f-603163446077} + + + {a15076ff-0dbe-4fb5-8b58-4ceb4b189c8f} + + + {a36a05f3-bc99-4097-b7a8-f81c37eec6e3} + + + {c9fd57aa-ede6-46f3-b968-0f4a7c64f7f1} + + + {bcd2eaff-60b9-41f4-8e1a-258639b27f99} + + + {ebc154be-8d55-478b-9038-856d445aaf15} + + + {0749340b-e216-450a-a02e-001917097ba5} + + + {6b6c31a6-0b8d-4dc0-8d6e-38ab6de709ff} + + + {d7537fdd-877b-461c-9c86-3235843fcfc0} + + + {61e77fc3-d018-4e08-985c-9871eca81fe2} + + + {2c983999-feb8-40db-885b-abf061e2ab58} + + + {093a811c-5f90-4c0e-b260-4b637079730a} + + + {c2fdb165-80e4-4ce0-9bf1-12e5c58f83a5} + + + {ad68d69a-99d0-4eea-9bb4-58cb7083a7a1} + + + {a04f2d63-3e47-470f-b4ac-c1d5caf8ce56} + + + {a0aa2098-142e-4688-8d73-00ec7e5e9361} + + + {f7fc551a-1d1a-4584-af3b-2eadb712b0f7} + + + {7155e1ba-d9b6-473b-8c59-77dd883b766f} + + + {017984f1-6659-4a44-96fd-7dbb8f9b2654} + + + {5d6f34a3-c647-479d-a1a9-89a9ffca4ab9} + + + {6f049254-6585-4a90-be74-70d3878d864f} + + + {e4051e75-f566-41ce-b86a-46c838872963} + + + {18d3c9bc-132e-4770-a665-fc030eb86394} + + + {8a2156f5-3462-447b-b04d-e555a917fbf2} + + + {e0cb4d67-dd35-43ab-88cc-63173cc31125} + + + {090821e7-2a93-44de-bf5e-d5dbbcb41621} + + + {11f70fef-83b4-4fb9-85ab-51109fbb6a56} + + + {7b594635-988d-40aa-8a00-0d60b1f49a5a} + + + {e7df083d-5b13-46bc-a5b9-610c3ffb33bc} + + + {2ef42e03-cbaa-4077-a7f4-008150037f01} + + + {4d1da71a-dd84-4073-be6d-1e534eca98f3} + + + {acb27adb-45a3-45cf-85f5-3ae00cf3357d} + + + {3a9d8989-ff64-411c-84ad-b7dfb2520d5a} + + + {de5f0642-c9ab-431b-a255-a936076ffed2} + + + {76ac5981-4824-487a-992f-273bfa73fb68} + + + {b1794e73-9397-4e45-8a0d-a4f6dc72c321} + + + {ff6b8d80-d0ed-4225-b56c-1d0a19824e2f} + + + {4d0806f8-ae38-4bac-8469-0a82fc61eecd} + + + {67f51112-db23-4c8a-af1b-f748f7bbce8f} + + + {a47c9da7-bf36-42ae-aedf-c00c071c0582} + + + {017967fb-353e-448b-ae2c-639a182f3ee0} + + + {f4d6c5f9-40d6-4e52-bc03-fef06e9f0221} + + + {122ac1f3-113d-4f91-8676-bbe16e236f4f} + + + {1d28fadf-f748-4616-830b-ec2faa1b5f8e} + + + {bf865c6c-8bf4-4bd6-aaed-ff2a7c92706a} + + + {3eefa342-44e2-493a-9165-40f85bcef557} + + + {f88c0f6a-8051-41e7-9bf6-b9d3c7bb2937} + + + {a6b9803b-8dc2-4552-856e-470f78757533} + + + {21ba77e3-ca31-4dbb-b85d-48ddf892e1da} + + + {06443c48-8447-447b-895f-da725cc13c0c} + + + {ba60dadb-f607-49b7-ab07-0da3a6e06138} + + + {abc41045-2c80-41e8-a8e5-80383e3331b7} + + + {6e66e638-15af-47a6-83de-93bb0cb8ae3d} + + + {b2a3a14e-806c-4ebf-9413-0bbca21b6699} + + + {57a41953-69e1-408c-94ca-5a0fc35bee3d} + + + {afe55d4b-8cbd-4fc0-b4b5-e823d35ac9f6} + + + {ff3c3e8d-02aa-446f-912b-876aad8bb71a} + + + {5ce05bd9-a7f6-47cf-81c3-8c95d3627c5c} + + + {f90e55f2-d904-4421-8284-db37fe80c549} + + + {4c8bf8d5-d6d9-4b6b-96dd-00d64f476027} + + + {90c63e2f-0b47-4aca-a1df-26c436af7c69} + + + {ad3528e0-0c39-42d5-b756-fdf691df5f17} + + + {262a14ae-51b7-4d11-be00-2bf7840dc67d} + + + {40ad6aa5-e972-4aaf-bbb0-c783e72fb341} + + + {d705167f-d99e-49b5-a667-24c0c2fe7bcc} + + + {d52b4de1-b2d7-4c80-afb4-7c6edae1efcb} + + + {61ac299e-6446-4df9-b5cc-9b2c0890b47c} + + + {147837b5-da79-4938-abcf-f8926a72b25c} + + + {34edb787-189e-49c7-8412-f5def16b6f99} + + + {1f029554-0246-45da-8bfd-8d4bc8d4cffc} + + + {15633337-4260-4618-bffa-df945dba2b1a} + + + {81d283e0-15b7-4dcf-a85d-961169a993cd} + + + {dea799c3-4584-461c-a788-9766f61cea56} + + + {619bbb82-dfbc-499e-b078-048ad7e26222} + + + {f5065760-0ad8-4fb3-b6a9-f3ba06be0e51} + + + {360a336e-01e3-4a34-8608-efd2c7c72ef7} + + + {1d9e76bb-7f51-487f-b0b4-de3419fd1925} + + + {177ed754-f97c-4e53-9e75-1f548ae2a0b4} + + + {4b317e13-b7e6-4468-8a2e-bfbbe3bb272b} + + + {acc4e8ae-a1f1-4f2b-9bf2-e12b74fa3a1a} + + + {893769f2-22f7-4c41-ad2b-cb8668fb3b66} + + + {c1441371-f323-4549-90a0-53c6f743b4b1} + + + {b043e348-607a-4ac2-95de-f573db5dd04f} + + + {af98fe8e-ce25-437a-8ab9-efa9d8f0a5b0} + + + {9a61fbe5-f9a2-4c83-b407-5a295808664e} + + + {f55d07b2-80f2-4a01-8fb8-0b09545bf916} + + + {829b148f-b0d9-4a70-87ea-22f57281ac1f} + + + {08832b8f-5370-4c06-95ab-b5b285eb5fc5} + + + {918450ce-de83-4daf-8f25-7aaa8afcb856} + + + {5d807c82-39b9-4651-ab8a-14244deff851} + + + {9dee27ed-5aaf-4fad-b219-faebcebbe450} + + + {22d0b2d5-3279-4144-a23c-8eafb9d90e63} + + + {0061db22-43de-4b54-a161-c43958cdcd7e} + + + + + + Xbox\GameConfig + + + Xbox\GameConfig + + + Xbox\res\audio + + + Xbox\res\audio + + + Xbox\res\audio + + + Xbox\4JLibs\Media + + + Xbox\res + + + Xbox\res + + + Xbox\xexxml + + + Xbox\xexxml + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\DynamicConf + + + + Windows64\GameConfig + + + Windows64\GameConfig + + + Durango + + + Durango + + + Durango + + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + PS3\Miles Sound System\lib\spu + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Durango\Iggy\gdraw + + + Durango\Iggy\gdraw + + + Durango\Iggy\gdraw + + + PS3\Iggy\gdraw + + + PS3\Iggy\gdraw + + + Windows64\Iggy\gdraw + + + Orbis\Iggy\gdraw + + + Orbis\Iggy\gdraw + + + Common\Source Files\Network + + + PSVita\GameConfig + + + PSVita\GameConfig + + + Orbis\4JLibs\libs + + + PSVita\Iggy\gdraw + + + PSVita\Iggy\gdraw + + + + + Header Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + Header Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\player + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client + + + net\minecraft\client\player + + + net\minecraft\client\player + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\client\player + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client\level + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\title + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\4JLibs\inc + + + Xbox\GameConfig + + + Xbox\Source Files + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server\level + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Help & Options + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Controls + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Credits + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Tutorial + + + Xbox\Source Files\XUI\Menu screens\Leaderboards + + + Xbox\Source Files\XUI\Menu screens\Pause + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens\Social + + + Header Files + + + Header Files + + + Header Files + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\XML + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\SentientLibs\inc + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Sentient\DynamicConf + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\XUI\Menu screens\Debug + + + net\minecraft\server\network + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\Sentient + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\XML + + + net\minecraft\server\level + + + net\minecraft\server\network + + + net\minecraft\client + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\multiplayer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\server + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Header Files + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\Social + + + Header Files + + + Windows + + + Windows + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Durango\4JLibs\inc + + + Common\Source Files\Trial + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Durango\Source Files + + + Common\Source Files\Tutorial\Hints + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\Source Files\Sentient + + + Durango\XML + + + Durango\Source Files\Sentient + + + Durango\Source Files\Social + + + Durango + + + Common + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\4JLibs\inc + + + PS3\Source Files\Social + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files\Sentient + + + PS3\Source Files + + + PS3\PS3Extras + + + PS3\PS3Extras + + + Durango + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + PS3 + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\skins + + + net\minecraft\client\particle + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture\custom + + + net\minecraft\client\renderer\texture\custom + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI + + + PS3\PS3Extras + + + PS3\PS3Extras + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\skins + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Header Files + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\GameConfig + + + Windows64\XML + + + Windows64\Source Files + + + Windows64\Source Files\Social + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64\Source Files\Sentient + + + Windows64 + + + Windows64 + + + Durango\Source Files + + + Orbis\OrbisExtras + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\4JLibs\inc + + + Orbis\OrbisExtras + + + Orbis\OrbisExtras + + + Xbox\Source Files\XUI\Base Scene + + + Header Files + + + Common\Source Files\DLC + + + Orbis + + + Orbis\OrbisExtras + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Sentient + + + Orbis\Source Files\Social + + + Orbis\XML + + + Orbis\Source Files + + + Orbis\OrbisExtras + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + Common + + + Common + + + Common + + + Common + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + Windows64\Miles Sound System\Include + + + Windows64\Miles Sound System\Include + + + Orbis\Miles Sound System\include + + + Orbis\Miles Sound System\include + + + Durango\Miles Sound System\include + + + Durango\Miles Sound System\include + + + PS3\Miles Sound System\include + + + PS3\Miles Sound System\include + + + Common\Source Files\Audio + + + Xbox\Source Files\Audio + + + Common\Source Files\Audio + + + PS3\PS3Extras + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\CompressedTile_SPU + + + Common\Source Files\Localisation + + + Common\Source Files\DLC + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules\LevelRules + + + Common\Source Files\DLC + + + PS3 + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\UI + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\include + + + Windows64\Iggy\gdraw + + + Windows64 + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\GameRules\LevelGeneration + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Common\Source Files\DLC + + + Common\Source Files\Colours + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Durango\DurangoExtras + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\include + + + Durango\Iggy\gdraw + + + Durango + + + PS3 + + + PS3\Iggy\gdraw + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + PS3\Iggy\include + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Orbis\Iggy\gdraw + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Orbis\Iggy\include + + + Common\Source Files\Network + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files + + + Common\Source Files\Network + + + Common\Source Files\Network + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\Network + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\Debug + + + Common\Source Files\UI\Components + + + Xbox\Source Files\Network + + + Common\Source Files\Network + + + Xbox\Source Files\Network + + + Orbis + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\BuildVer + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3 + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + PS3\Source Files\Network + + + PS3\Source Files\Leaderboards + + + PS3\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + Xbox\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Windows64\Source Files\Leaderboards + + + Orbis\Source Files\Leaderboards + + + Durango\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Components + + + PS3\4JLibs + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PS3\Source Files + + + Common\Source Files\UI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\tileentity + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Durango\Source Files\Achievements + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\All Platforms + + + Xbox\Source Files\XUI\Containers + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + Orbis + + + Orbis\Source Files + + + Orbis\Network + + + net\minecraft\server\commands + + + net\minecraft\server\commands + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + PS3\Source Files\Network + + + Orbis\Network + + + Common\Source Files\GameRules\LevelGeneration + + + Durango\Network + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Xbox\Source Files\XUI\Menu screens + + + Durango\Network + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\4JLibs\inc + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Sentient + + + PSVita\Source Files\Social + + + PSVita\XML + + + PSVita + + + PSVita\Source Files + + + PSVita\GameConfig + + + Orbis\Network + + + Orbis\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Debug + + + Durango\Source Files + + + Durango\Network + + + Durango\Source Files\Leaderboards + + + Common\Source Files\Telemetry + + + Durango\Source Files\Sentient + + + Durango\ServiceConfig + + + Common\Source Files\UI + + + Common\Source Files\Network\Sony + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\UI\Components + + + Durango\Network + + + Durango\XML + + + PSVita\Iggy\gdraw + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\Iggy\include + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Leaderboards + + + PSVita\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + PSVita\Miles Sound System\Include + + + PSVita\Miles Sound System\Include + + + Durango\Source Files\Leaderboards + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Xbox\4JLibs\inc + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + Orbis\Network + + + Xbox\Source Files\Network + + + Common\Source Files\UI\Scenes + + + + + Source Files + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + net\minecraft\client\renderer\culling + + + Source Files + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\skins + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client + + + net\minecraft\client\player + + + net\minecraft\client\player + + + net\minecraft\client\player + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\client\player + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client + + + net\minecraft\client\level + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui\particle + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\title + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\inventory + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + net\minecraft\client\gui\achievement + + + Source Files + + + Source Files + + + Xbox\Source Files + + + Xbox\Source Files + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server\network + + + net\minecraft\server + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server + + + net\minecraft\server\level + + + net\minecraft\server\level + + + net\minecraft\server\level + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + net\minecraft\client\multiplayer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Help & Options + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Controls + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Credits + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens\Help & Options\How To Play + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Tutorial + + + Xbox\Source Files\XUI\Menu screens\Leaderboards + + + Xbox\Source Files\XUI\Menu screens\Pause + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens\Social + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\XML + + + Xbox\Source Files\Sentient\Telemetry + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Sentient\DynamicConf + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\Font + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\Sentient + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + net\minecraft\server\level + + + net\minecraft\client + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model\geom + + + net\minecraft\client\model\dragon + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model + + + net\minecraft\client\model\geom + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + Xbox\Source Files\Social + + + Source Files + + + Common\Source Files\Trial + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Constraints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Hints + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\Tutorial\Tasks + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration\StructureActions + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial + + + Common\Source Files\Tutorial\Hints + + + PS3\Source Files + + + PS3\PS3Extras + + + Durango + + + Durango\Source Files + + + Common\Source Files + + + Common\Source Files + + + Common\Source Files\GameRules\LevelGeneration + + + PS3 + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Base Scene + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Containers + + + Xbox\Source Files\XUI\Controls + + + net\minecraft\client\skins + + + net\minecraft\client\particle + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture\custom + + + net\minecraft\client\renderer\texture\custom + + + Xbox\Source Files\XUI\Menu screens\Help & Options\Settings + + + PS3\PS3Extras + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\skins + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\renderer\texture + + + net\minecraft\client\skins + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Windows64\Source Files + + + Windows64 + + + Windows64 + + + Durango\Source Files + + + Orbis\OrbisExtras + + + Xbox\Source Files\XUI\Base Scene + + + Common\Source Files\DLC + + + Orbis\OrbisExtras + + + Orbis + + + Orbis\Source Files + + + net\minecraft\client\particle + + + net\minecraft\client\particle + + + Common + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\renderer\entity + + + net\minecraft\client\model + + + net\minecraft\client\model + + + Xbox\Source Files\Audio + + + Common\Source Files\Audio + + + Common\Source Files\Audio + + + PS3\PS3Extras + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\ChunkRebuild_SPU + + + PS3\CompressedTile_SPU + + + Common\Source Files\Localisation + + + Common\Source Files\DLC + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelGeneration + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\Rules + + + Common\Source Files\GameRules\LevelRules + + + Common\Source Files\DLC + + + PS3 + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\GameRules\LevelRules\RuleDefinitions + + + Common\Source Files\UI + + + Windows64\Iggy\gdraw + + + Windows64 + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Common\Source Files\GameRules\LevelGeneration + + + net\minecraft\client\renderer\tileentity + + + net\minecraft\client\model + + + Xbox\Source Files\XUI\Menu screens\Debug + + + Common\Source Files\DLC + + + Common\Source Files\Colours + + + Common\Source Files\DLC + + + Common\Source Files\DLC + + + Durango\DurangoExtras + + + Durango\Iggy\gdraw + + + Durango + + + PS3 + + + PS3\Iggy\gdraw + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + Common\Source Files\zlib + + + PS3\Source Files\Audio + + + Common\Source Files\UI + + + Common\Source Files\UI + + + Orbis\Iggy\gdraw + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files + + + Common\Source Files\Network + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\Debug + + + Common\Source Files\UI\Components + + + Xbox\Source Files\Network + + + Common\Source Files\Network + + + Xbox\Source Files\Network + + + Orbis + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + PS3\Source Files\Network + + + PS3\Source Files\Leaderboards + + + PS3\Source Files\Leaderboards + + + Common\Source Files\Leaderboards + + + Xbox\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Windows64\Source Files\Leaderboards + + + Orbis\Source Files\Leaderboards + + + Durango\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PS3\PS3Extras + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Scenes\Help & Options + + + Common\Source Files\UI\Components + + + PS3\4JLibs + + + Common\Source Files\UI\Components + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Common\Source Files\Audio + + + PS3\Source Files + + + Common\Source Files\UI\Controls + + + Xbox\Source Files\XUI\Menu screens + + + net\minecraft\client\renderer\tileentity + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Durango\Source Files\Achievements + + + Common\Source Files\UI\Scenes\Debug + + + Xbox\Source Files\XUI\Containers + + + Common\Source Files\UI\All Platforms + + + Common\Source Files\UI\Scenes\In-Game Menu Screens\Containers + + + Xbox\Source Files\XUI\Containers + + + net\minecraft\client\model + + + net\minecraft\client\renderer\entity + + + Orbis + + + Orbis\Network + + + net\minecraft\server\commands + + + net\minecraft\server\commands + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + Orbis\Network + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + PS3\Source Files\Network + + + PS3\Source Files\Network + + + Orbis\Network + + + Common\Source Files\GameRules\LevelGeneration + + + Durango\Network + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + Xbox\Source Files\XUI\Menu screens + + + Durango\Network + + + PSVita + + + PSVita + + + PSVita\Source Files + + + PSVita\Source Files + + + PSVita\PSVitaExtras + + + Orbis\Network + + + Common\Source Files\Network\Sony + + + Orbis\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Debug + + + Durango\Network + + + Durango\Source Files\Leaderboards + + + Common\Source Files\Telemetry + + + Durango\Source Files\Sentient + + + Common\Source Files\UI + + + Orbis\Network + + + PS3\Source Files\Network + + + Common\Source Files\Network\Sony + + + Orbis + + + Orbis + + + Orbis + + + Common\Source Files\UI\Components + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\Network + + + Durango\XML + + + PSVita\Iggy\gdraw + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\Source Files\Leaderboards + + + PSVita\Source Files\Leaderboards + + + Common\Source Files\UI\Scenes\Frontend Menu screens + + + Durango\Source Files\Leaderboards + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + PSVita\PSVitaExtras + + + PSVita\PSVitaExtras + + + Common\Source Files\UI\Controls + + + Common\Source Files\UI\Scenes\In-Game Menu Screens + + + PSVita\Source Files\Network + + + PSVita\Source Files\Network + + + Orbis\Network + + + Common\Source Files\UI\Scenes + + + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Durango\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\Miles Sound System\lib + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + Durango\Iggy\lib + + + PS3\Iggy\lib + + + PS3\Iggy\lib + + + PS3\Iggy\lib + + + Orbis\Iggy\lib + + + Orbis\Iggy\lib + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + Durango\Miles Sound System\lib + + + Durango\Miles Sound System\lib + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + PS3\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Orbis\4JLibs\libs + + + Durango\4JLibs\libs + + + Durango\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\Iggy\Lib + + + PSVita\Iggy\Lib + + + PSVita\Miles Sound System\lib + + + PSVita\Miles Sound System\lib + + + PSVita\Miles Sound System\lib + + + PSVita\Miles Sound System\lib + + + Xbox\4JLibs\libs + + + Xbox\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + PSVita\4JLibs\libs + + + + + Xbox\SentientLibs + + + + + Windows + + + Windows + + + Durango + + + + + Windows + + + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\ContentPackage + + + PS3\SPUObjFiles\Release + + + PS3\SPUObjFiles\Debug + + + PS3\SPUObjFiles\ContentPackage + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp new file mode 100644 index 0000000..8c6da75 --- /dev/null +++ b/Minecraft.Client/Minecraft.cpp @@ -0,0 +1,4983 @@ +#include "stdafx.h" +#include "Minecraft.h" +#include "GameMode.h" +#include "Timer.h" +#include "ProgressRenderer.h" +#include "LevelRenderer.h" +#include "ParticleEngine.h" +#include "MultiPlayerLocalPlayer.h" +#include "User.h" +#include "Textures.h" +#include "GameRenderer.h" +#include "HumanoidModel.h" +#include "Options.h" +#include "TexturePackRepository.h" +#include "StatsCounter.h" +#include "EntityRenderDispatcher.h" +#include "TileEntityRenderDispatcher.h" +#include "SurvivalMode.h" +#include "Chunk.h" +#include "CreativeMode.h" +#include "DemoLevel.h" +#include "MultiPlayerLevel.h" +#include "MultiPlayerLocalPlayer.h" +#include "DemoUser.h" +#include "GuiParticles.h" +#include "Screen.h" +#include "DeathScreen.h" +#include "ErrorScreen.h" +#include "TitleScreen.h" +#include "InventoryScreen.h" +#include "InBedChatScreen.h" +#include "AchievementPopup.h" +#include "Input.h" +#include "FrustumCuller.h" +#include "Camera.h" +#ifdef _WINDOWS64 +#include "KeyboardMouseInput.h" +#endif + +#include "..\Minecraft.World\MobEffect.h" +#include "..\Minecraft.World\Difficulty.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" +#include "..\Minecraft.World\net.minecraft.world.phys.h" +#include "..\Minecraft.World\File.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\Minecraft.World\net.minecraft.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\ByteBuffer.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\Minecraft.World.h" +#include "ClientConnection.h" +#include "..\Minecraft.World\HellRandomLevelSource.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "..\Minecraft.World\net.minecraft.world.entity.monster.h" +#include "..\Minecraft.World\StrongholdFeature.h" +#include "..\Minecraft.World\IntCache.h" +#include "..\Minecraft.World\Villager.h" +#include "..\Minecraft.World\SparseLightStorage.h" +#include "..\Minecraft.World\SparseDataStorage.h" +#include "TextureManager.h" +#ifdef _XBOX +#include "Xbox\Network\NetworkPlayerXbox.h" +#endif +#include "Common\UI\IUIScene_CreativeMenu.h" +#include "Common\UI\UIFontData.h" +#include "DLCTexturePack.h" + +#ifdef __ORBIS__ +#include "Orbis\Network\PsPlusUpsellWrapper_Orbis.h" +#endif + +// 4J Turning this on will change the graph at the bottom of the debug overlay to show the number of packets of each type added per fram +//#define DEBUG_RENDER_SHOWS_PACKETS 1 +//#define SPLITSCREEN_TEST + +// If not disabled, this creates an event queue on a seperate thread so that the Level::tick calls can be offloaded +// from the main thread, and have longer to run, since it's called at 20Hz instead of 60 +#define DISABLE_LEVELTICK_THREAD + +Minecraft *Minecraft::m_instance = NULL; +__int64 Minecraft::frameTimes[512]; +__int64 Minecraft::tickTimes[512]; +int Minecraft::frameTimePos = 0; +__int64 Minecraft::warezTime = 0; +File Minecraft::workDir = File(L""); + +#ifdef __PSVITA__ + +TOUCHSCREENRECT QuickSelectRect[3]= +{ + { 560, 890, 1360, 980 }, + { 450, 840, 1449, 960 }, + { 320, 840, 1600, 970 }, +}; + +int QuickSelectBoxWidth[3]= +{ + 89, + 111, + 142 +}; +#endif + +Minecraft::Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet *minecraftApplet, int width, int height, bool fullscreen) +{ + // 4J - added this block of initialisers + gameMode = NULL; + hasCrashed = false; + timer = new Timer(SharedConstants::TICKS_PER_SECOND); + oldLevel = NULL; //4J Stu added + level = NULL; + levels = MultiPlayerLevelArray(3); // 4J Added + levelRenderer = NULL; + player = nullptr; + cameraTargetPlayer = nullptr; + particleEngine = NULL; + user = NULL; + parent = NULL; + pause = false; + textures = NULL; + font = NULL; + screen = NULL; + localPlayerIdx = 0; + rightClickDelay = 0; + + // 4J Stu Added + InitializeCriticalSection( &ProgressRenderer::s_progress ); + InitializeCriticalSection(&m_setLevelCS); + //m_hPlayerRespawned = CreateEvent(NULL, FALSE, FALSE, NULL); + + progressRenderer = NULL; + gameRenderer = NULL; + bgLoader = NULL; + + ticks = 0; + // 4J-PB - moved into the local player + //missTime = 0; + //lastClickTick = 0; + //isRaining = false; + // 4J-PB - end + + orgWidth = orgHeight = 0; + achievementPopup = new AchievementPopup(this); + gui = NULL; + noRender = false; + humanoidModel = new HumanoidModel(0); + hitResult = 0; + options = NULL; + soundEngine = new SoundEngine(); + mouseHandler = NULL; + skins = NULL; + workingDirectory = File(L""); + levelSource = NULL; + stats[0] = NULL; + stats[1] = NULL; + stats[2] = NULL; + stats[3] = NULL; + connectToPort = 0; + workDir = File(L""); + // 4J removed + //wasDown = false; + lastTimer = -1; + + // 4J removed + //lastTickTime = System::currentTimeMillis(); + recheckPlayerIn = 0; + running = true; + unoccupiedQuadrant = -1; + + Stats::init(); + + orgHeight = height; + this->fullscreen = fullscreen; + this->minecraftApplet = NULL; + + this->parent = parent; + // 4J - Our actual physical frame buffer is always 1280x720 ie in a 16:9 ratio. If we want to do a 4:3 mode, we are telling the original minecraft code + // that the width is 3/4 what it actually is, to correctly present a 4:3 image. Have added width_phys and height_phys for any code we add that requires + // to know the real physical dimensions of the frame buffer. + if( RenderManager.IsWidescreen() ) + { + this->width = width; + } + else + { + this->width = (width * 3 ) / 4; + } + this->height = height; + this->width_phys = width; + this->height_phys = height; + + this->fullscreen = fullscreen; + + appletMode = false; + + Minecraft::m_instance = this; + TextureManager::createInstance(); + + for(int i=0;isoundEngine->init(NULL); +#endif + +#ifndef DISABLE_LEVELTICK_THREAD + levelTickEventQueue = new C4JThread::EventQueue(levelTickUpdateFunc, levelTickThreadInitFunc, "LevelTick_EventQueuePoll"); + levelTickEventQueue->setProcessor(3); + levelTickEventQueue->setPriority(THREAD_PRIORITY_NORMAL); +#endif // DISABLE_LEVELTICK_THREAD +} + +void Minecraft::clearConnectionFailed() +{ + for(int i=0;iaddDebugPacks(); + textures = new Textures(skins, options); + //renderLoadingScreen(); + + font = new Font(options, L"font/Default.png", textures, false, TN_DEFAULT_FONT, 23, 20, 8, 8, SFontData::Codepoints); + altFont = new Font(options, L"font/alternate.png", textures, false, TN_ALT_FONT, 16, 16, 8, 8); + + //if (options.languageCode != null) { + // Language.getInstance().loadLanguage(options.languageCode); + // // font.setEnforceUnicodeSheet("true".equalsIgnoreCase(I18n.get("language.enforceUnicode"))); + // font.setEnforceUnicodeSheet(Language.getInstance().isSelectedLanguageIsUnicode()); + // font.setBidirectional(Language.isBidirectional(options.languageCode)); + //} + + // 4J Stu - Not using these any more + //WaterColor::init(textures->loadTexturePixels(L"misc/watercolor.png")); + //GrassColor::init(textures->loadTexturePixels(L"misc/grasscolor.png")); + //FoliageColor::init(textures->loadTexturePixels(L"misc/foliagecolor.png")); + + gameRenderer = new GameRenderer(this); + EntityRenderDispatcher::instance->itemInHandRenderer = new ItemInHandRenderer(this,false); + + for( int i=0 ; i<4 ; ++i ) + stats[i] = new StatsCounter(); + + /* 4J - TODO, 4J-JEV: Unnecessary. + Achievements::openInventory->setDescFormatter(NULL); + Achievements.openInventory.setDescFormatter(new DescFormatter(){ + public String format(String i18nValue) { + return String.format(i18nValue, Keyboard.getKeyName(options.keyBuild.key)); + } + }); + */ + + // 4J-PB - We'll do this in a xui intro + //renderLoadingScreen(); + + //Keyboard::create(); + Mouse::create(); +#if 0 // 4J - removed + mouseHandler = new MouseHandler(parent); + try { + Controllers.create(); + } catch (Exception e) { + e.printStackTrace(); + } +#endif + + MemSect(31); + checkGlError(L"Pre startup"); + MemSect(0); + + // width = Display.getDisplayMode().getWidth(); + // height = Display.getDisplayMode().getHeight(); + + glEnable(GL_TEXTURE_2D); + glShadeModel(GL_SMOOTH); + glClearDepth(1.0); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glCullFace(GL_BACK); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + MemSect(31); + checkGlError(L"Startup"); + MemSect(0); + + // openGLCapabilities = new OpenGLCapabilities(); // 4J - removed + + levelRenderer = new LevelRenderer(this, textures); + textures->stitch(); + + glViewport(0, 0, width, height); + + particleEngine = new ParticleEngine(level, textures); + // try { // 4J - removed try/catch + bgLoader = new BackgroundDownloader(workingDirectory, this); + bgLoader->start(); + // } catch (Exception e) { + // } + + MemSect(31); + checkGlError(L"Post startup"); + MemSect(0); + gui = new Gui(this); + + if (connectToIp != L"") // 4J - was NULL comparison + { + // setScreen(new ConnectScreen(this, connectToIp, connectToPort)); // 4J TODO - put back in + } + else + { + setScreen(new TitleScreen()); + } + progressRenderer = new ProgressRenderer(this); + + RenderManager.CBuffLockStaticCreations(); +} + +void Minecraft::renderLoadingScreen() +{ + // 4J Unused + // testing stuff on vita just now +#ifdef __PSVITA__ + ScreenSizeCalculator ssc(options, width, height); + + // xxx + RenderManager.StartFrame(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, (float)ssc.rawWidth, (float)ssc.rawHeight, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + glViewport(0, 0, width, height); + glClearColor(0, 0, 0, 0); + + Tesselator *t = Tesselator::getInstance(); + + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glDisable(GL_FOG); + // xxx + glBindTexture(GL_TEXTURE_2D, textures->loadTexture(TN_MOB_PIG)); + t->begin(); + t->color(0xffffff); + t->vertexUV((float)(0), (float)( height), (float)( 0), (float)( 0), (float)( 0)); + t->vertexUV((float)(width), (float)( height), (float)( 0), (float)( 0), (float)( 0)); + t->vertexUV((float)(width), (float)( 0), (float)( 0), (float)( 0), (float)( 0)); + t->vertexUV((float)(0), (float)( 0), (float)( 0), (float)( 0), (float)( 0)); + t->end(); + + int lw = 256; + int lh = 256; + glColor4f(1, 1, 1, 1); + t->color(0xffffff); + blit((ssc.getWidth() - lw) / 2, (ssc.getHeight() - lh) / 2, 0, 0, lw, lh); + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + + Display::swapBuffers(); + // xxx + RenderManager.Present(); +#endif +} + +void Minecraft::blit(int x, int y, int sx, int sy, int w, int h) +{ + float us = 1 / 256.0f; + float vs = 1 / 256.0f; + Tesselator *t = Tesselator::getInstance(); + t->begin(); + t->vertexUV((float)(x + 0), (float)( y + h), (float)( 0), (float)( (sx + 0) * us), (float)( (sy + h) * vs)); + t->vertexUV((float)(x + w), (float)( y + h), (float)( 0), (float)( (sx + w) * us), (float)( (sy + h) * vs)); + t->vertexUV((float)(x + w), (float)( y + 0), (float)( 0), (float)( (sx + w) * us), (float)( (sy + 0) * vs)); + t->vertexUV((float)(x + 0), (float)( y + 0), (float)( 0), (float)( (sx + 0) * us), (float)( (sy + 0) * vs)); + t->end(); +} + +File Minecraft::getWorkingDirectory() +{ + if (workDir.getPath().empty()) workDir = getWorkingDirectory(L"minecraft"); + return workDir; +} + +File Minecraft::getWorkingDirectory(const wstring& applicationName) +{ +#if 0 + // 4J - original version + final String userHome = System.getProperty("user.home", "."); + final File workingDirectory; + switch (getPlatform()) { + case linux: + case solaris: + workingDirectory = new File(userHome, '.' + applicationName + '/'); + break; + case windows: + final String applicationData = System.getenv("APPDATA"); + if (applicationData != null) workingDirectory = new File(applicationData, "." + applicationName + '/'); + else workingDirectory = new File(userHome, '.' + applicationName + '/'); + break; + case macos: + workingDirectory = new File(userHome, "Library/Application Support/" + applicationName); + break; + default: + workingDirectory = new File(userHome, applicationName + '/'); + } + if (!workingDirectory.exists()) if (!workingDirectory.mkdirs()) throw new RuntimeException("The working directory could not be created: " + workingDirectory); + return workingDirectory; +#else + wstring userHome = L"home"; // 4J - TODO + File workingDirectory(userHome, applicationName); + // 4J Removed + //if (!workingDirectory.exists()) + //{ + // workingDirectory.mkdirs(); + //} + return workingDirectory; +#endif +} + +Minecraft::OS Minecraft::getPlatform() +{ + return xbox; +} + +LevelStorageSource *Minecraft::getLevelSource() +{ + return levelSource; +} + +void Minecraft::setScreen(Screen *screen) +{ + if( dynamic_cast(this->screen) != NULL ) return; + + if (this->screen != NULL) + { + this->screen->removed(); + } + +#ifdef _WINDOWS64 + if (screen != NULL && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + + //4J Gordon: Do not force a stats save here + /*if (dynamic_cast(screen)!=NULL) + { + stats->forceSend(); + } + stats->forceSave();*/ + + if (screen == NULL && level == NULL) + { + screen = new TitleScreen(); + } + else if (player != NULL && !ui.GetMenuDisplayed(player->GetXboxPad()) && player->getHealth() <= 0) + { + //screen = new DeathScreen(); + + // 4J Stu - If we exit from the death screen then we are saved as being dead. In the Java + // game when you load the game you are still dead, but this is silly so only show the dead + // screen if we have died during gameplay + if(ticks==0) + { + player->respawn(); + } + else + { + ui.NavigateToScene(player->GetXboxPad(),eUIScene_DeathMenu,NULL); + } + } + + if (dynamic_cast(screen)!=NULL) + { + options->renderDebug = false; + gui->clearMessages(); + } + + this->screen = screen; + if (screen != NULL) + { + // releaseMouse(); // 4J - removed + ScreenSizeCalculator ssc(options, width, height); + int screenWidth = ssc.getWidth(); + int screenHeight = ssc.getHeight(); + screen->init(this, screenWidth, screenHeight); + noRender = false; + } + else + { + // grabMouse(); // 4J - removed + } + + // 4J-PB - if a screen has been set, go into menu mode + // it's possible that player doesn't exist here yet + /*if(screen!=NULL) + { + if(player && player->GetXboxPad()!=-1) + { + InputManager.SetMenuDisplayed(player->GetXboxPad(),true); + } + else + { + // set all + //InputManager.SetMenuDisplayed(XUSER_INDEX_ANY,true); + } + } + else + { + if(player && player->GetXboxPad()!=-1) + { + InputManager.SetMenuDisplayed(player->GetXboxPad(),false); + } + else + { + //InputManager.SetMenuDisplayed(XUSER_INDEX_ANY,false); + } + }*/ +} + +void Minecraft::checkGlError(const wstring& string) +{ + // 4J - TODO +} + +void Minecraft::destroy() +{ + //4J Gordon: Do not force a stats save here + /*stats->forceSend(); + stats->forceSave();*/ + + // 4J - all try/catch/finally things in here removed + // try { + if (this->bgLoader != NULL) + { + bgLoader->halt(); + } + // } catch (Exception e) { + // } + + // try { + setLevel(NULL); + // } catch (Throwable e) { + // } + + // try { + MemoryTracker::release(); + // } catch (Throwable e) { + // } + + soundEngine->destroy(); + Mouse::destroy(); + Keyboard::destroy(); + //} finally { + Display::destroy(); + // if (!hasCrashed) System.exit(0); //4J - removed + //} + //System.gc(); // 4J - removed +} + +// 4J-PB - splitting this function into 3 parts, so we can call the middle part from our xbox game loop + +#if 0 +void Minecraft::run() +{ + running = true; + // try { // 4J - removed try/catch + init(); + // } catch (Exception e) { + // e.printStackTrace(); + // crash(new CrashReport("Failed to start game", e)); + // return; + // } + // try { // 4J - removed try/catch + if (Minecraft::FLYBY_MODE) + { + generateFlyby(); + return; + } + + __int64 lastTime = System::currentTimeMillis(); + int frames = 0; + + while (running) + { + // try { // 4J - removed try/catch + // if (minecraftApplet != null && !minecraftApplet.isActive()) break; // 4J - removed + AABB::resetPool(); + Vec3::resetPool(); + + // if (parent == NULL && Display.isCloseRequested()) { // 4J - removed + // stop(); + // } + + if (pause && level != NULL) + { + float lastA = timer->a; + timer->advanceTime(); + timer->a = lastA; + } + else + { + timer->advanceTime(); + } + + __int64 beforeTickTime = System::nanoTime(); + for (int i = 0; i < timer->ticks; i++) + { + ticks++; + // try { // 4J - try/catch removed + tick(); + // } catch (LevelConflictException e) { + // this.level = null; + // setLevel(null); + // setScreen(new LevelConflictScreen()); + // } + } + __int64 tickDuraction = System::nanoTime() - beforeTickTime; + checkGlError(L"Pre render"); + + TileRenderer::fancy = options->fancyGraphics; + + // if (pause) timer.a = 1; + + soundEngine->update(player, timer->a); + + glEnable(GL_TEXTURE_2D); + if (level != NULL) level->updateLights(); + + // if (!Keyboard::isKeyDown(Keyboard.KEY_F7)) Display.update(); // 4J - removed + + if (player != NULL && player->isInWall()) options->thirdPersonView = false; + if (!noRender) + { + if (gameMode != NULL) gameMode->render(timer->a); + gameRenderer->render(timer->a); + } + + /* 4J - removed + if (!Display::isActive()) + { + if (fullscreen) + { + this->toggleFullScreen(); + } + Sleep(10); + } + */ + + if (options->renderDebug) + { + renderFpsMeter(tickDuraction); + } + else + { + lastTimer = System::nanoTime(); + } + + achievementPopup->render(); + + Sleep(0); // 4J - was Thread.yield() + + // if (Keyboard::isKeyDown(Keyboard::KEY_F7)) Display.update(); // 4J - removed condition + Display::update(); + + // checkScreenshot(); // 4J - removed + + /* 4J - removed + if (parent != NULL && !fullscreen) + { + if (parent.getWidth() != width || parent.getHeight() != height) + { + width = parent.getWidth(); + height = parent.getHeight(); + if (width <= 0) width = 1; + if (height <= 0) height = 1; + + resize(width, height); + } + } + */ + checkGlError(L"Post render"); + frames++; + pause = !isClientSide() && screen != NULL && screen->isPauseScreen(); + + while (System::currentTimeMillis() >= lastTime + 1000) + { + fpsString = _toString(frames) + L" fps, " + _toString(Chunk::updates) + L" chunk updates"; + Chunk::updates = 0; + lastTime += 1000; + frames = 0; + } + /* + } catch (LevelConflictException e) { + this.level = null; + setLevel(null); + setScreen(new LevelConflictScreen()); + } catch (OutOfMemoryError e) { + emergencySave(); + setScreen(new OutOfMemoryScreen()); + System.gc(); + } + */ + } + /* + } catch (StopGameException e) { + } catch (Throwable e) { + emergencySave(); + e.printStackTrace(); + crash(new CrashReport("Unexpected error", e)); + } finally { + destroy(); + } + */ + destroy(); +} +#endif + +void Minecraft::run() +{ + running = true; + // try { // 4J - removed try/catch + init(); + // } catch (Exception e) { + // e.printStackTrace(); + // crash(new CrashReport("Failed to start game", e)); + // return; + // } + // try { // 4J - removed try/catch + } + +// 4J added - Selects which local player is currently active for processing by the existing minecraft code +bool Minecraft::setLocalPlayerIdx(int idx) +{ + localPlayerIdx = idx; + // If the player is not null, but the game mode is then this is just a temp player + // whose only real purpose is to hold the viewport position + if( localplayers[idx] == NULL || localgameModes[idx] == NULL ) return false; + + gameMode = localgameModes[idx]; + player = localplayers[idx]; + cameraTargetPlayer = localplayers[idx]; + gameRenderer->itemInHandRenderer = localitemInHandRenderers[idx]; + level = getLevel( localplayers[idx]->dimension ); + particleEngine->setLevel( level ); + + return true; +} + +int Minecraft::getLocalPlayerIdx() +{ + return localPlayerIdx; +} + +void Minecraft::updatePlayerViewportAssignments() +{ + unoccupiedQuadrant = -1; + // Find out how many viewports we'll be needing + int viewportsRequired = 0; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] != NULL ) viewportsRequired++; + } + if( viewportsRequired == 3 ) viewportsRequired = 4; + + // Allocate away... + if( viewportsRequired == 1 ) + { + // Single viewport + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] != NULL ) localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN; + } + } + else if( viewportsRequired == 2 ) + { + // Split screen - TODO - option for vertical/horizontal split + int found = 0; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] != NULL ) + { + // Primary player settings decide what the mode is + if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_SplitScreenVertical)) + { + localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_SPLIT_LEFT + found; + } + else + { + localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_SPLIT_TOP + found; + } + found++; + } + } + } + else if( viewportsRequired >= 3 ) + { + // Quadrants - this is slightly more complicated. We don't want to move viewports around if we are going from 3 to 4, or 4 to 3 players, + // so persist any allocations for quadrants that already exist. + bool quadrantsAllocated[4] = {false,false,false,false}; + + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] != NULL ) + { + + // 4J Stu - If the game hasn't started, ignore current allocations (as the players won't have seen them) + // This fixes an issue with the primary player being the 4th controller quadrant, but ending up in the 3rd viewport. + if(app.GetGameStarted()) + { + if( ( localplayers[i]->m_iScreenSection >= C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT ) && + ( localplayers[i]->m_iScreenSection <= C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT ) ) + { + quadrantsAllocated[localplayers[i]->m_iScreenSection - C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT] = true; + } + } + else + { + // Reset the viewport so that it can be assigned in the next loop + localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN; + } + } + } + + // Found which quadrants are currently in use, now allocate out any spares that are required + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] != NULL ) + { + if( ( localplayers[i]->m_iScreenSection < C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT ) || + ( localplayers[i]->m_iScreenSection > C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT ) ) + { + for( int j = 0; j < 4; j++ ) + { + if( !quadrantsAllocated[j] ) + { + localplayers[i]->m_iScreenSection = C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT + j; + quadrantsAllocated[j] = true; + break; + } + } + } + } + } + // If there's an unoccupied quadrant, record which one so we can clear it to black when rendering + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( quadrantsAllocated[i] == false ) + { + unoccupiedQuadrant = i; + } + } + } + + // 4J Stu - If the game is not running we do not want to do this yet, and should wait until the task + // that caused the app to not be running is finished + if(app.GetGameStarted())ui.UpdatePlayerBasePositions(); +} + +// Add a temporary player so that the viewports get re-arranged, and add the player to the game session +bool Minecraft::addLocalPlayer(int idx) +{ + //int iLocalPlayerC=app.GetLocalPlayerCount(); + if( m_pendingLocalConnections[idx] != NULL ) + { + // 4J Stu - Should we ever be in a state where this happens? + assert(false); + m_pendingLocalConnections[idx]->close(); + } + m_connectionFailed[idx] = false; + m_pendingLocalConnections[idx] = NULL; + + bool success=g_NetworkManager.AddLocalPlayerByUserIndex(idx); + + if(success) + { + app.DebugPrintf("Adding temp local player on pad %d\n", idx); + localplayers[idx] = shared_ptr( new MultiplayerLocalPlayer(this, level, user, NULL ) ); + localgameModes[idx] = NULL; + + updatePlayerViewportAssignments(); + +#ifdef _XBOX + // tell the xui scenes a splitscreen player joined + XUIMessage xuiMsg; + CustomMessage_Splitscreenplayer_Struct myMsgData; + CustomMessage_Splitscreenplayer( &xuiMsg, &myMsgData, true); + + // send the message + for(int i=0;iiPad = idx; + param->stringId = IDS_PROGRESS_CONNECTING; + param->showTooltips = true; + param->setFailTimer = true; + param->timerTime = CONNECTING_PROGRESS_CHECK_TIME; + + // Joining as second player so always the small progress + ui.NavigateToScene(idx, eUIScene_ConnectingProgress, param); + + } + else + { + app.DebugPrintf("g_NetworkManager.AddLocalPlayerByUserIndex failed\n"); +#ifdef _DURANGO + ProfileManager.RemoveGamepadFromGame(idx); +#endif + } + + return success; +} + +void Minecraft::addPendingLocalConnection(int idx, ClientConnection *connection) +{ + m_pendingLocalConnections[idx] = connection; +} + +shared_ptr Minecraft::createExtraLocalPlayer(int idx, const wstring& name, int iPad, int iDimension, ClientConnection *clientConnection /*= NULL*/,MultiPlayerLevel *levelpassedin) +{ + if( clientConnection == NULL) return nullptr; + + if( clientConnection == m_pendingLocalConnections[idx] ) + { + int tempScreenSection = C4JRender::VIEWPORT_TYPE_FULLSCREEN; + if( localplayers[idx] != NULL && localgameModes[idx] == NULL ) + { + // A temp player displaying a connecting screen + tempScreenSection = localplayers[idx]->m_iScreenSection; + } + wstring prevname = user->name; + user->name = name; + + // Don't need this any more + m_pendingLocalConnections[idx] = NULL; + + // Add the connection to the level which will now take responsibility for ticking it + // 4J-PB - can't use the dimension from localplayers[idx], since there may be no localplayers at this point + //MultiPlayerLevel *mpLevel = (MultiPlayerLevel *)getLevel( localplayers[idx]->dimension ); + + MultiPlayerLevel *mpLevel; + + if(levelpassedin) + { + level=levelpassedin; + mpLevel=levelpassedin; + } + else + { + level=getLevel( iDimension ); + mpLevel = getLevel( iDimension ); + mpLevel->addClientConnection( clientConnection ); + } + + if( app.GetTutorialMode() ) + { + localgameModes[idx] = new FullTutorialMode(idx, this, clientConnection); + } + // check if we're in the trial version + else if(ProfileManager.IsFullVersion()==false) + { + localgameModes[idx] = new TrialMode(idx, this, clientConnection); + } + else + { + localgameModes[idx] = new ConsoleGameMode(idx, this, clientConnection); + } + + // 4J-PB - can't do this here because they use a render context, but this is running from a thread. + // Moved the creation of these into the main thread, before level launch + //localitemInHandRenderers[idx] = new ItemInHandRenderer(this); + localplayers[idx] = localgameModes[idx]->createPlayer(level); + + PlayerUID playerXUIDOffline = INVALID_XUID; + PlayerUID playerXUIDOnline = INVALID_XUID; + ProfileManager.GetXUID(idx,&playerXUIDOffline,false); + ProfileManager.GetXUID(idx,&playerXUIDOnline,true); + localplayers[idx]->setXuid(playerXUIDOffline); + localplayers[idx]->setOnlineXuid(playerXUIDOnline); + localplayers[idx]->setIsGuest(ProfileManager.IsGuest(idx)); + + localplayers[idx]->displayName = ProfileManager.GetDisplayName(idx); + + localplayers[idx]->m_iScreenSection = tempScreenSection; + + if( levelpassedin == NULL) level->addEntity(localplayers[idx]); // Don't add if we're passing the level in, we only do this from the client connection & we'll be handling adding it ourselves + + localplayers[idx]->SetXboxPad(iPad); + + if( localplayers[idx]->input != NULL ) delete localplayers[idx]->input; + localplayers[idx]->input = new Input(); + + localplayers[idx]->resetPos(); + + levelRenderer->setLevel(idx, level); + localplayers[idx]->level = level; + + user->name = prevname; + + updatePlayerViewportAssignments(); + + // Fix for #105852 - TU12: Content: Gameplay: Local splitscreen Players are spawned at incorrect places after re-joining previously saved and loaded "Mass Effect World". + // Move this check to ClientConnection::handleMovePlayer +// // 4J-PB - can't call this when this function is called from the qnet thread (GetGameStarted will be false) +// if(app.GetGameStarted()) +// { +// ui.CloseUIScenes(idx); +// } + } + + return localplayers[idx]; +} + +// on a respawn of the local player, just store them +void Minecraft::storeExtraLocalPlayer(int idx) +{ + localplayers[idx] = player; + + if( localplayers[idx]->input != NULL ) delete localplayers[idx]->input; + localplayers[idx]->input = new Input(); + + if(ProfileManager.IsSignedIn(idx)) + { + localplayers[idx]->name = convStringToWstring( ProfileManager.GetGamertag(idx) ); + } +} + +void Minecraft::removeLocalPlayerIdx(int idx) +{ + bool updateXui = true; + if(localgameModes[idx] != NULL) + { + if( getLevel( localplayers[idx]->dimension )->isClientSide ) + { + shared_ptr mplp = localplayers[idx]; + ( (MultiPlayerLevel *)getLevel( localplayers[idx]->dimension ) )->removeClientConnection(mplp->connection, true); + delete mplp->connection; + mplp->connection = NULL; + g_NetworkManager.RemoveLocalPlayerByUserIndex(idx); + } + getLevel( localplayers[idx]->dimension )->removeEntity(localplayers[idx]); + +#ifdef _XBOX + // 4J Stu - Fix for #12368 - Crash: Game crashes when saving then exiting and selecting to save + app.TutorialSceneNavigateBack(idx); +#endif + + // 4J Stu - Fix for #13257 - CRASH: Gameplay: Title crashed after exiting the tutorial + // It doesn't matter if they were in the tutorial already + playerLeftTutorial( idx ); + + delete localgameModes[idx]; + localgameModes[idx] = NULL; + } + else if( m_pendingLocalConnections[idx] != NULL ) + { + m_pendingLocalConnections[idx]->sendAndDisconnect( shared_ptr( new DisconnectPacket(DisconnectPacket::eDisconnect_Quitting) ) );; + delete m_pendingLocalConnections[idx]; + m_pendingLocalConnections[idx] = NULL; + g_NetworkManager.RemoveLocalPlayerByUserIndex(idx); + } + else + { + // Not sure how this works on qnet, but for other platforms, calling RemoveLocalPlayerByUserIndex won't do anything if there isn't a local user to remove + // Now just updating the UI directly in this case +#ifdef _XBOX + // 4J Stu - A signout early in the game creation before this player has connected to the game server + updateXui = false; +#endif + // 4J Stu - Adding this back in for exactly the reason my comment above suggests it was added in the first place +#ifdef _XBOX_ONE + g_NetworkManager.RemoveLocalPlayerByUserIndex(idx); +#endif + } + localplayers[idx] = nullptr; + + if( idx == ProfileManager.GetPrimaryPad() ) + { + // We should never try to remove the Primary player in this way + assert(false); + /* + // If we are removing the primary player then there can't be a valid gamemode left anymore, this + // pointer will be referring to the one we've just deleted + gameMode = NULL; + // Remove references to player + player = NULL; + cameraTargetPlayer = NULL; + EntityRenderDispatcher::instance->cameraEntity = NULL; + TileEntityRenderDispatcher::instance->cameraEntity = NULL; + */ + } + else if( updateXui ) + { + gameRenderer->DisableUpdateThread(); + levelRenderer->setLevel(idx, NULL); + gameRenderer->EnableUpdateThread(); + ui.CloseUIScenes(idx,true); + updatePlayerViewportAssignments(); + } + + // We only create these once ever so don't delete it here + //delete localitemInHandRenderers[idx]; +} + +void Minecraft::createPrimaryLocalPlayer(int iPad) +{ + localgameModes[iPad] = gameMode; + localplayers[iPad] = player; + //gameRenderer->itemInHandRenderer = localitemInHandRenderers[iPad]; + // Give them the gamertag if they're signed in + if(ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) + { + user->name = convStringToWstring( ProfileManager.GetGamertag(ProfileManager.GetPrimaryPad()) ); + } +} + +void Minecraft::run_middle() +{ + static __int64 lastTime = 0; + static bool bFirstTimeIntoGame = true; + static bool bAutosaveTimerSet=false; + static unsigned int uiAutosaveTimer=0; + static int iFirstTimeCountdown=60; + if( lastTime == 0 ) lastTime = System::nanoTime(); + static int frames = 0; + + EnterCriticalSection(&m_setLevelCS); + + if(running) + { + if (reloadTextures) + { + reloadTextures = false; + textures->reloadAll(); + } + + //while (running) + { + // try { // 4J - removed try/catch + // if (minecraftApplet != null && !minecraftApplet.isActive()) break; // 4J - removed + AABB::resetPool(); + Vec3::resetPool(); + + // if (parent == NULL && Display.isCloseRequested()) { // 4J - removed + // stop(); + // } + + // 4J-PB - AUTOSAVE TIMER - only in the full game and if the player is the host + if(level!=NULL && ProfileManager.IsFullVersion() && g_NetworkManager.IsHost()) + { + /*if(!bAutosaveTimerSet) + { + // set the timer + bAutosaveTimerSet=true; + + app.SetAutosaveTimerTime(); + } + else*/ + { + // if the pause menu is up for the primary player, don't autosave + // If saving isn't disabled, and the main player has a app action running , or has any crafting or containers open, don't autosave + if(!StorageManager.GetSaveDisabled() && (app.GetXuiAction(ProfileManager.GetPrimaryPad())==eAppAction_Idle) ) + { + if(!ui.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad()) && !ui.IsIgnoreAutosaveMenuDisplayed(ProfileManager.GetPrimaryPad())) + { + // check if the autotimer countdown has reached zero + unsigned char ucAutosaveVal=app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Autosave); + bool bTrialTexturepack=false; + if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) + { + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; + + DLCPack *pDLCPack=pDLCTexPack->getDLCInfoParentPack(); + + if( pDLCPack ) + { + if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) + { + bTrialTexturepack=true; + } + } + } + + // If the autosave value is not zero, and the player isn't using a trial texture pack, then check whether we need to save this tick + if((ucAutosaveVal!=0) && !bTrialTexturepack) + { + if(app.AutosaveDue()) + { + // disable the autosave countdown + ui.ShowAutosaveCountdownTimer(false); + + // Need to save now + app.DebugPrintf("+++++++++++\n"); + app.DebugPrintf("+++Autosave\n"); + app.DebugPrintf("+++++++++++\n"); + app.SetAction(ProfileManager.GetPrimaryPad(),eAppAction_AutosaveSaveGame); + //app.SetAutosaveTimerTime(); +#ifndef _CONTENT_PACKAGE + { + // print the time + SYSTEMTIME UTCSysTime; + GetSystemTime( &UTCSysTime ); + //char szTime[15]; + + app.DebugPrintf("%02d:%02d:%02d\n",UTCSysTime.wHour,UTCSysTime.wMinute,UTCSysTime.wSecond); + } +#endif + } + else + { + unsigned int uiTimeToAutosave=app.SecondsToAutosave(); + + if(uiTimeToAutosave<6) + { + ui.ShowAutosaveCountdownTimer(true); + ui.UpdateAutosaveCountdownTimer(uiTimeToAutosave); + } + } + } + } + else + { + // disable the autosave countdown + ui.ShowAutosaveCountdownTimer(false); + } + } + } + } + + // 4J-PB - Once we're in the level, check if the players have the level in their banned list and ask if they want to play it + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( localplayers[i] && (app.GetBanListCheck(i)==false) && !Minecraft::GetInstance()->isTutorial() && ProfileManager.IsSignedInLive(i) && !ProfileManager.IsGuest(i) ) + { + // If there is a sys ui displayed, we can't display the message box here, so ignore until we can + if(!ProfileManager.IsSystemUIDisplayed()) + { + app.SetBanListCheck(i,true); + // 4J-PB - check if the level is in the banned level list + // get the unique save name and xuid from whoever is the host +#if defined _XBOX || defined _XBOX_ONE + INetworkPlayer *pHostPlayer = g_NetworkManager.GetHostPlayer(); + +#ifdef _XBOX + PlayerUID xuid=((NetworkPlayerXbox *)pHostPlayer)->GetUID(); +#else + PlayerUID xuid=pHostPlayer->GetUID(); +#endif + + if(app.IsInBannedLevelList(i,xuid,app.GetUniqueMapName())) + { + // put up a message box asking if the player would like to unban this level + app.DebugPrintf("This level is banned\n"); + // set the app action to bring up the message box to give them the option to remove from the ban list or exit the level + app.SetAction(i,eAppAction_LevelInBanLevelList,(void *)TRUE); + } +#endif + } + } + } + + if(!ProfileManager.IsSystemUIDisplayed() && app.DLCInstallProcessCompleted() && !app.DLCInstallPending() && app.m_dlcManager.NeedsCorruptCheck() ) + { + app.m_dlcManager.checkForCorruptDLCAndAlert(); + } + + // When we go into the first loaded level, check if the console has active joypads that are not in the game, and bring up the quadrant display to remind them to press start (if the session has space) + if(level!=NULL && bFirstTimeIntoGame && g_NetworkManager.SessionHasSpace()) + { + // have a short delay before the display + if(iFirstTimeCountdown==0) + { + bFirstTimeIntoGame=false; + + if(app.IsLocalMultiplayerAvailable()) + { + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if((localplayers[i] == NULL) && InputManager.IsPadConnected(i)) + { + if(!ui.PressStartPlaying(i)) + { + ui.ShowPressStart(i); + } + } + } + } + } + else iFirstTimeCountdown--; + } + // 4J-PB - store any button toggles for the players, since the minecraft::tick may not be called if we're running fast, and a button press and release will be missed + + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { +#ifdef __ORBIS__ + if ( m_pPsPlusUpsell != NULL && m_pPsPlusUpsell->hasResponse() && m_pPsPlusUpsell->m_userIndex == i ) + { + delete m_pPsPlusUpsell; + m_pPsPlusUpsell = NULL; + + if ( ProfileManager.HasPlayStationPlus(i) ) + { + app.DebugPrintf(" Player_%i is now authorised for PsPlus.\n", i); + if (!ui.PressStartPlaying(i)) ui.ShowPressStart(i); + } + else + { + UINT uiIDA[1] = { IDS_OK }; + ui.RequestMessageBox( IDS_CANTJOIN_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA, 1, i, NULL, NULL, app.GetStringTable() ); + } + } + else +#endif + if(localplayers[i]) + { + // 4J-PB - add these to check for coming out of idle + if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_JUMP)) localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<abilities.flying) + { + if(InputManager.ButtonDown(i, MINECRAFT_ACTION_SNEAK_TOGGLE)) localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL< 0) + localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<inventory) + localplayers[i]->inventory->selected = slot; + } + } + } +#endif + +#ifndef _FINAL_BUILD + if( app.DebugSettingsOn() && app.GetUseDPadForDebug() ) + { + localplayers[i]->ullDpad_last = 0; + localplayers[i]->ullDpad_this = 0; + localplayers[i]->ullDpad_filtered = 0; + if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_DPAD_RIGHT)) localplayers[i]->ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullButtonsPressed|=1LL<ullDpad_this = 0; + int dirCount = 0; + +#ifndef __PSVITA__ + if(InputManager.ButtonDown(i, MINECRAFT_ACTION_DPAD_LEFT)) { localplayers[i]->ullDpad_this|=1LL<ullDpad_this|=1LL<ullDpad_this|=1LL<ullDpad_this|=1LL<ullDpad_last = localplayers[i]->ullDpad_this; + localplayers[i]->ullDpad_filtered = localplayers[i]->ullDpad_this; + } + else + { + localplayers[i]->ullDpad_filtered = localplayers[i]->ullDpad_last; + } + } + + // for the opacity timer + if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_LEFT_SCROLL) || InputManager.ButtonPressed(i, MINECRAFT_ACTION_RIGHT_SCROLL)) + //InputManager.ButtonPressed(i, MINECRAFT_ACTION_USE) || InputManager.ButtonPressed(i, MINECRAFT_ACTION_ACTION)) + { + app.SetOpacityTimer(i); + } + } + else + { + // 4J Stu - This doesn't make any sense with the way we handle XboxOne users +#ifndef _DURANGO + // did we just get input from a player who doesn't exist? They'll be wanting to join the game then + bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() && InputManager.ButtonPressed(i); +#ifdef __ORBIS__ + // Check for remote play + tryJoin = tryJoin && InputManager.IsLocalMultiplayerAvailable(); + + // 4J Stu - Check that content restriction information has been received + if( !g_NetworkManager.IsLocalGame() ) + { + tryJoin = tryJoin && ProfileManager.GetChatAndContentRestrictions(i,true,NULL,NULL,NULL); + } +#endif + if(tryJoin) + { + if(!ui.PressStartPlaying(i)) + { +#ifdef __ORBIS__ + // Don't let player start joining until their PS Plus check has finished + if (g_NetworkManager.IsLocalGame() || !ProfileManager.RequestingPlaystationPlus(i)) +#endif + { + ui.ShowPressStart(i); + } + } + else + { + // did we just get input from a player who doesn't exist? They'll be wanting to join the game then +#ifdef __ORBIS__ + if(InputManager.ButtonPressed(i, ACTION_MENU_A)) +#else + if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_PAUSEMENU)) +#endif + { + // Let them join + + // are they signed in? + if(ProfileManager.IsSignedIn(i)) + { + // if this is a local game, then the player just needs to be signed in + if( g_NetworkManager.IsLocalGame() || (ProfileManager.IsSignedInLive(i) && ProfileManager.AllowedToPlayMultiplayer(i) ) ) + { +#ifdef __ORBIS__ + bool contentRestricted = false; + ProfileManager.GetChatAndContentRestrictions(i,false,NULL,&contentRestricted,NULL); // TODO! + + if (!g_NetworkManager.IsLocalGame() && contentRestricted) + { + ui.RequestContentRestrictedMessageBox(IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_CONTENT_RESTRICTION, i); + } + else if(!g_NetworkManager.IsLocalGame() && !ProfileManager.HasPlayStationPlus(i)) + { + m_pPsPlusUpsell = new PsPlusUpsellWrapper(i); + m_pPsPlusUpsell->displayUpsell(); + } + else +#endif + if( level->isClientSide ) + { + bool success=addLocalPlayer(i); + + if(!success) + { + app.DebugPrintf("Bringing up the sign in ui\n"); + ProfileManager.RequestSignInUI(false, g_NetworkManager.IsLocalGame(), true, false,true,&Minecraft::InGame_SignInReturned, this,i); + } + else + { +#ifdef __ORBIS__ + if(g_NetworkManager.IsLocalGame() == false) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(i,false,&chatRestricted,NULL,NULL); + if(chatRestricted) + { + ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, i ); + } + } +#endif + } + } + else + { + // create the localplayer + shared_ptr player = localplayers[i]; + if( player == NULL) + { + player = createExtraLocalPlayer(i, (convStringToWstring( ProfileManager.GetGamertag(i) )).c_str(), i, level->dimension->id); + } + } + } + else + { + if( ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && !ProfileManager.AllowedToPlayMultiplayer(i) ) + { + ProfileManager.RequestConvertOfflineToGuestUI( &Minecraft::InGame_SignInReturned, this,i); + // 4J Stu - Don't allow converting to guests as we don't allow any guest sign-in while in the game + // Fix for #66516 - TCR #124: MPS Guest Support ; #001: BAS Game Stability: TU8: The game crashes when second Guest signs-in on console which takes part in Xbox LIVE multiplayer session. + //ProfileManager.RequestConvertOfflineToGuestUI( &Minecraft::InGame_SignInReturned, this,i); + +#ifndef _XBOX + ui.HidePressStart(); +#endif + +#ifdef __ORBIS__ + int npAvailability = ProfileManager.getNPAvailability(i); + + // Check if PSN is unavailable because of age restriction + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + { + UINT uiIDA[1]; + uiIDA[0] = IDS_OK; + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, i, NULL, NULL, app.GetStringTable()); + } + else if (ProfileManager.IsSignedIn(i) && !ProfileManager.IsSignedInLive(i)) + { + // You're not signed in to PSN! + UINT uiIDA[2]; + uiIDA[0] = IDS_PRO_NOTONLINE_ACCEPT; + uiIDA[1] = IDS_CANCEL; + ui.RequestMessageBox(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, i,&Minecraft::MustSignInReturnedPSN, this, app.GetStringTable(), NULL, 0, false); + } + else +#endif + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox(IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA, 1, i, NULL, NULL, app.GetStringTable()); + } + } + //else + { + // player not signed in to live + // bring up the sign in dialog + app.DebugPrintf("Bringing up the sign in ui\n"); + ProfileManager.RequestSignInUI(false, g_NetworkManager.IsLocalGame(), true, false,true,&Minecraft::InGame_SignInReturned, this,i); + } + } + } + else + { + // bring up the sign in dialog + app.DebugPrintf("Bringing up the sign in ui\n"); + ProfileManager.RequestSignInUI(false, g_NetworkManager.IsLocalGame(), true, false,true,&Minecraft::InGame_SignInReturned, this,i); + } + } + } + } +#endif // _DURANGO + } + } + +#ifdef _DURANGO + // did we just get input from a player who doesn't exist? They'll be wanting to join the game then + if(!pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() ) + { + int firstEmptyUser = 0; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if(localplayers[i] == NULL) + { + firstEmptyUser = i; + break; + } + } + + // For durango, check for unmapped controllers + for(unsigned int iPad = XUSER_MAX_COUNT; iPad < (XUSER_MAX_COUNT + InputManager.MAX_GAMEPADS); ++iPad) + { + if(InputManager.IsPadLocked(iPad) || !InputManager.IsPadConnected(iPad) ) continue; + if(!InputManager.ButtonPressed(iPad)) continue; + + if(!ui.PressStartPlaying(firstEmptyUser)) + { + ui.ShowPressStart(firstEmptyUser); + } + else + { + // did we just get input from a player who doesn't exist? They'll be wanting to join the game then + if(InputManager.ButtonPressed(iPad, MINECRAFT_ACTION_PAUSEMENU)) + { + // bring up the sign in dialog + app.DebugPrintf("Bringing up the sign in ui\n"); + ProfileManager.RequestSignInUI(false, g_NetworkManager.IsLocalGame(), true, false,true,&Minecraft::InGame_SignInReturned, this,iPad); + + // 4J Stu - If we are joining a pad here, then we don't want to try and join any others + break; + } + } + } + } +#endif + + if (pause && level != NULL) + { + float lastA = timer->a; + timer->advanceTime(); + timer->a = lastA; + } + else + { + timer->advanceTime(); + } + + //__int64 beforeTickTime = System::nanoTime(); + for (int i = 0; i < timer->ticks; i++) + { + bool bLastTimerTick = ( i == ( timer->ticks - 1 ) ); + // 4J-PB - the tick here can run more than once, and this is a problem for our input, which would see the a key press twice with the same time - let's tick the inputmanager again + if(i!=0) + { + InputManager.Tick(); + app.HandleButtonPresses(); + } + + ticks++; + // try { // 4J - try/catch removed + bool bFirst = true; + for( int idx = 0; idx < XUSER_MAX_COUNT; idx++ ) + { + // 4J - If we are waiting for this connection to do something, then tick it here. + // This replaces many of the original Java scenes which would tick the connection while showing that scene + if( m_pendingLocalConnections[idx] != NULL ) + { + m_pendingLocalConnections[idx]->tick(); + } + + // reset the player inactive tick + if(localplayers[idx]!=NULL) + { + // any input received? + bool hasControllerInput = (localplayers[idx]->ullButtonsPressed!=0) || InputManager.GetJoypadStick_LX(idx,false)!=0.0f || + InputManager.GetJoypadStick_LY(idx,false)!=0.0f || InputManager.GetJoypadStick_RX(idx,false)!=0.0f || + InputManager.GetJoypadStick_RY(idx,false)!=0.0f; +#ifdef _WINDOWS64 + bool hasKBMInput = (idx == 0) && g_KBMInput.HasAnyInput(); +#else + bool hasKBMInput = false; +#endif + if(hasControllerInput || hasKBMInput) + { + localplayers[idx]->ResetInactiveTicks(); + } + else + { + localplayers[idx]->IncrementInactiveTicks(); + } + + if(localplayers[idx]->GetInactiveTicks()>200) + { + if(!localplayers[idx]->isIdle() && localplayers[idx]->onGround) + { + localplayers[idx]->setIsIdle(true); + } + } + else + { + if(localplayers[idx]->isIdle()) + { + localplayers[idx]->setIsIdle(false); + } + } + } + + if( setLocalPlayerIdx(idx) ) + { + tick(bFirst, bLastTimerTick); + bFirst = false; + // clear the stored button downs since the tick for this player will now have actioned them + player->ullButtonsPressed=0LL; + } + } + + ui.HandleGameTick(); + + setLocalPlayerIdx(ProfileManager.GetPrimaryPad()); + + // 4J - added - now do the equivalent of level::animateTick, but taking into account the positions of all our players + + for( int l = 0; l < levels.length; l++ ) + { + if( levels[l] ) + { + levels[l]->animateTickDoWork(); + } + } + + // } catch (LevelConflictException e) { + // this.level = null; + // setLevel(null); + // setScreen(new LevelConflictScreen()); + // } +// SparseLightStorage::tick(); // 4J added +// CompressedTileStorage::tick(); // 4J added +// SparseDataStorage::tick(); // 4J added + } + //__int64 tickDuraction = System::nanoTime() - beforeTickTime; + MemSect(31); + checkGlError(L"Pre render"); + MemSect(0); + + TileRenderer::fancy = options->fancyGraphics; + + // if (pause) timer.a = 1; + + PIXBeginNamedEvent(0,"Sound engine update"); + soundEngine->tick((shared_ptr *)localplayers, timer->a); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Light update"); + + if (level != NULL) level->updateLights(); + glEnable(GL_TEXTURE_2D); + + PIXEndNamedEvent(); + + // if (!Keyboard::isKeyDown(Keyboard.KEY_F7)) Display.update(); // 4J - removed + + // 4J-PB - changing this to be per player + //if (player != NULL && player->isInWall()) options->thirdPersonView = false; + if (player != NULL && player->isInWall()) player->SetThirdPersonView(0); + + if (!noRender) + { + bool bFirst = true; + int iPrimaryPad=ProfileManager.GetPrimaryPad(); + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( setLocalPlayerIdx(i) ) + { + PIXBeginNamedEvent(0,"Game render player idx %d",i); + RenderManager.StateSetViewport((C4JRender::eViewportType)player->m_iScreenSection); + gameRenderer->render(timer->a, bFirst); + bFirst = false; + PIXEndNamedEvent(); + + if(i==iPrimaryPad) + { +#ifdef __ORBIS__ + // PS4 does much of the screen-capturing for every frame, to simplify the synchronisation when we actually want a capture. This call tells it the point in the frame to do it. + RenderManager.InternalScreenCapture(); +#endif + // check to see if we need to capture a screenshot for the save game thumbnail + switch(app.GetXuiAction(i)) + { + case eAppAction_ExitWorldCapturedThumbnail: + case eAppAction_SaveGameCapturedThumbnail: + case eAppAction_AutosaveSaveGameCapturedThumbnail: + // capture the save thumbnail + app.CaptureSaveThumbnail(); + break; + } + } + } + } + // If there's an unoccupied quadrant, then clear that to black + if( unoccupiedQuadrant > -1 ) + { + // render a logo + RenderManager.StateSetViewport((C4JRender::eViewportType)(C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT + unoccupiedQuadrant)); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + ui.SetEmptyQuadrantLogo(C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT + unoccupiedQuadrant); + } + setLocalPlayerIdx(iPrimaryPad); + RenderManager.StateSetViewport(C4JRender::VIEWPORT_TYPE_FULLSCREEN); + +#ifdef _XBOX + // Do we need to capture a screenshot for a social post? + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if(app.GetXuiAction(i)==eAppAction_SocialPostScreenshot) + { + app.CaptureScreenshot(i); + } + } +#endif + } + glFlush(); + + /* 4J - removed + if (!Display::isActive()) + { + if (fullscreen) + { + this->toggleFullScreen(); + } + Sleep(10); + } + */ + + if (options->renderDebug) + { + //renderFpsMeter(tickDuraction); + +#if DEBUG_RENDER_SHOWS_PACKETS + // To show data for only one packet type + //Packet::renderPacketStats(31); + + // To show data for all packet types selected as being renderable in the Packet:static_ctor call to Packet::map + Packet::renderAllPacketStats(); +#else + // To show the size of the QNet queue in bytes and messages + g_NetworkManager.renderQueueMeter(); +#endif + } + else + { + lastTimer = System::nanoTime(); + } + + achievementPopup->render(); + + PIXBeginNamedEvent(0,"Sleeping"); + Sleep(0); // 4J - was Thread.yield() + PIXEndNamedEvent(); + + // if (Keyboard::isKeyDown(Keyboard::KEY_F7)) Display.update(); // 4J - removed condition + PIXBeginNamedEvent(0,"Display update"); + Display::update(); + PIXEndNamedEvent(); + + // checkScreenshot(); // 4J - removed + + /* 4J - removed + if (parent != NULL && !fullscreen) + { + if (parent.getWidth() != width || parent.getHeight() != height) + { + width = parent.getWidth(); + height = parent.getHeight(); + if (width <= 0) width = 1; + if (height <= 0) height = 1; + + resize(width, height); + } + } + */ + MemSect(31); + checkGlError(L"Post render"); + MemSect(0); + frames++; + //pause = !isClientSide() && screen != NULL && screen->isPauseScreen(); + //pause = g_NetworkManager.IsLocalGame() && g_NetworkManager.GetPlayerCount() == 1 && app.IsPauseMenuDisplayed(ProfileManager.GetPrimaryPad()); + pause = app.IsAppPaused(); + +#ifndef _CONTENT_PACKAGE + while (System::nanoTime() >= lastTime + 1000000000) + { + MemSect(31); + fpsString = _toString(frames) + L" fps, " + _toString(Chunk::updates) + L" chunk updates"; + MemSect(0); + Chunk::updates = 0; + lastTime += 1000000000; + frames = 0; + } +#endif + /* + } catch (LevelConflictException e) { + this.level = null; + setLevel(null); + setScreen(new LevelConflictScreen()); + } catch (OutOfMemoryError e) { + emergencySave(); + setScreen(new OutOfMemoryScreen()); + System.gc(); + } + */ + } + /* + } catch (StopGameException e) { + } catch (Throwable e) { + emergencySave(); + e.printStackTrace(); + crash(new CrashReport("Unexpected error", e)); + } finally { + destroy(); + } + */ + } + LeaveCriticalSection(&m_setLevelCS); +} + +void Minecraft::run_end() +{ + destroy(); +} + +void Minecraft::emergencySave() +{ + // 4J - lots of try/catches removed here, and garbage collector things + levelRenderer->clear(); + AABB::clearPool(); + Vec3::clearPool(); + setLevel(NULL); +} + +void Minecraft::renderFpsMeter(__int64 tickTime) +{ + int nsPer60Fps = 1000000000l / 60; + if (lastTimer == -1) + { + lastTimer = System::nanoTime(); + } + __int64 now = System::nanoTime(); + Minecraft::tickTimes[(Minecraft::frameTimePos) & (Minecraft::frameTimes_length - 1)] = tickTime; + Minecraft::frameTimes[(Minecraft::frameTimePos++) & (Minecraft::frameTimes_length - 1)] = now - lastTimer; + lastTimer = now; + + glClear(GL_DEPTH_BUFFER_BIT); + glMatrixMode(GL_PROJECTION); + glEnable(GL_COLOR_MATERIAL); + glLoadIdentity(); + glOrtho(0, (float)width, (float)height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glLineWidth(1); + glDisable(GL_TEXTURE_2D); + Tesselator *t = Tesselator::getInstance(); + t->begin(GL_QUADS); + int hh1 = (int) (nsPer60Fps / 200000); + t->color(0x20000000); + t->vertex((float)(0), (float)( height - hh1), (float)( 0)); + t->vertex((float)(0), (float)( height), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height - hh1), (float)( 0)); + + t->color(0x20200000); + t->vertex((float)(0), (float)( height - hh1 * 2), (float)( 0)); + t->vertex((float)(0), (float)( height - hh1), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height - hh1), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height - hh1 * 2), (float)( 0)); + + t->end(); + __int64 totalTime = 0; + for (int i = 0; i < Minecraft::frameTimes_length; i++) + { + totalTime += Minecraft::frameTimes[i]; + } + int hh = (int) (totalTime / 200000 / Minecraft::frameTimes_length); + t->begin(GL_QUADS); + t->color(0x20400000); + t->vertex((float)(0), (float)( height - hh), (float)( 0)); + t->vertex((float)(0), (float)( height), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height), (float)( 0)); + t->vertex((float)(Minecraft::frameTimes_length), (float)( height - hh), (float)( 0)); + t->end(); + t->begin(GL_LINES); + for (int i = 0; i < Minecraft::frameTimes_length; i++) + { + int col = ((i - Minecraft::frameTimePos) & (Minecraft::frameTimes_length - 1)) * 255 / Minecraft::frameTimes_length; + int cc = col * col / 255; + cc = cc * cc / 255; + int cc2 = cc * cc / 255; + cc2 = cc2 * cc2 / 255; + if (Minecraft::frameTimes[i] > nsPer60Fps) + { + t->color(0xff000000 + cc * 65536); + } + else + { + t->color(0xff000000 + cc * 256); + } + + __int64 time = Minecraft::frameTimes[i] / 200000; + __int64 time2 = Minecraft::tickTimes[i] / 200000; + + t->vertex((float)(i + 0.5f), (float)( height - time + 0.5f), (float)( 0)); + t->vertex((float)(i + 0.5f), (float)( height + 0.5f), (float)( 0)); + + // if (Minecraft.frameTimes[i]>nsPer60Fps) { + t->color(0xff000000 + cc * 65536 + cc * 256 + cc * 1); + // } else { + // t.color(0xff808080 + cc/2 * 256); + // } + t->vertex((float)(i + 0.5f), (float)( height - time + 0.5f), (float)( 0)); + t->vertex((float)(i + 0.5f), (float)( height - (time - time2) + 0.5f), (float)( 0)); + } + t->end(); + + glEnable(GL_TEXTURE_2D); +} + +void Minecraft::stop() +{ + running = false; + // keepPolling = false; +} + +void Minecraft::pauseGame() +{ + if (screen != NULL) return; + + // setScreen(new PauseScreen()); // 4J - TODO put back in +} + +void Minecraft::resize(int width, int height) +{ + if (width <= 0) width = 1; + if (height <= 0) height = 1; + this->width = width; + this->height = height; + + if (screen != NULL) + { + ScreenSizeCalculator ssc(options, width, height); + int screenWidth = ssc.getWidth(); + int screenHeight = ssc.getHeight(); + // screen->init(this, screenWidth, screenHeight); // 4J - TODO - put back in + } +} + +void Minecraft::verify() +{ + /* 4J - TODO + new Thread() { + public void run() { + try { + HttpURLConnection huc = (HttpURLConnection) new URL("https://login.minecraft.net/session?name=" + user.name + "&session=" + user.sessionId).openConnection(); + huc.connect(); + if (huc.getResponseCode() == 400) { + warezTime = System.currentTimeMillis(); + } + huc.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + */ +} + + + + +void Minecraft::levelTickUpdateFunc(void* pParam) +{ + Level* pLevel = (Level*)pParam; + pLevel->tick(); +} + +void Minecraft::levelTickThreadInitFunc() +{ + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::UseDefaultThreadStorage(); +} + + +// 4J - added bFirst parameter, which is true for the first active viewport in splitscreen +// 4J - added bUpdateTextures, which is true if the actual renderer textures are to be updated - this will be true for the last time this tick runs with bFirst true +void Minecraft::tick(bool bFirst, bool bUpdateTextures) +{ + int iPad=player->GetXboxPad(); + //OutputDebugString("Minecraft::tick\n"); + + //4J-PB - only tick this player's stats + stats[iPad]->tick(iPad); + + // Tick the opacity timer (to display the interface at default opacity for a certain time if the user has been navigating it) + app.TickOpacityTimer(iPad); + + // 4J added + if( bFirst ) levelRenderer->destroyedTileManager->tick(); + + gui->tick(); + gameRenderer->pick(1); +#if 0 + // 4J - removed - we don't use ChunkCache anymore + if (player != NULL) + { + ChunkSource *cs = level->getChunkSource(); + if (dynamic_cast(cs) != NULL) + { + ChunkCache *spcc = (ChunkCache *)cs; + + // 4J - there was also Mth::floors on these ints but that seems superfluous + int xt = ((int) player->x) >> 4; + int zt = ((int) player->z) >> 4; + spcc->centerOn(xt, zt); + } + } +#endif + + // soundEngine.playMusicTick(); + + if (!pause && level != NULL) gameMode->tick(); + MemSect(31); + glBindTexture(GL_TEXTURE_2D, textures->loadTexture(TN_TERRAIN));//L"/terrain.png")); + MemSect(0); + if( bFirst ) + { + PIXBeginNamedEvent(0,"Texture tick"); + if (!pause) textures->tick(bUpdateTextures); + PIXEndNamedEvent(); + } + + /* + * if (serverConnection != null && !(screen instanceof ErrorScreen)) { + * if (!serverConnection.isConnected()) { + * progressRenderer.progressStart("Connecting.."); + * progressRenderer.progressStagePercentage(0); } else { + * serverConnection.tick(); serverConnection.sendPosition(player); } } + */ + if (screen == NULL && player != NULL ) + { + if (player->getHealth() <= 0 && !ui.GetMenuDisplayed(iPad) ) + { + setScreen(NULL); + } + else if (player->isSleeping() && level != NULL && level->isClientSide) + { + // setScreen(new InBedChatScreen()); // 4J - TODO put back in + } + } + else if (screen != NULL && (dynamic_cast(screen)!=NULL) && !player->isSleeping()) + { + setScreen(NULL); + } + + if (screen != NULL) + { + player->missTime = 10000; + player->lastClickTick[0] = ticks + 10000; + player->lastClickTick[1] = ticks + 10000; + } + + if (screen != NULL) + { + screen->updateEvents(); + if (screen != NULL) + { + screen->particles->tick(); + screen->tick(); + } + } + +#ifdef _WINDOWS64 + if ((screen != NULL || ui.GetMenuDisplayed(iPad)) && g_KBMInput.IsMouseGrabbed()) + { + g_KBMInput.SetMouseGrabbed(false); + } +#endif + + if (screen == NULL && !ui.GetMenuDisplayed(iPad) ) + { +#ifdef _WINDOWS64 + if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsWindowFocused()) + { + g_KBMInput.SetMouseGrabbed(true); + } +#endif + // 4J-PB - add some tooltips if required + int iA=-1, iB=-1, iX, iY=IDS_CONTROLS_INVENTORY, iLT=-1, iRT=-1, iLB=-1, iRB=-1; + + if(player->abilities.instabuild) + { + iX=IDS_TOOLTIPS_CREATIVE; + } + else + { + iX=IDS_CONTROLS_CRAFTING; + } + // control scheme remapping can move the Action button, so we need to check this + int *piAction; + int *piJump; + int *piUse; + + unsigned int uiAction = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal( iPad ) ,MINECRAFT_ACTION_ACTION ); + unsigned int uiJump = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal( iPad ) ,MINECRAFT_ACTION_JUMP ); + unsigned int uiUse = InputManager.GetGameJoypadMaps(InputManager.GetJoypadMapVal( iPad ) ,MINECRAFT_ACTION_USE ); + + // Also need to handle PS3 having swapped triggers/bumpers + switch(uiAction) + { + case _360_JOY_BUTTON_RT: + piAction=&iRT; + break; + case _360_JOY_BUTTON_LT: + piAction=&iLT; + break; + case _360_JOY_BUTTON_LB: + piAction=&iLB; + break; + case _360_JOY_BUTTON_RB: + piAction=&iRB; + break; + case _360_JOY_BUTTON_A: + default: + piAction=&iA; + break; + } + + switch(uiJump) + { + case _360_JOY_BUTTON_LT: + piJump=&iLT; + break; + case _360_JOY_BUTTON_RT: + piJump=&iRT; + break; + case _360_JOY_BUTTON_LB: + piJump=&iLB; + break; + case _360_JOY_BUTTON_RB: + piJump=&iRB; + break; + case _360_JOY_BUTTON_A: + default: + piJump=&iA; + break; + } + + switch(uiUse) + { + case _360_JOY_BUTTON_LB: + piUse=&iLB; + break; + case _360_JOY_BUTTON_RB: + piUse=&iRB; + break; + case _360_JOY_BUTTON_LT: + piUse=&iLT; + break; + case _360_JOY_BUTTON_RT: + default: + piUse=&iRT; + break; + } + + if (player->isUnderLiquid(Material::water)) + { + *piJump=IDS_TOOLTIPS_SWIMUP; + } + else + { + *piJump=-1; + } + + *piUse=-1; + *piAction=-1; + + // 4J-PB another special case for when the player is sleeping in a bed + if (player->isSleeping() && (level != NULL) && level->isClientSide) + { + *piUse=IDS_TOOLTIPS_WAKEUP; + } + else + { + // no hit result, but we may have something in our hand that we can do something with + shared_ptr itemInstance = player->inventory->getSelected(); + + // 4J-JEV: Moved all this here to avoid having it in 3 different places. + if (itemInstance) + { + // 4J-PB - very special case for boat and empty bucket and glass bottle and more + bool bUseItem = gameMode->useItem(player, level, itemInstance, true); + + switch (itemInstance->getItem()->id) + { + // food + case Item::potatoBaked_Id: + case Item::potato_Id: + case Item::pumpkinPie_Id: + case Item::potatoPoisonous_Id: + case Item::carrotGolden_Id: + case Item::carrots_Id: + case Item::mushroomStew_Id: + case Item::apple_Id: + case Item::bread_Id: + case Item::porkChop_raw_Id: + case Item::porkChop_cooked_Id: + case Item::apple_gold_Id: + case Item::fish_raw_Id: + case Item::fish_cooked_Id: + case Item::cookie_Id: + case Item::beef_cooked_Id: + case Item::beef_raw_Id: + case Item::chicken_cooked_Id: + case Item::chicken_raw_Id: + case Item::melon_Id: + case Item::rotten_flesh_Id: + case Item::spiderEye_Id: + // Check that we are actually hungry so will eat this item + { + FoodItem *food = (FoodItem *)itemInstance->getItem(); + if (food != NULL && food->canEat(player)) + { + *piUse=IDS_TOOLTIPS_EAT; + } + } + break; + + case Item::milk_Id: + *piUse=IDS_TOOLTIPS_DRINK; + break; + + case Item::fishingRod_Id: // use + *piUse=IDS_TOOLTIPS_USE; + break; + + case Item::egg_Id: // throw + case Item::snowBall_Id: + *piUse=IDS_TOOLTIPS_THROW; + break; + + case Item::bow_Id: // draw or release + if ( player->abilities.instabuild || player->inventory->hasResource(Item::arrow_Id) ) + { + if (player->isUsingItem()) *piUse=IDS_TOOLTIPS_RELEASE_BOW; + else *piUse=IDS_TOOLTIPS_DRAW_BOW; + } + break; + + case Item::sword_wood_Id: + case Item::sword_stone_Id: + case Item::sword_iron_Id: + case Item::sword_diamond_Id: + case Item::sword_gold_Id: + *piUse=IDS_TOOLTIPS_BLOCK; + break; + + case Item::bucket_empty_Id: + case Item::glassBottle_Id: + if (bUseItem) *piUse=IDS_TOOLTIPS_COLLECT; + break; + + case Item::boat_Id: + case Tile::waterLily_Id: + if (bUseItem) *piUse=IDS_TOOLTIPS_PLACE; + break; + + case Item::potion_Id: + if (bUseItem) + { + if (MACRO_POTION_IS_SPLASH(itemInstance->getAuxValue())) *piUse=IDS_TOOLTIPS_THROW; + else *piUse=IDS_TOOLTIPS_DRINK; + } + break; + + case Item::enderPearl_Id: + if (bUseItem) *piUse=IDS_TOOLTIPS_THROW; + break; + + case Item::eyeOfEnder_Id: + // This will only work if there is a stronghold in this dimension + if ( bUseItem && (level->dimension->id==0) && level->getLevelData()->getHasStronghold() ) + { + *piUse=IDS_TOOLTIPS_THROW; + } + break; + + case Item::expBottle_Id: + if (bUseItem) *piUse=IDS_TOOLTIPS_THROW; + break; + } + } + + if (hitResult!=NULL) + { + switch(hitResult->type) + { + case HitResult::TILE: + { + int x,y,z; + x=hitResult->x; + y=hitResult->y; + z=hitResult->z; + int face = hitResult->f; + + int iTileID=level->getTile(x,y ,z ); + int iData = level->getData(x, y, z); + + if( gameMode != NULL && gameMode->getTutorial() != NULL ) + { + // 4J Stu - For the tutorial we want to be able to record what items we look at so that we can give hints + gameMode->getTutorial()->onLookAt(iTileID,iData); + } + + // 4J-PB - Call the useItemOn with the TestOnly flag set + bool bUseItemOn=gameMode->useItemOn(player, level, itemInstance, x, y, z, face, hitResult->pos, true); + + /* 4J-Jev: + * Moved this here so we have item tooltips to fallback on + * for noteblocks, enderportals and flowerpots in case of non-standard items. + * (ie. ignite behaviour) + */ + if (bUseItemOn && itemInstance!=NULL) + { + switch (itemInstance->getItem()->id) + { + case Tile::mushroom1_Id: + case Tile::mushroom2_Id: + case Tile::tallgrass_Id: + case Tile::cactus_Id: + case Tile::sapling_Id: + case Tile::reeds_Id: + case Tile::flower_Id: + case Tile::rose_Id: + *piUse=IDS_TOOLTIPS_PLANT; + break; + + // Things to USE + case Item::hoe_wood_Id: + case Item::hoe_stone_Id: + case Item::hoe_iron_Id: + case Item::hoe_diamond_Id: + case Item::hoe_gold_Id: + *piUse=IDS_TOOLTIPS_TILL; + break; + + case Item::seeds_wheat_Id: + case Item::netherStalkSeeds_Id: + *piUse=IDS_TOOLTIPS_PLANT; + break; + + case Item::bucket_empty_Id: + switch(iTileID) + { + // can collect lava or water in the empty bucket + case Tile::water_Id: + case Tile::calmWater_Id: + case Tile::lava_Id: + case Tile::calmLava_Id: + *piUse=IDS_TOOLTIPS_COLLECT; + break; + } + break; + + case Item::bucket_lava_Id: + case Item::bucket_water_Id: + *piUse=IDS_TOOLTIPS_EMPTY; + break; + + case Item::dye_powder_Id: + // bonemeal grows various plants + if (itemInstance->getAuxValue() == DyePowderItem::WHITE) + { + switch(iTileID) + { + case Tile::sapling_Id: + case Tile::crops_Id: + case Tile::grass_Id: + case Tile::mushroom1_Id: + case Tile::mushroom2_Id: + case Tile::melonStem_Id: + case Tile::pumpkinStem_Id: + case Tile::carrots_Id: + case Tile::potatoes_Id: + *piUse=IDS_TOOLTIPS_GROW; + break; + } + } + break; + + case Item::painting_Id: + *piUse=IDS_TOOLTIPS_HANG; + break; + + case Item::flintAndSteel_Id: + case Item::fireball_Id: + *piUse=IDS_TOOLTIPS_IGNITE; + break; + + default: + *piUse=IDS_TOOLTIPS_PLACE; + break; + } + } + + switch(iTileID) + { + case Tile::anvil_Id: + case Tile::enchantTable_Id: + case Tile::brewingStand_Id: + case Tile::workBench_Id: + case Tile::furnace_Id: + case Tile::furnace_lit_Id: + case Tile::door_wood_Id: + case Tile::dispenser_Id: + case Tile::lever_Id: + case Tile::button_stone_Id: + case Tile::button_wood_Id: + case Tile::trapdoor_Id: + case Tile::fenceGate_Id: + *piAction=IDS_TOOLTIPS_MINE; + *piUse=IDS_TOOLTIPS_USE; + break; + + case Tile::goldenRail_Id: + case Tile::detectorRail_Id: + case Tile::rail_Id: + if (bUseItemOn) *piUse=IDS_TOOLTIPS_PLACE; + *piAction=IDS_TOOLTIPS_MINE; + break; + + case Tile::chest_Id: + case Tile::enderChest_Id: + *piUse=IDS_TOOLTIPS_OPEN; + *piAction=IDS_TOOLTIPS_MINE; + break; + + case Tile::bed_Id: + if (bUseItemOn) *piUse=IDS_TOOLTIPS_SLEEP; + *piAction=IDS_TOOLTIPS_MINE; + break; + + case Tile::musicBlock_Id: + // if in creative mode, we will mine + if (player->abilities.instabuild) *piAction=IDS_TOOLTIPS_MINE; + else *piAction=IDS_TOOLTIPS_PLAY; + *piUse=IDS_TOOLTIPS_CHANGEPITCH; + break; + + case Tile::sign_Id: + *piAction=IDS_TOOLTIPS_MINE; + break; + + case Tile::cauldron_Id: + // special case for a cauldron of water and an empty bottle + if (itemInstance) + { + int iID=itemInstance->getItem()->id; + int currentData = level->getData(x, y, z); + if ((iID==Item::glassBottle_Id) && (currentData > 0)) + { + *piUse=IDS_TOOLTIPS_COLLECT; + } + } + *piAction=IDS_TOOLTIPS_MINE; + break; + + case Tile::cake_Id: + if (player->abilities.instabuild) // if in creative mode, we will mine + { + *piAction=IDS_TOOLTIPS_MINE; + } + else + { + if (player->getFoodData()->needsFood() ) // 4J-JEV: Changed from healthto hunger. + { + *piAction=IDS_TOOLTIPS_EAT; + *piUse=IDS_TOOLTIPS_EAT; + } + else + { + *piAction=IDS_TOOLTIPS_MINE; + } + } + break; + + case Tile::recordPlayer_Id: + if (!bUseItemOn && itemInstance!=NULL) + { + int iID=itemInstance->getItem()->id; + if ( (iID>=Item::record_01_Id) && (iID<=Item::record_12_Id) ) + { + *piUse=IDS_TOOLTIPS_PLAY; + } + *piAction=IDS_TOOLTIPS_MINE; + } + else + { + if (Tile::recordPlayer->TestUse(level, x, y, z, player)) // means we can eject + { + *piUse=IDS_TOOLTIPS_EJECT; + } + *piAction=IDS_TOOLTIPS_MINE; + } + break; + + case Tile::flowerPot_Id: + if ( !bUseItemOn && (itemInstance != NULL) && (iData == 0) ) + { + int iID = itemInstance->getItem()->id; + if (iID<256) // is it a tile? + { + switch(iID) + { + case Tile::flower_Id: + case Tile::rose_Id: + case Tile::sapling_Id: + case Tile::mushroom1_Id: + case Tile::mushroom2_Id: + case Tile::cactus_Id: + case Tile::deadBush_Id: + *piUse=IDS_TOOLTIPS_PLANT; + break; + + case Tile::tallgrass_Id: + if (itemInstance->getAuxValue() != TallGrass::TALL_GRASS) *piUse=IDS_TOOLTIPS_PLANT; + break; + } + } + } + *piAction=IDS_TOOLTIPS_MINE; + break; + + default: + *piAction=IDS_TOOLTIPS_MINE; + break; + } + } + break; + + case HitResult::ENTITY: + eINSTANCEOF entityType = hitResult->entity->GetType(); + + if( gameMode != NULL && gameMode->getTutorial() != NULL ) + { + // 4J Stu - For the tutorial we want to be able to record what items we look at so that we can give hints + gameMode->getTutorial()->onLookAtEntity(entityType); + } + + switch(entityType) + { + case eTYPE_CHICKEN: + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + // is there an object in hand? + if(player->inventory->IsHeldItem()) + { + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + switch(iID) + { + default: + { + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + + if(!animal->isBaby() && !animal->isInLove() && (animal->getAge() == 0) && animal->isFood(heldItem)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + break; + } + } + break; + + case eTYPE_COW: + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + // is there an object in hand? + if(player->inventory->IsHeldItem()) + { + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + // It's an item + switch(iID) + { + // Things to USE + case Item::bucket_empty_Id: + *piUse=IDS_TOOLTIPS_MILK; + break; + default: + { + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + + if(!animal->isBaby() && !animal->isInLove() && (animal->getAge() == 0) && animal->isFood(heldItem)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + break; + } + } + break; + case eTYPE_MUSHROOMCOW: + // is there an object in hand? + if(player->inventory->IsHeldItem()) + { + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + // It's an item + switch(iID) + { + // Things to USE + case Item::bowl_Id: + case Item::bucket_empty_Id: // You can milk a mooshroom with either a bowl (mushroom soup) or a bucket (milk)! + *piUse=IDS_TOOLTIPS_MILK; + break; + case Item::shears_Id: + { + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + if(!animal->isBaby()) *piUse=IDS_TOOLTIPS_SHEAR; + } + break; + default: + { + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + + if(!animal->isBaby() && !animal->isInLove() && (animal->getAge() == 0) && animal->isFood(heldItem)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + break; + } + } + else + { + // 4J-PB - Fix for #13081 - No tooltip is displayed for hitting a cow when you have nothing in your hand + // nothing in your hand + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + } + break; + + case eTYPE_BOAT: + *piAction=IDS_TOOLTIPS_MINE; + + // are we in the boat already? + if (dynamic_pointer_cast( player->riding ) != NULL) + { + *piUse=IDS_TOOLTIPS_EXIT; + } + else + { + *piUse=IDS_TOOLTIPS_SAIL; + } + break; + case eTYPE_MINECART: + *piAction=IDS_TOOLTIPS_MINE; + // are we in the minecart already? + if (dynamic_pointer_cast( player->riding ) != NULL) + { + *piUse=IDS_TOOLTIPS_EXIT; + } + else + { + switch(dynamic_pointer_cast(hitResult->entity)->type) + { + case Minecart::RIDEABLE: + *piUse=IDS_TOOLTIPS_RIDE; + break; + case Minecart::CHEST: + *piUse=IDS_TOOLTIPS_OPEN; + break; + case Minecart::FURNACE: + // if you have coal, it'll go + // is there an object in hand? + if(player->inventory->IsHeldItem()) + { + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + if(iID==Item::coal->id) + { + *piUse=IDS_TOOLTIPS_USE; + } + else + { + *piUse=IDS_TOOLTIPS_HIT; + } + } + break; + } + } + + break; + case eTYPE_SHEEP: + // can dye a sheep + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + if(player->inventory->IsHeldItem()) + { + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + switch(iID) + { + case Item::dye_powder_Id: + { + shared_ptr sheep = dynamic_pointer_cast(hitResult->entity); + // convert to tile-based color value (0 is white instead of black) + int newColor = ClothTile::getTileDataForItemAuxValue(heldItem->getAuxValue()); + + // can only use a dye on sheep that haven't been sheared + if(!(sheep->isSheared() && sheep->getColor() != newColor)) + { + *piUse=IDS_TOOLTIPS_DYE; + } + } + break; + case Item::shears_Id: + { + shared_ptr sheep = dynamic_pointer_cast(hitResult->entity); + + // can only shear a sheep that hasn't been sheared + if(!sheep->isSheared() ) + { + *piUse=IDS_TOOLTIPS_SHEAR; + } + } + + break; + default: + { + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + + if(!animal->isBaby() && !animal->isInLove() && (animal->getAge() == 0) && animal->isFood(heldItem)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + break; + } + } + + break; + case eTYPE_PIG: + // can ride a pig + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + if (dynamic_pointer_cast( player->riding ) != NULL) + { + *piUse=IDS_TOOLTIPS_EXIT; + } + else + { + // does the pig have a saddle? + if(dynamic_pointer_cast(hitResult->entity)->hasSaddle()) + { + *piUse=IDS_TOOLTIPS_RIDE; + } + else if (!dynamic_pointer_cast(hitResult->entity)->isBaby()) + { + if(player->inventory->IsHeldItem()) + { + shared_ptr heldItem=player->inventory->getSelected(); + int iID=heldItem->getItem()->id; + + switch(iID) + { + case Item::saddle_Id: + *piUse=IDS_TOOLTIPS_SADDLE; + break; + default: + { + shared_ptr animal = dynamic_pointer_cast(hitResult->entity); + + if(!animal->isBaby() && !animal->isInLove() && (animal->getAge() == 0) && animal->isFood(heldItem)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + break; + } + } + } + } + + break; + case eTYPE_WOLF: + // can be tamed, fed, and made to sit/stand, or enter love mode + { + int iID=-1; + shared_ptr heldItem=nullptr; + shared_ptr wolf = dynamic_pointer_cast(hitResult->entity); + + if(player->inventory->IsHeldItem()) + { + heldItem=player->inventory->getSelected(); + iID=heldItem->getItem()->id; + } + + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + + switch(iID) + { + case Item::bone_Id: + if (!wolf->isAngry() && !wolf->isTame()) + { + *piUse=IDS_TOOLTIPS_TAME; + } + else if (equalsIgnoreCase(player->getUUID(), wolf->getOwnerUUID())) + { + if(wolf->isSitting()) + { + *piUse=IDS_TOOLTIPS_FOLLOWME; + } + else + { + *piUse=IDS_TOOLTIPS_SIT; + } + } + + break; + case Item::enderPearl_Id: + // Use is throw, so don't change the tips for the wolf + break; + case Item::dye_powder_Id: + if (wolf->isTame()) + { + if (ClothTile::getTileDataForItemAuxValue(heldItem->getAuxValue()) != wolf->getCollarColor()) + { + *piUse=IDS_TOOLTIPS_DYECOLLAR; + } + else if (wolf->isSitting()) + { + *piUse=IDS_TOOLTIPS_FOLLOWME; + } + else + { + *piUse=IDS_TOOLTIPS_SIT; + } + } + break; + default: + if(wolf->isTame()) + { + if(wolf->isFood(heldItem)) + { + if(wolf->GetSynchedHealth() < wolf->getMaxHealth()) + { + *piUse=IDS_TOOLTIPS_HEAL; + } + else + { + if(!wolf->isBaby() && !wolf->isInLove() && (wolf->getAge() == 0)) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + // break out here + break; + } + + if (equalsIgnoreCase(player->getUUID(), wolf->getOwnerUUID())) + { + if(wolf->isSitting()) + { + *piUse=IDS_TOOLTIPS_FOLLOWME; + } + else + { + *piUse=IDS_TOOLTIPS_SIT; + } + } + } + break; + } + } + break; + case eTYPE_OZELOT: + { + int iID=-1; + shared_ptr heldItem=nullptr; + shared_ptr ocelot = dynamic_pointer_cast(hitResult->entity); + + if(player->inventory->IsHeldItem()) + { + heldItem=player->inventory->getSelected(); + iID=heldItem->getItem()->id; + } + + if(player->isAllowedToAttackAnimals()) *piAction=IDS_TOOLTIPS_HIT; + + if(ocelot->isTame()) + { + // 4J-PB - if you have a raw fish in your hand, you will feed the ocelot rather than have it sit/follow + if(ocelot->isFood(heldItem)) + { + if(!ocelot->isBaby()) + { + if(!ocelot->isInLove()) + { + if(ocelot->getAge() == 0) + { + *piUse=IDS_TOOLTIPS_LOVEMODE; + } + } + else + { + *piUse=IDS_TOOLTIPS_FEED; + } + } + + } + else if (equalsIgnoreCase(player->getUUID(), ocelot->getOwnerUUID())) + { + if(ocelot->isSitting()) + { + *piUse=IDS_TOOLTIPS_FOLLOWME; + } + else + { + *piUse=IDS_TOOLTIPS_SIT; + } + } + } + else if(iID!=-1) + { + switch(iID) + { + default: + { + if(ocelot->isFood(heldItem)) *piUse=IDS_TOOLTIPS_TAME; + } + break; + } + } + } + + break; + + case eTYPE_PLAYER: + { + // Fix for #58576 - TU6: Content: Gameplay: Hit button prompt is available when attacking a host who has "Invisible" option turned on + shared_ptr TargetPlayer = dynamic_pointer_cast(hitResult->entity); + + if(!TargetPlayer->hasInvisiblePrivilege()) // This means they are invisible, not just that they have the privilege + { + if( app.GetGameHostOption(eGameHostOption_PvP) && player->isAllowedToAttackPlayers()) + { + *piAction=IDS_TOOLTIPS_HIT; + } + } + } + break; + case eTYPE_ITEM_FRAME: + { + shared_ptr itemFrame = dynamic_pointer_cast(hitResult->entity); + + // is the frame occupied? + if(itemFrame->getItem()!=NULL) + { + // rotate the item + *piUse=IDS_TOOLTIPS_ROTATE; + } + else + { + // is there an object in hand? + if(player->inventory->IsHeldItem()) + { + *piUse=IDS_TOOLTIPS_PLACE; + } + } + + *piAction=IDS_TOOLTIPS_HIT; + } + break; + case eTYPE_VILLAGER: + { + shared_ptr villager = dynamic_pointer_cast(hitResult->entity); + if (!villager->isBaby()) + { + *piUse=IDS_TOOLTIPS_TRADE; + } + *piAction=IDS_TOOLTIPS_HIT; + } + break; + case eTYPE_ZOMBIE: + { + shared_ptr zomb = dynamic_pointer_cast(hitResult->entity); + shared_ptr heldItem=nullptr; + + if(player->inventory->IsHeldItem()) + { + heldItem=player->inventory->getSelected(); + } + + if ( zomb->isVillager() && zomb->isWeakened() //zomb->hasEffect(MobEffect::weakness) - not present on client. + && heldItem != NULL && heldItem->getItem()->id == Item::apple_gold_Id ) + { + *piUse=IDS_TOOLTIPS_CURE; + } + *piAction=IDS_TOOLTIPS_HIT; + } + break; + default: + *piAction=IDS_TOOLTIPS_HIT; + break; + } + break; + } + } + } + + ui.SetTooltips( iPad, iA,iB,iX,iY,iLT,iRT,iLB,iRB); + + int wheel = 0; + if (InputManager.GetValue(iPad, MINECRAFT_ACTION_LEFT_SCROLL, true) > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_LEFT_SCROLL) ) + { + wheel = 1; + } + else if (InputManager.GetValue(iPad, MINECRAFT_ACTION_RIGHT_SCROLL,true) > 0 && gameMode->isInputAllowed(MINECRAFT_ACTION_RIGHT_SCROLL) ) + { + wheel = -1; + } +#ifdef _WINDOWS64 + if (iPad == 0 && wheel == 0) + { + int mw = g_KBMInput.GetMouseWheel(); + if (mw > 0) wheel = -1; + else if (mw < 0) wheel = 1; + } +#endif + if (wheel != 0) + { + player->inventory->swapPaint(wheel); + + if( gameMode != NULL && gameMode->getTutorial() != NULL ) + { + // 4J Stu - For the tutorial we want to be able to record what items we are using so that we can give hints + gameMode->getTutorial()->onSelectedItemChanged(player->inventory->getSelected()); + } + + // Update presence + player->updateRichPresence(); + + if (options->isFlying) + { + if (wheel > 0) wheel = 1; + if (wheel < 0) wheel = -1; + + options->flySpeed += wheel * .25f; + } + } + + if( gameMode->isInputAllowed(MINECRAFT_ACTION_ACTION) ) + { + if((player->ullButtonsPressed&(1LL<handleMouseClick(0); + player->lastClickTick[0] = ticks; + } + +#ifdef _WINDOWS64 + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_LEFT)); +#else + bool actionHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_ACTION); +#endif + if (actionHeld && ticks - player->lastClickTick[0] >= timer->ticksPerSecond / 4) + { + //printf("MINECRAFT_ACTION_ACTION ButtonDown"); + player->handleMouseClick(0); + player->lastClickTick[0] = ticks; + } + + if(actionHeld) + { + player->handleMouseDown(0, true ); + } + else + { + player->handleMouseDown(0, false ); + } + } + + // 4J Stu - This is how we used to handle the USE action. It has now been replaced with the block below which is more like the way the Java game does it, + // however we may find that the way we had it previously is more fun to play. + /* + if ((InputManager.GetValue(iPad, MINECRAFT_ACTION_USE,true)>0) && gameMode->isInputAllowed(MINECRAFT_ACTION_USE) ) + { + handleMouseClick(1); + lastClickTick = ticks; + } + */ +#ifdef _WINDOWS64 + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE) || (iPad == 0 && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_RIGHT)); +#else + bool useHeld = InputManager.ButtonDown(iPad, MINECRAFT_ACTION_USE); +#endif + if( player->isUsingItem() ) + { + if(!useHeld) gameMode->releaseUsingItem(player); + } + else if( gameMode->isInputAllowed(MINECRAFT_ACTION_USE) ) + { + if( player->abilities.instabuild ) + { + // 4J - attempt to handle click in special creative mode fashion if possible (used for placing blocks at regular intervals) + bool didClick = player->creativeModeHandleMouseClick(1, useHeld ); + // If this handler has put us in lastClick_oldRepeat mode then it is because we aren't placing blocks - behave largely as the code used to + if( player->lastClickState == LocalPlayer::lastClick_oldRepeat ) + { + // If we've already handled the click in creativeModeHandleMouseClick then just record the time of this click + if( didClick ) + { + player->lastClickTick[1] = ticks; + } + else + { + // Otherwise just the original game code for handling autorepeat + if (useHeld && ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4) + { + player->handleMouseClick(1); + player->lastClickTick[1] = ticks; + } + } + } + } + else + { + // Consider as a click if we've had a period of not pressing the button, or we've reached auto-repeat time since the last time + // Auto-repeat is only considered if we aren't riding or sprinting, to avoid photo sensitivity issues when placing fire whilst doing fast things + // Also disable repeat when the player is sleeping to stop the waking up right after using the bed + bool firstClick = ( player->lastClickTick[1] == 0 ); + bool autoRepeat = ticks - player->lastClickTick[1] >= timer->ticksPerSecond / 4; + if ( player->isRiding() || player->isSprinting() || player->isSleeping() ) autoRepeat = false; + if (useHeld) + { + // If the player has just exited a bed, then delay the time before a repeat key is allowed without releasing + if(player->isSleeping() ) player->lastClickTick[1] = ticks + (timer->ticksPerSecond * 2); + if( firstClick || autoRepeat ) + { + bool wasSleeping = player->isSleeping(); + + player->handleMouseClick(1); + + // If the player has just exited a bed, then delay the time before a repeat key is allowed without releasing + if(wasSleeping) player->lastClickTick[1] = ticks + (timer->ticksPerSecond * 2); + else player->lastClickTick[1] = ticks; + } + } + else + { + player->lastClickTick[1] = 0; + } + } + } + + if(app.DebugSettingsOn()) + { + if (player->ullButtonsPressed & ( 1LL << MINECRAFT_ACTION_CHANGE_SKIN) ) + { + player->ChangePlayerSkin(); + } + } + + if (player->missTime > 0) player->missTime--; + +#ifdef _DEBUG_MENUS_ENABLED + if(app.DebugSettingsOn()) + { +#ifndef __PSVITA__ + // 4J-PB - debugoverlay for primary player only + if(iPad==ProfileManager.GetPrimaryPad()) + { + if((player->ullButtonsPressed&(1LL<renderDebug = !options->renderDebug; +#ifdef _XBOX + app.EnableDebugOverlay(options->renderDebug,iPad); +#else + // 4J Stu - The xbox uses a completely different way of navigating to this scene + ui.NavigateToScene(0, eUIScene_DebugOverlay, NULL, eUILayer_Debug); +#endif +#endif + } + + if((player->ullButtonsPressed&(1LL< mob = dynamic_pointer_cast(Creeper::_class->newInstance( level )); + //shared_ptr mob = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + shared_ptr mob = dynamic_pointer_cast(shared_ptr(new Spider( level ))); + mob->moveTo(player->x+1, player->y, player->z+1, level->random->nextFloat() * 360, 0); + level->addEntity(mob); + } + } + + if( (player->ullButtonsPressed&(1LL<abilities.debugflying = !player->abilities.debugflying; + player->abilities.flying = !player->abilities.flying; + } +#endif // PSVITA + } +#endif + + if((player->ullButtonsPressed&(1LL<isInputAllowed(MINECRAFT_ACTION_RENDER_THIRD_PERSON)) + { + // 4J-PB - changing this to be per player + player->SetThirdPersonView((player->ThirdPersonView()+1)%3); + //options->thirdPersonView = !options->thirdPersonView; + } + + if((player->ullButtonsPressed&(1LL<isInputAllowed(MINECRAFT_ACTION_GAME_INFO)) + { + ui.NavigateToScene(iPad,eUIScene_InGameInfoMenu); + ui.PlayUISFX(eSFX_Press); + } + + if((player->ullButtonsPressed&(1LL<isInputAllowed(MINECRAFT_ACTION_INVENTORY)) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->player ); + ui.PlayUISFX(eSFX_Press); + app.LoadInventoryMenu(iPad,player); + } + + if((player->ullButtonsPressed&(1LL<isInputAllowed(MINECRAFT_ACTION_CRAFTING)) + { + shared_ptr player = dynamic_pointer_cast( Minecraft::GetInstance()->player ); + + // 4J-PB - reordered the if statement so creative mode doesn't bring up the crafting table + // Fix for #39014 - TU5: Creative Mode: Pressing X to access the creative menu while looking at a crafting table causes the crafting menu to display + if(gameMode->hasInfiniteItems()) + { + // Creative mode + + ui.PlayUISFX(eSFX_Press); + app.LoadCreativeMenu(iPad,player); + } + // 4J-PB - Microsoft request that we use the 3x3 crafting if someone presses X while at the workbench + else if ((hitResult!=NULL) && (hitResult->type == HitResult::TILE) && (level->getTile(hitResult->x, hitResult->y, hitResult->z) == Tile::workBench_Id)) + { + //ui.PlayUISFX(eSFX_Press); + //app.LoadXuiCrafting3x3Menu(iPad,player,hitResult->x, hitResult->y, hitResult->z); + bool usedItem = false; + gameMode->useItemOn(player, level, nullptr, hitResult->x, hitResult->y, hitResult->z, 0, hitResult->pos, false, &usedItem); + } + else + { + ui.PlayUISFX(eSFX_Press); + app.LoadCrafting2x2Menu(iPad,player); + } + } + + if ( (player->ullButtonsPressed&(1LL<ullButtonsPressed&(1LL<GetXboxPad()); + ui.PlayUISFX(eSFX_Press); + ui.NavigateToScene(iPad, eUIScene_PauseMenu, NULL, eUILayer_Scene); + } + + if((player->ullButtonsPressed&(1LL<isInputAllowed(MINECRAFT_ACTION_DROP)) + { + player->drop(); + } + + __uint64 ullButtonsPressed=player->ullButtonsPressed; + + bool selected = false; +#ifdef __PSVITA__ + // 4J-PB - use the touchscreen for quickselect + SceTouchData* pTouchData = InputManager.GetTouchPadData(iPad,false); + + if(pTouchData->reportNum==1) + { + int iHudSize=app.GetGameSettings(iPad,eGameSetting_UISize); + if((pTouchData->report[0].x>QuickSelectRect[iHudSize].left)&&(pTouchData->report[0].xreport[0].y>QuickSelectRect[iHudSize].top)&&(pTouchData->report[0].yinventory->selected=(pTouchData->report[0].x-QuickSelectRect[iHudSize].left)/QuickSelectBoxWidth[iHudSize]; + selected = true; + app.DebugPrintf("Touch %d\n",player->inventory->selected); + } + } +#endif + if( selected || wheel != 0 || (player->ullButtonsPressed&(1LL< selectedItem = player->getSelectedItem(); + // Dropping items happens over network, so if we only have one then assume that we dropped it and should hide the item + int iCount=0; + + if(selectedItem != NULL) iCount=selectedItem->GetCount(); + if(selectedItem != NULL && !( (player->ullButtonsPressed&(1LL<GetCount() == 1)) + { + itemName = selectedItem->getHoverName(); + } + if( !(player->ullButtonsPressed&(1LL<GetCount() <= 1) ) ui.SetSelectedItem( iPad, itemName ); + } + } + else + { + // 4J-PB + if (InputManager.GetValue(iPad, ACTION_MENU_CANCEL) > 0 && gameMode->isInputAllowed(ACTION_MENU_CANCEL)) + { + setScreen(NULL); + } + } + + // monitor for keyboard input + // #ifndef _CONTENT_PACKAGE + // if(!(ui.GetMenuDisplayed(iPad))) + // { + // WCHAR wchInput; + // if(InputManager.InputDetected(iPad,&wchInput)) + // { + // printf("Input Detected!\n"); + // + // // see if we can react to this + // if(app.GetXuiAction(iPad)==eAppAction_Idle) + // { + // app.SetAction(iPad,eAppAction_DebugText,(LPVOID)wchInput); + // } + // } + // } + // #endif + +#if 0 + // 4J - TODO - some replacement for input handling... + if (screen == NULL || screen.passEvents) + { + while (Mouse.next()) + { + long passedTime = System.currentTimeMillis() - lastTickTime; + if (passedTime > 200) continue; + + int wheel = Mouse.getEventDWheel(); + if (wheel != 0) { + player->inventory.swapPaint(wheel); + + if (options.isFlying) { + if (wheel > 0) wheel = 1; + if (wheel < 0) wheel = -1; + + options.flySpeed += wheel * .25f; + } + } + + if (screen == null) { + if (!mouseGrabbed && Mouse.getEventButtonState()) { + grabMouse(); + } else { + if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState()) { + handleMouseClick(0); + lastClickTick = ticks; + } + if (Mouse.getEventButton() == 1 && Mouse.getEventButtonState()) { + handleMouseClick(1); + lastClickTick = ticks; + } + if (Mouse.getEventButton() == 2 && Mouse.getEventButtonState()) { + handleGrabTexture(); + } + } + } else if (screen != null) { + screen.mouseEvent(); + } + } + + if (missTime > 0) missTime--; + + while (Keyboard.next()) { + player->setKey(Keyboard.getEventKey(), Keyboard.getEventKeyState()); + if (Keyboard.getEventKeyState()) { + if (Keyboard.getEventKey() == Keyboard.KEY_F11) { + toggleFullScreen(); + continue; + } + /* + * if (Keyboard.getEventKey() == Keyboard.KEY_F4) { new + * PortalForcer().createPortal(level, player); continue; } + */ + + /* + * if (Keyboard.getEventKey() == Keyboard.KEY_RETURN) { + * level.pathFind(); continue; } + */ + + if (screen != null) { + screen.keyboardEvent(); + } else { + if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) { + pauseGame(); + } + + if (Keyboard.getEventKey() == Keyboard.KEY_S && Keyboard.isKeyDown(Keyboard.KEY_F3)) { + reloadSound(); + } + + // if (Keyboard.getEventKey() == Keyboard.KEY_P) { + // gameMode = new DemoMode(this); + // selectLevel(CreateWorldScreen.findAvailableFolderName(getLevelSource(), "Demo"), "Demo World", 0L); + // setScreen(null); + // + // } + + if (Keyboard.getEventKey() == Keyboard.KEY_F1) { + options.hideGui = !options.hideGui; + } + if (Keyboard.getEventKey() == Keyboard.KEY_F3) { + options.renderDebug = !options.renderDebug; + } + if (Keyboard.getEventKey() == Keyboard.KEY_F5) { + options.thirdPersonView = !options.thirdPersonView; + } + if (Keyboard.getEventKey() == Keyboard.KEY_F8) { + options.smoothCamera = !options.smoothCamera; + } + if (DEADMAU5_CAMERA_CHEATS) { + if (Keyboard.getEventKey() == Keyboard.KEY_F6) { + options.isFlying = !options.isFlying; + } + if (Keyboard.getEventKey() == Keyboard.KEY_F9) { + options.fixedCamera = !options.fixedCamera; + } + if (Keyboard.getEventKey() == Keyboard.KEY_ADD) { + options.cameraSpeed += .1f; + } + if (Keyboard.getEventKey() == Keyboard.KEY_SUBTRACT) { + options.cameraSpeed -= .1f; + if (options.cameraSpeed < 0) { + options.cameraSpeed = 0; + } + } + } + + if (Keyboard.getEventKey() == options.keyBuild.key) { + setScreen(new InventoryScreen(player)); + } + + if (Keyboard.getEventKey() == options.keyDrop.key) { + player->drop(); + } + if (isClientSide() && Keyboard.getEventKey() == options.keyChat.key) { + setScreen(new ChatScreen()); + } + } + + for (int i = 0; i < 9; i++) { + if (Keyboard.getEventKey() == Keyboard.KEY_1 + i) player->inventory.selected = i; + } + if (Keyboard.getEventKey() == options.keyFog.key) { + options.toggle(Options.Option.RENDER_DISTANCE, Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) ? -1 : 1); + } + } + } + + if (screen == null) { + if (Mouse.isButtonDown(0) && ticks - lastClickTick >= timer.ticksPerSecond / 4 && mouseGrabbed) { + handleMouseClick(0); + lastClickTick = ticks; + } + if (Mouse.isButtonDown(1) && ticks - lastClickTick >= timer.ticksPerSecond / 4 && mouseGrabbed) { + handleMouseClick(1); + lastClickTick = ticks; + } + } + + handleMouseDown(0, screen == null && Mouse.isButtonDown(0) && mouseGrabbed); + } +#endif + + if (level != NULL) + { + if (player != NULL) + { + recheckPlayerIn++; + if (recheckPlayerIn == 30) + { + recheckPlayerIn = 0; + level->ensureAdded(player); + } + } + // 4J Changed - We are setting the difficulty the same as the server so that leaderboard updates work correctly + //level->difficulty = options->difficulty; + //if (level->isClientSide) level->difficulty = Difficulty::HARD; + if( !level->isClientSide ) + { + //app.DebugPrintf("Minecraft::tick - Difficulty = %d",options->difficulty); + level->difficulty = options->difficulty; + } + + PIXBeginNamedEvent(0,"Game renderer tick"); + if (!pause) gameRenderer->tick( bFirst); + PIXEndNamedEvent(); + + // 4J - we want to tick each level once only per frame, and do it when a player that is actually in that level happens to be active. + // This is important as things that get called in the level tick (eg the levellistener) eventually end up working out what the current + // level is by determing it from the current player. Use flags here to make sure each level is only ticked the once. + static unsigned int levelsTickedFlags; + if( bFirst ) + { + levelsTickedFlags = 0; + +#ifndef DISABLE_LEVELTICK_THREAD + PIXBeginNamedEvent(0,"levelTickEventQueue waitForFinish"); + levelTickEventQueue->waitForFinish(); + PIXEndNamedEvent(); +#endif // DISABLE_LEVELTICK_THREAD + SparseLightStorage::tick(); // 4J added + CompressedTileStorage::tick(); // 4J added + SparseDataStorage::tick(); // 4J added + } + + for(unsigned int i = 0; i < levels.length; ++i) + { + if( player->level != levels[i] ) continue; // Don't tick if the current player isn't in this level + + // 4J - this doesn't fully tick the animateTick here, but does register this player's position. The actual + // work is now done in Level::animateTickDoWork() so we can take into account multiple players in the one level. + if (!pause && levels[i] != NULL) levels[i]->animateTick(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); + + if( levelsTickedFlags & ( 1 << i ) ) continue; // Don't tick further if we've already ticked this level this frame + levelsTickedFlags |= (1 << i); + + PIXBeginNamedEvent(0,"Level renderer tick"); + if (!pause) levelRenderer->tick(); + PIXEndNamedEvent(); + // if (!pause && player!=null) { + // if (player != null && !level.entities.contains(player)) { + // level.addEntity(player); + // } + // } + if( levels[i] != NULL ) + { + if (!pause) + { + if (levels[i]->lightningBoltTime > 0) levels[i]->lightningBoltTime--; + PIXBeginNamedEvent(0,"Level entity tick"); + levels[i]->tickEntities(); + PIXEndNamedEvent(); + } + + // optimisation to set the culling off early, in parallel with other stuff +#if defined __PS3__ && !defined DISABLE_SPU_CODE + // kick off the culling for all valid players in this level + int currPlayerIdx = getLocalPlayerIdx(); + for( int idx = 0; idx < XUSER_MAX_COUNT; idx++ ) + { + if(localplayers[idx]!=NULL) + { + if( localplayers[idx]->level == levels[i] ) + { + setLocalPlayerIdx(idx); + gameRenderer->setupCamera(timer->a, i); + Camera::prepare(localplayers[idx], localplayers[idx]->ThirdPersonView() == 2); + shared_ptr cameraEntity = cameraTargetPlayer; + double xOff = cameraEntity->xOld + (cameraEntity->x - cameraEntity->xOld) * timer->a; + double yOff = cameraEntity->yOld + (cameraEntity->y - cameraEntity->yOld) * timer->a; + double zOff = cameraEntity->zOld + (cameraEntity->z - cameraEntity->zOld) * timer->a; + FrustumCuller frustObj; + Culler *frustum = &frustObj; + MemSect(0); + frustum->prepare(xOff, yOff, zOff); + levelRenderer->cull_SPU(idx, frustum, 0); + } + } + } + setLocalPlayerIdx(currPlayerIdx); +#endif // __PS3__ + + // 4J Stu - We are always online, but still could be paused + if (!pause) // || isClientSide()) + { + //app.DebugPrintf("Minecraft::tick spawn settings - Difficulty = %d",options->difficulty); + levels[i]->setSpawnSettings(level->difficulty > 0, true); + PIXBeginNamedEvent(0,"Level tick"); +#ifdef DISABLE_LEVELTICK_THREAD + levels[i]->tick(); +#else + levelTickEventQueue->sendEvent(levels[i]); +#endif // DISABLE_LEVELTICK_THREAD + PIXEndNamedEvent(); + } + } + } + + if( bFirst ) + { + PIXBeginNamedEvent(0,"Particle tick"); + if (!pause) particleEngine->tick(); + PIXEndNamedEvent(); + } + + // 4J Stu - Keep ticking the connections if paused so that they don't time out + if( pause ) tickAllConnections(); + // player->tick(); + } +#ifdef __PS3__ + +// while(!g_tickLevelQueue.empty()) +// { +// Level* pLevel = g_tickLevelQueue.front(); +// g_tickLevelQueue.pop(); +// pLevel->tick(); +// }; + +#endif + + // if (Keyboard.isKeyDown(Keyboard.KEY_NUMPAD7) || + // Keyboard.isKeyDown(Keyboard.KEY_Q)) rota++; + // if (Keyboard.isKeyDown(Keyboard.KEY_NUMPAD9) || + // Keyboard.isKeyDown(Keyboard.KEY_E)) rota--; + // 4J removed + //lastTickTime = System::currentTimeMillis(); +} + +void Minecraft::reloadSound() +{ + // System.out.println("FORCING RELOAD!"); // 4J - removed + soundEngine = new SoundEngine(); + soundEngine->init(options); + bgLoader->forceReload(); +} + +bool Minecraft::isClientSide() +{ + return level != NULL && level->isClientSide; +} + +void Minecraft::selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, const wstring& levelName, LevelSettings *levelSettings) +{ + } + +bool Minecraft::saveSlot(int slot, const wstring& name) +{ + return false; +} + +bool Minecraft::loadSlot(const wstring& userName, int slot) +{ + return false; +} + +void Minecraft::releaseLevel(int message) +{ + //this->level = NULL; + setLevel(NULL, message); +} + +// 4J Stu - This code was within setLevel, but I moved it out so that I can call it at a better +// time when exiting from an online game +void Minecraft::forceStatsSave(int idx) +{ + //4J Gordon: Force a stats save + stats[idx]->save(idx, true); + + //4J Gordon: If the player is signed in, save the leaderboards + if( ProfileManager.IsSignedInLive(idx) ) + { + int tempLockedProfile = ProfileManager.GetLockedProfile(); + ProfileManager.SetLockedProfile(idx); + stats[idx]->saveLeaderboards(); + ProfileManager.SetLockedProfile(tempLockedProfile); + } +} + +// 4J Added +MultiPlayerLevel *Minecraft::getLevel(int dimension) +{ + if (dimension == -1) return levels[1]; + else if(dimension == 1) return levels[2]; + else return levels[0]; +} + +// 4J Stu - Removed as redundant with default values in params. +//void Minecraft::setLevel(Level *level, bool doForceStatsSave /*= true*/) +//{ +// setLevel(level, -1, NULL, doForceStatsSave); +//} + +// Also causing ambiguous call for some reason +// as it is matching shared_ptr from the func below with bool from this one +//void Minecraft::setLevel(Level *level, const wstring& message, bool doForceStatsSave /*= true*/) +//{ +// setLevel(level, message, NULL, doForceStatsSave); +//} + +void Minecraft::forceaddLevel(MultiPlayerLevel *level) +{ + int dimId = level->dimension->id; + if (dimId == -1) levels[1] = level; + else if(dimId == 1) levels[2] = level; + else levels[0] = level; +} + +void Minecraft::setLevel(MultiPlayerLevel *level, int message /*=-1*/, shared_ptr forceInsertPlayer /*=NULL*/, bool doForceStatsSave /*=true*/, bool bPrimaryPlayerSignedOut /*=false*/) +{ + EnterCriticalSection(&m_setLevelCS); + bool playerAdded = false; + this->cameraTargetPlayer = nullptr; + + if(progressRenderer != NULL) + { + this->progressRenderer->progressStart(message); + this->progressRenderer->progressStage(-1); + } + + // 4J-PB - since we now play music in the menu, just let it keep playing + //soundEngine->playStreaming(L"", 0, 0, 0, 0, 0); + + // 4J - stop update thread from processing this level, which blocks until it is safe to move on - will be re-enabled if we set the level to be non-NULL + gameRenderer->DisableUpdateThread(); + + for(unsigned int i = 0; i < levels.length; ++i) + { + // 4J We only need to save out in multiplayer is we are setting the level to NULL + // If we ever go back to making single player only then this will not work properly! + if (levels[i] != NULL && level == NULL) + { + // 4J Stu - This is really only relevant for single player (ie not what we do at the moment) + if((doForceStatsSave==true) && player!=NULL) + forceStatsSave(player->GetXboxPad() ); + + // 4J Stu - Added these for the case when we exit a level so we are setting the level to NULL + // The level renderer needs to have it's stored level set to NULL so that it doesn't break next time we set one + if (levelRenderer != NULL) + { + for(DWORD p = 0; p < XUSER_MAX_COUNT; ++p) + { + levelRenderer->setLevel(p, NULL); + } + } + if (particleEngine != NULL) particleEngine->setLevel(NULL); + } + } + // 4J If we are setting the level to NULL then we are exiting, so delete the levels + if( level == NULL ) + { + if(levels[0]!=NULL) + { + delete levels[0]; + levels[0] = NULL; + + // Both level share the same savedDataStorage + if(levels[1]!=NULL) levels[1]->savedDataStorage = NULL; + } + if(levels[1]!=NULL) + { + delete levels[1]; + levels[1] = NULL; + } + if(levels[2]!=NULL) + { + delete levels[2]; + levels[2] = NULL; + } + + // Delete all the player objects + for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + shared_ptr mplp = localplayers[idx]; + if(mplp != NULL && mplp->connection != NULL ) + { + delete mplp->connection; + mplp->connection = NULL; + } + + if( localgameModes[idx] != NULL ) + { + delete localgameModes[idx]; + localgameModes[idx] = NULL; + } + + if( m_pendingLocalConnections[idx] != NULL ) + { + delete m_pendingLocalConnections[idx]; + m_pendingLocalConnections[idx] = NULL; + } + + localplayers[idx] = nullptr; + } + // If we are removing the primary player then there can't be a valid gamemode left anymore, this + // pointer will be referring to the one we've just deleted + gameMode = NULL; + // Remove references to player + player = nullptr; + cameraTargetPlayer = nullptr; + EntityRenderDispatcher::instance->cameraEntity = nullptr; + TileEntityRenderDispatcher::instance->cameraEntity = nullptr; + } + this->level = level; + + if (level != NULL) + { + int dimId = level->dimension->id; + if (dimId == -1) levels[1] = level; + else if(dimId == 1) levels[2] = level; + else levels[0] = level; + + // If no player has been set, then this is the first level to be set this game, so set up + // a primary player & initialise some other things + if (player == NULL) + { + int iPrimaryPlayer = ProfileManager.GetPrimaryPad(); + + player = gameMode->createPlayer(level); + + PlayerUID playerXUIDOffline = INVALID_XUID; + PlayerUID playerXUIDOnline = INVALID_XUID; + ProfileManager.GetXUID(iPrimaryPlayer,&playerXUIDOffline,false); + ProfileManager.GetXUID(iPrimaryPlayer,&playerXUIDOnline,true); +#ifdef __PSVITA__ + if(CGameNetworkManager::usingAdhocMode() && playerXUIDOnline.getOnlineID()[0] == 0) + { + // player doesn't have an online UID, set it from the player name + playerXUIDOnline.setForAdhoc(); + } +#endif + player->setXuid(playerXUIDOffline); + player->setOnlineXuid(playerXUIDOnline); + + player->displayName = ProfileManager.GetDisplayName(iPrimaryPlayer); + + + + player->resetPos(); + gameMode->initPlayer(player); + + player->SetXboxPad(iPrimaryPlayer); + + for(int i=0;iresetPos(); + // gameMode.initPlayer(player); + if (level != NULL) + { + level->addEntity(player); + playerAdded = true; + } + } + + if(player->input != NULL) delete player->input; + player->input = new Input(); + + if (levelRenderer != NULL) levelRenderer->setLevel(player->GetXboxPad(), level); + if (particleEngine != NULL) particleEngine->setLevel(level); + +#if 0 + // 4J - removed - we don't use ChunkCache anymore + ChunkSource *cs = level->getChunkSource(); + if (dynamic_cast(cs) != NULL) + { + ChunkCache *spcc = (ChunkCache *)cs; + + // 4J - these had a Mth::floor which seems unrequired + int xt = ((int) player->x) >> 4; + int zt = ((int) player->z) >> 4; + + spcc->centerOn(xt, zt); + } +#endif + gameMode->adjustPlayer(player); + + for(int i=0;icameraTargetPlayer = player; + + // 4J - allow update thread to start processing the level now both it & the player should be ok + gameRenderer->EnableUpdateThread(); + } + else + { + levelSource->clearAll(); + player = nullptr; + + // Clear all players if the new level is NULL + for(int i=0;iclose(); + m_pendingLocalConnections[i] = NULL; + localplayers[i] = nullptr; + localgameModes[i] = NULL; + } + } + + // System.gc(); // 4J - removed + // 4J removed + //this->lastTickTime = 0; + LeaveCriticalSection(&m_setLevelCS); +} + +void Minecraft::prepareLevel(int title) +{ + if(progressRenderer != NULL) + { + this->progressRenderer->progressStart(title); + this->progressRenderer->progressStage(IDS_PROGRESS_BUILDING_TERRAIN); + } + int r = 128; + if (gameMode->isCutScene()) r = 64; + int pp = 0; + int max = r * 2 / 16 + 1; + max = max * max; + ChunkSource *cs = level->getChunkSource(); + + Pos *spawnPos = level->getSharedSpawnPos(); + if (player != NULL) + { + spawnPos->x = (int) player->x; + spawnPos->z = (int) player->z; + } + +#if 0 + // 4J - removed - we don't use ChunkCache anymore + if (dynamic_cast(cs)!=NULL) + { + ChunkCache *spcc = (ChunkCache *) cs; + + spcc->centerOn(spawnPos->x >> 4, spawnPos->z >> 4); + } +#endif + + for (int x = -r; x <= r; x += 16) + { + for (int z = -r; z <= r; z += 16) + { + if(progressRenderer != NULL) this->progressRenderer->progressStagePercentage((pp++) * 100 / max); + level->getTile(spawnPos->x + x, 64, spawnPos->z + z); + if (!gameMode->isCutScene()) { + while (level->updateLights()) + ; + } + } + } + delete spawnPos; + if (!gameMode->isCutScene()) + { + if(progressRenderer != NULL) this->progressRenderer->progressStage(IDS_PROGRESS_SIMULATING_WORLD); + max = 2000; +} +} + +void Minecraft::fileDownloaded(const wstring& name, File *file) +{ + int p = (int)name.find(L"/"); + wstring category = name.substr(0, p); + wstring name2 = name.substr(p + 1); + toLower(category); + if (category==L"sound") + { + soundEngine->add(name, file); + } + else if (category==L"newsound") + { + soundEngine->add(name, file); + } + else if (category==L"streaming") + { + soundEngine->addStreaming(name, file); + } + else if (category==L"music") + { + soundEngine->addMusic(name, file); + } + else if (category==L"newmusic") + { + soundEngine->addMusic(name, file); + } +} + +wstring Minecraft::gatherStats1() +{ + //return levelRenderer->gatherStats1(); + return L"Time to autosave: " + _toString( app.SecondsToAutosave() ) + L"s"; +} + +wstring Minecraft::gatherStats2() +{ + return g_NetworkManager.GatherStats(); + //return levelRenderer->gatherStats2(); +} + +wstring Minecraft::gatherStats3() +{ + return g_NetworkManager.GatherRTTStats(); + //return L"P: " + particleEngine->countParticles() + L". T: " + level->gatherStats(); +} + +wstring Minecraft::gatherStats4() +{ + return level->gatherChunkSourceStats(); +} + +void Minecraft::respawnPlayer(int iPad, int dimension, int newEntityId) +{ + gameRenderer->DisableUpdateThread(); // 4J - don't do updating whilst we are adjusting the player & localplayer array + shared_ptr localPlayer = localplayers[iPad]; + + level->validateSpawn(); + level->removeAllPendingEntityRemovals(); + + if (localPlayer != NULL) + { + + level->removeEntity(localPlayer); + } + + shared_ptr oldPlayer = localPlayer; + cameraTargetPlayer = nullptr; + + // 4J-PB - copy and set the players xbox pad + int iTempPad=localPlayer->GetXboxPad(); + int iTempScreenSection = localPlayer->m_iScreenSection; + EDefaultSkins skin = localPlayer->getPlayerDefaultSkin(); + player = localgameModes[iPad]->createPlayer(level); + + PlayerUID playerXUIDOffline = INVALID_XUID; + PlayerUID playerXUIDOnline = INVALID_XUID; + ProfileManager.GetXUID(iTempPad,&playerXUIDOffline,false); + ProfileManager.GetXUID(iTempPad,&playerXUIDOnline,true); + player->setXuid(playerXUIDOffline); + player->setOnlineXuid(playerXUIDOnline); + player->setIsGuest( ProfileManager.IsGuest(iTempPad) ); + + player->displayName = ProfileManager.GetDisplayName(iPad); + + player->SetXboxPad(iTempPad); + + player->m_iScreenSection = iTempScreenSection; + player->setPlayerIndex( localPlayer->getPlayerIndex() ); + player->setCustomSkin(localPlayer->getCustomSkin()); + player->setPlayerDefaultSkin( skin ); + player->setCustomCape(localPlayer->getCustomCape()); + player->m_sessionTimeStart = localPlayer->m_sessionTimeStart; + player->m_dimensionTimeStart = localPlayer->m_dimensionTimeStart; + player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, localPlayer->getAllPlayerGamePrivileges()); + + player->SetThirdPersonView(oldPlayer->ThirdPersonView()); + + // Fix for #63021 - TU7: Content: UI: Travelling from/to the Nether results in switching currently held item to another. + // Fix for #81759 - TU9: Content: Gameplay: Entering The End Exit Portal replaces the Player's currently held item with the first one from the Quickbar + if( localPlayer->getHealth() > 0 && localPlayer->y > -64) + { + player->inventory->selected = localPlayer->inventory->selected; + } + + // Set the animation override if the skin has one + DWORD dwSkinID=app.getSkinIdFromPath(player->customTextureUrl); + if(GET_IS_DLC_SKIN_FROM_BITMASK(dwSkinID)) + { + player->setAnimOverrideBitmask(player->getSkinAnimOverrideBitmask(dwSkinID)); + } + + player->dimension = dimension; + cameraTargetPlayer = player; + + // 4J-PB - are we the primary player or a local player? + if(iPad==ProfileManager.GetPrimaryPad()) + { + createPrimaryLocalPlayer(iPad); + + // update the debugoptions + app.SetGameSettingsDebugMask(ProfileManager.GetPrimaryPad(),app.GetGameSettingsDebugMask(-1,true)); + } + else + { + storeExtraLocalPlayer(iPad); + } + + player->setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); + + player->resetPos(); + level->addEntity(player); + gameMode->initPlayer(player); + + if(player->input != NULL) delete player->input; + player->input = new Input(); + player->entityId = newEntityId; + player->animateRespawn(); + gameMode->adjustPlayer(player); + + // 4J - added isClientSide check here + if (!level->isClientSide) + { + prepareLevel(IDS_PROGRESS_RESPAWNING); + } + + // 4J Added for multiplayer. At this point we know everything is ready to run again + //SetEvent(m_hPlayerRespawned); + player->SetPlayerRespawned(true); + + if (dynamic_cast(screen) != NULL) setScreen(NULL); + + gameRenderer->EnableUpdateThread(); +} + +void Minecraft::start(const wstring& name, const wstring& sid) +{ + startAndConnectTo(name, sid, L""); +} + +void Minecraft::startAndConnectTo(const wstring& name, const wstring& sid, const wstring& url) +{ + bool fullScreen = false; + wstring userName = name; + + /* 4J - removed window handling things here + final Frame frame = new Frame("Minecraft"); + Canvas canvas = new Canvas(); + frame.setLayout(new BorderLayout()); + + frame.add(canvas, BorderLayout.CENTER); + + // OverlayLayout oll = new OverlayLayout(frame); + // oll.addLayoutComponent(canvas, BorderLayout.CENTER); + // oll.addLayoutComponent(new JLabel("TEST"), BorderLayout.EAST); + + canvas.setPreferredSize(new Dimension(854, 480)); + frame.pack(); + frame.setLocationRelativeTo(null); + */ + + Minecraft *minecraft; + // 4J - was new Minecraft(frame, canvas, NULL, 854, 480, fullScreen); + + minecraft = new Minecraft(NULL, NULL, NULL, 1280, 720, fullScreen); + + /* - 4J - removed + { + @Override + public void onCrash(CrashReport crashReport) { + frame.removeAll(); + frame.add(new CrashInfoPanel(crashReport), BorderLayout.CENTER); + frame.validate(); + } + }; */ + + /* 4J - removed + final Thread thread = new Thread(minecraft, "Minecraft main thread"); + thread.setPriority(Thread.MAX_PRIORITY); + */ + minecraft->serverDomain = L"www.minecraft.net"; + + // 4J Stu - We never want the player to be DemoUser, we always want them to have their gamertag displayed + //if (ProfileManager.IsFullVersion()) + { + if (userName != L"" && sid != L"") // 4J - username & side were compared with NULL rather than empty strings + { + minecraft->user = new User(userName, sid); + } + else + { + minecraft->user = new User(L"Player" + _toString(System::currentTimeMillis() % 1000), L""); + } + } + //else + //{ + // minecraft->user = new DemoUser(); + //} + + /* 4J - TODO + if (url != NULL) + { + String[] tokens = url.split(":"); + minecraft.connectTo(tokens[0], Integer.parseInt(tokens[1])); + } + */ + + /* 4J - removed + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent arg0) { + minecraft.stop(); + try { + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.exit(0); + } + }); + */ + // 4J - TODO - consider whether we need to actually create a thread here + minecraft->run(); +} + +ClientConnection *Minecraft::getConnection(int iPad) +{ + return localplayers[iPad]->connection; +} + +// 4J-PB - so we can access this from within our xbox game loop +Minecraft *Minecraft::GetInstance() +{ + return m_instance; +} + +bool useLomp = false; + +int g_iMainThreadId; + +void Minecraft::main() +{ + wstring name; + wstring sessionId; + + //g_iMainThreadId = GetCurrentThreadId(); + + useLomp = true; + + MinecraftWorld_RunStaticCtors(); + EntityRenderDispatcher::staticCtor(); + TileEntityRenderDispatcher::staticCtor(); + User::staticCtor(); + Tutorial::staticCtor(); + ColourTable::staticCtor(); + app.loadDefaultGameRules(); + +#ifdef _LARGE_WORLDS + LevelRenderer::staticCtor(); +#endif + + // 4J Stu - This block generates XML for the game rules schema + //for(unsigned int i = 0; i < Item::items.length; ++i) + //{ + // if(Item::items[i] != NULL) + // { + // wprintf(L"%ls\n", i, app.GetString( Item::items[i]->getDescriptionId() )); + // } + //} + + //wprintf(L"\n\n\n\n\n"); + // + //for(unsigned int i = 0; i < 256; ++i) + //{ + // if(Tile::tiles[i] != NULL) + // { + // wprintf(L"%ls\n", i, app.GetString( Tile::tiles[i]->getDescriptionId() )); + // } + //} + //__debugbreak(); + + // 4J-PB - Can't call this for the first 5 seconds of a game - MS rule + //if (ProfileManager.IsFullVersion()) + { + name = L"Player" + _toString<__int64>(System::currentTimeMillis() % 1000); + sessionId = L"-"; + /* 4J - TODO - get a session ID from somewhere? + if (args.length > 0) name = args[0]; + sessionId = "-"; + if (args.length > 1) sessionId = args[1]; + */ + } + + // Common for all platforms + IUIScene_CreativeMenu::staticCtor(); + + // On PS4, we call Minecraft::Start from another thread, as this has been timed taking ~2.5 seconds and we need to do some basic + // rendering stuff so that we don't break the TRCs on SubmitDone calls +#ifndef __ORBIS__ + Minecraft::start(name, sessionId); +#endif +} + +bool Minecraft::renderNames() +{ + if (m_instance == NULL || !m_instance->options->hideGui) + { + return true; + } + return false; +} + +bool Minecraft::useFancyGraphics() +{ + return (m_instance != NULL && m_instance->options->fancyGraphics); +} + +bool Minecraft::useAmbientOcclusion() +{ + return (m_instance != NULL && m_instance->options->ambientOcclusion); +} + +bool Minecraft::renderDebug() +{ + return (m_instance != NULL && m_instance->options->renderDebug); +} + +bool Minecraft::handleClientSideCommand(const wstring& chatMessage) +{ + /* 4J - TODO + if (chatMessage.startsWith("/")) { + if (DEADMAU5_CAMERA_CHEATS) { + if (chatMessage.startsWith("/follow")) { + String[] tokens = chatMessage.split(" "); + if (tokens.length >= 2) { + String playerName = tokens[1]; + + boolean found = false; + for (Player player : level.players) { + if (playerName.equalsIgnoreCase(player.name)) { + cameraTargetPlayer = player; + found = true; + break; + } + } + + if (!found) { + try { + int entityId = Integer.parseInt(playerName); + for (Entity e : level.entities) { + if (e.entityId == entityId && e instanceof Mob) { + cameraTargetPlayer = (Mob) e; + found = true; + break; + } + } + } catch (NumberFormatException e) { + } + } + } + + return true; + } + } + } + */ + return false; +} + +int Minecraft::maxSupportedTextureSize() +{ + // 4J Force value + return 1024; + + //for (int texSize = 16384; texSize > 0; texSize >>= 1) { + // GL11.glTexImage2D(GL11.GL_PROXY_TEXTURE_2D, 0, GL11.GL_RGBA, texSize, texSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); + // final int width = GL11.glGetTexLevelParameteri(GL11.GL_PROXY_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH); + // if (width != 0) { + // return texSize; + // } + //} + //return -1; +} + +void Minecraft::delayTextureReload() +{ + reloadTextures = true; +} + +__int64 Minecraft::currentTimeMillis() +{ + return System::currentTimeMillis();//(Sys.getTime() * 1000) / Sys.getTimerResolution(); +} + +/*void Minecraft::handleMouseDown(int button, bool down) +{ +if (gameMode->instaBuild) return; +if (!down) missTime = 0; +if (button == 0 && missTime > 0) return; + +if (down && hitResult != NULL && hitResult->type == HitResult::TILE && button == 0) +{ +int x = hitResult->x; +int y = hitResult->y; +int z = hitResult->z; +gameMode->continueDestroyBlock(x, y, z, hitResult->f); +particleEngine->crack(x, y, z, hitResult->f); +} +else +{ +gameMode->stopDestroyBlock(); +} +} + +void Minecraft::handleMouseClick(int button) +{ +if (button == 0 && missTime > 0) return; +if (button == 0) +{ +app.DebugPrintf("handleMouseClick - Player %d is swinging\n",player->GetXboxPad()); +player->swing(); +} + +bool mayUse = true; + +// * if (button == 1) { ItemInstance item = +// * player.inventory.getSelected(); if (item != null) { if +// * (gameMode.useItem(player, item)) { +// * gameRenderer.itemInHandRenderer.itemUsed(); return; } } } + +// 4J-PB - Adding a special case in here for sleeping in a bed in a multiplayer game - we need to wake up, and we don't have the inbedchatscreen with a button + +if(button==1 && (player->isSleeping() && level != NULL && level->isClientSide)) +{ +shared_ptr mplp = dynamic_pointer_cast( player ); + +if(mplp) mplp->StopSleeping(); + +// 4J - TODO +//if (minecraft.player instanceof MultiplayerLocalPlayer) +//{ +// ClientConnection connection = ((MultiplayerLocalPlayer) minecraft.player).connection; +// connection.send(new PlayerCommandPacket(minecraft.player, PlayerCommandPacket.STOP_SLEEPING)); +//} +} + +if (hitResult == NULL) +{ +if (button == 0 && !(dynamic_cast(gameMode) != NULL)) missTime = 10; +} +else if (hitResult->type == HitResult::ENTITY) +{ +if (button == 0) +{ +gameMode->attack(player, hitResult->entity); +} +if (button == 1) +{ +gameMode->interact(player, hitResult->entity); +} +} +else if (hitResult->type == HitResult::TILE) +{ +int x = hitResult->x; +int y = hitResult->y; +int z = hitResult->z; +int face = hitResult->f; + +// * if (button != 0) { if (hitResult.f == 0) y--; if (hitResult.f == +// * 1) y++; if (hitResult.f == 2) z--; if (hitResult.f == 3) z++; if +// * (hitResult.f == 4) x--; if (hitResult.f == 5) x++; } + +// if (isClientSide()) +// { +// return; +// } + +if (button == 0) +{ +gameMode->startDestroyBlock(x, y, z, hitResult->f); +} +else +{ +shared_ptr item = player->inventory->getSelected(); +int oldCount = item != NULL ? item->count : 0; +if (gameMode->useItemOn(player, level, item, x, y, z, face)) +{ +mayUse = false; +app.DebugPrintf("Player %d is swinging\n",player->GetXboxPad()); +player->swing(); +} +if (item == NULL) +{ +return; +} + +if (item->count == 0) +{ +player->inventory->items[player->inventory->selected] = NULL; +} +else if (item->count != oldCount) +{ +gameRenderer->itemInHandRenderer->itemPlaced(); +} +} +} + +if (mayUse && button == 1) +{ +shared_ptr item = player->inventory->getSelected(); +if (item != NULL) +{ +if (gameMode->useItem(player, level, item)) +{ +gameRenderer->itemInHandRenderer->itemUsed(); +} +} +} +} +*/ + +// 4J-PB +Screen * Minecraft::getScreen() +{ + return screen; +} + +bool Minecraft::isTutorial() +{ + return m_inFullTutorialBits > 0; + + /*if( gameMode != NULL && gameMode->isTutorial() ) + { + return true; + } + else + { + return false; + }*/ +} + +void Minecraft::playerStartedTutorial(int iPad) +{ + // If the app doesn't think we are in a tutorial mode then just ignore this add + if( app.GetTutorialMode() ) m_inFullTutorialBits = m_inFullTutorialBits | ( 1 << iPad ); +} + +void Minecraft::playerLeftTutorial(int iPad) +{ + // 4J Stu - Fix for bug that was flooding Sentient with LevelStart events + // If the tutorial bits are already 0 then don't need to update anything + if(m_inFullTutorialBits == 0) + { + app.SetTutorialMode( false ); + return; + } + + m_inFullTutorialBits = m_inFullTutorialBits & ~( 1 << iPad ); + if(m_inFullTutorialBits == 0) + { + app.SetTutorialMode( false ); + + // 4J Stu -This telemetry event means something different on XboxOne, so we don't call it for simple state changes like this +#ifndef _XBOX_ONE + for(DWORD idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + if(localplayers[idx] != NULL) + { + TelemetryManager->RecordLevelStart(idx, eSen_FriendOrMatch_Playing_With_Invited_Friends, eSen_CompeteOrCoop_Coop_and_Competitive, level->difficulty, app.GetLocalPlayerCount(), g_NetworkManager.GetOnlinePlayerCount()); + } + } +#endif + } +} + +#ifdef _DURANGO +void Minecraft::inGameSignInCheckAllPrivilegesCallback(LPVOID lpParam, bool hasPrivileges, int iPad) +{ + Minecraft* pClass = (Minecraft*)lpParam; + + if(!hasPrivileges) + { + ProfileManager.RemoveGamepadFromGame(iPad); + } + else + { + if( !g_NetworkManager.SessionHasSpace() ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_MULTIPLAYER_FULL_TITLE, IDS_MULTIPLAYER_FULL_TEXT, uiIDA, 1); + ProfileManager.RemoveGamepadFromGame(iPad); + } + else if( ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad) ) + { + // create the local player for the iPad + shared_ptr player = pClass->localplayers[iPad]; + if( player == NULL) + { + if( pClass->level->isClientSide ) + { + pClass->addLocalPlayer(iPad); + } + else + { + // create the local player for the iPad + shared_ptr player = pClass->localplayers[iPad]; + if( player == NULL) + { + player = pClass->createExtraLocalPlayer(iPad, (convStringToWstring( ProfileManager.GetGamertag(iPad) )).c_str(), iPad, pClass->level->dimension->id); + } + } + } + } + } +} +#endif + +int Minecraft::InGame_SignInReturned(void *pParam,bool bContinue, int iPad) +{ + Minecraft* pMinecraftClass = (Minecraft*)pParam; + + if(g_NetworkManager.IsInSession()) + { + // 4J Stu - There seems to be a bug in the signin ui call that enables guest sign in. We never allow this within game, so make sure that it's disabled + // Fix for #66516 - TCR #124: MPS Guest Support ; #001: BAS Game Stability: TU8: The game crashes when second Guest signs-in on console which takes part in Xbox LIVE multiplayer session. + app.DebugPrintf("Disabling Guest Signin\n"); + XEnableGuestSignin(FALSE); + } + + // If sign in succeded, we're in game and this player isn't already playing, continue + if(bContinue==true && g_NetworkManager.IsInSession() && pMinecraftClass->localplayers[iPad] == NULL) + { + // It's possible that the player has not signed in - they can back out or choose no for the converttoguest + if(ProfileManager.IsSignedIn(iPad)) + { +#ifdef _DURANGO + if(!g_NetworkManager.IsLocalGame() && ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad)) + { + ProfileManager.CheckMultiplayerPrivileges(iPad, true, &inGameSignInCheckAllPrivilegesCallback, pMinecraftClass); + } + else +#endif + if( !g_NetworkManager.SessionHasSpace() ) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_OK; + ui.RequestMessageBox(IDS_MULTIPLAYER_FULL_TITLE, IDS_MULTIPLAYER_FULL_TEXT, uiIDA, 1); +#ifdef _DURANGO + ProfileManager.RemoveGamepadFromGame(iPad); +#endif + } + // if this is a local game then profiles just need to be signed in + else if( g_NetworkManager.IsLocalGame() || (ProfileManager.IsSignedInLive(iPad) && ProfileManager.AllowedToPlayMultiplayer(iPad)) ) + { +#ifdef __ORBIS__ + bool contentRestricted = false; + ProfileManager.GetChatAndContentRestrictions(iPad,false,NULL,&contentRestricted,NULL); // TODO! + + if (!g_NetworkManager.IsLocalGame() && contentRestricted) + { + ui.RequestContentRestrictedMessageBox(IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_CONTENT_RESTRICTION, iPad); + } + else if(!g_NetworkManager.IsLocalGame() && !ProfileManager.HasPlayStationPlus(iPad)) + { + pMinecraftClass->m_pPsPlusUpsell = new PsPlusUpsellWrapper(iPad); + pMinecraftClass->m_pPsPlusUpsell->displayUpsell(); + } + else +#endif + if( pMinecraftClass->level->isClientSide ) + { + pMinecraftClass->addLocalPlayer(iPad); + } + else + { + // create the local player for the iPad + shared_ptr player = pMinecraftClass->localplayers[iPad]; + if( player == NULL) + { + player = pMinecraftClass->createExtraLocalPlayer(iPad, (convStringToWstring( ProfileManager.GetGamertag(iPad) )).c_str(), iPad, pMinecraftClass->level->dimension->id); + } + } + } + else if( ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && !ProfileManager.AllowedToPlayMultiplayer(iPad) ) + { + // 4J Stu - Don't allow converting to guests as we don't allow any guest sign-in while in the game + // Fix for #66516 - TCR #124: MPS Guest Support ; #001: BAS Game Stability: TU8: The game crashes when second Guest signs-in on console which takes part in Xbox LIVE multiplayer session. + //ProfileManager.RequestConvertOfflineToGuestUI( &Minecraft::InGame_SignInReturned, pMinecraftClass,iPad); + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_NO_MULTIPLAYER_PRIVILEGE_TITLE, IDS_NO_MULTIPLAYER_PRIVILEGE_JOIN_TEXT, uiIDA,1,iPad,NULL,NULL, app.GetStringTable()); +#ifdef _DURANGO + ProfileManager.RemoveGamepadFromGame(iPad); +#endif + } + } + } + return 0; +} + +void Minecraft::tickAllConnections() +{ + int oldIdx = getLocalPlayerIdx(); + for(unsigned int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + shared_ptr mplp = localplayers[i]; + if( mplp && mplp->connection) + { + setLocalPlayerIdx(i); + mplp->connection->tick(); + } + } + setLocalPlayerIdx(oldIdx); +} + +bool Minecraft::addPendingClientTextureRequest(const wstring &textureName) +{ + AUTO_VAR(it, find( m_pendingTextureRequests.begin(), m_pendingTextureRequests.end(), textureName)); + if( it == m_pendingTextureRequests.end() ) + { + m_pendingTextureRequests.push_back(textureName); + return true; + } + return false; +} + +void Minecraft::handleClientTextureReceived(const wstring &textureName) +{ + AUTO_VAR(it, find( m_pendingTextureRequests.begin(), m_pendingTextureRequests.end(), textureName)); + if( it != m_pendingTextureRequests.end() ) + { + m_pendingTextureRequests.erase(it); + } +} + +unsigned int Minecraft::getCurrentTexturePackId() +{ + return skins->getSelected()->getId(); +} + +ColourTable *Minecraft::getColourTable() +{ + TexturePack *selected = skins->getSelected(); + + ColourTable *colours = selected->getColourTable(); + + if(colours == NULL) + { + colours = skins->getDefault()->getColourTable(); + } + + return colours; +} + +#if defined __ORBIS__ +int Minecraft::MustSignInReturnedPSN(void *pParam, int iPad, C4JStorage::EMessageResult result) +{ + Minecraft* pMinecraft = (Minecraft *)pParam; + + if(result == C4JStorage::EMessage_ResultAccept) + { + SQRNetworkManager_Orbis::AttemptPSNSignIn(&Minecraft::InGame_SignInReturned, pMinecraft, false, iPad); + } + + return 0; +} +#endif + diff --git a/Minecraft.Client/Minecraft.h b/Minecraft.Client/Minecraft.h new file mode 100644 index 0000000..3076030 --- /dev/null +++ b/Minecraft.Client/Minecraft.h @@ -0,0 +1,347 @@ +#pragma once +class Timer; +class MultiPlayerLevel; +class LevelRenderer; +class MultiplayerLocalPlayer; +class Player; +class Mob; +class ParticleEngine; +class User; +class Canvas; +class Textures; +class Font; +class Screen; +class ProgressRenderer; +class GameRenderer; +class BackgroundDownloader; +class HumanoidModel; +class HitResult; +class Options; +class SoundEngine; +class MinecraftApplet; +class MouseHandler; +class TexturePackRepository; +class File; +class LevelStorageSource; +class StatsCounter; +class Component; +class Entity; +class AchievementPopup; +class WaterTexture; +class LavaTexture; +class Gui; +class ClientConnection; +class ConsoleSaveFile; +class ItemInHandRenderer; +class LevelSettings; +class ColourTable; +class MultiPlayerGameMode; +class PsPlusUpsellWrapper; + +#include "..\Minecraft.World\File.h" +#include "..\Minecraft.World\DisconnectPacket.h" +#include "..\Minecraft.World\C4JThread.h" + +using namespace std; + +class Minecraft +{ +private: + enum OS{ + linux, solaris, windows, macos, unknown, xbox + }; + +public: + static const wstring VERSION_STRING; + Minecraft(Component *mouseComponent, Canvas *parent, MinecraftApplet *minecraftApplet, int width, int height, bool fullscreen); + void init(); + + // 4J - removed + // void crash(CrashReport crash); + // public abstract void onCrash(CrashReport crash); + +private: + static Minecraft *m_instance; + +public: + MultiPlayerGameMode *gameMode; + +private: + bool fullscreen; + bool hasCrashed; + + C4JThread::EventQueue* levelTickEventQueue; + + static void levelTickUpdateFunc(void* pParam); + static void levelTickThreadInitFunc(); + +public: + int width, height; + int width_phys, height_phys; // 4J - added + // private OpenGLCapabilities openGLCapabilities; + +private: + Timer *timer; + bool reloadTextures; +public: + Level *oldLevel; // 4J Stu added to keep a handle on an old level so we can delete it + //HANDLE m_hPlayerRespawned; // 4J Added so we can wait in menus until it is done (for async in multiplayer) +public: + + MultiPlayerLevel *level; + LevelRenderer *levelRenderer; + shared_ptr player; + + MultiPlayerLevelArray levels; + + shared_ptr localplayers[XUSER_MAX_COUNT]; + MultiPlayerGameMode *localgameModes[XUSER_MAX_COUNT]; + int localPlayerIdx; + ItemInHandRenderer *localitemInHandRenderers[XUSER_MAX_COUNT]; + // 4J-PB - so we can have debugoptions in the server + unsigned int uiDebugOptionsA[XUSER_MAX_COUNT]; + + // 4J Stu - Added these so that we can show a Xui scene while connecting + bool m_connectionFailed[XUSER_MAX_COUNT]; + DisconnectPacket::eDisconnectReason m_connectionFailedReason[XUSER_MAX_COUNT]; + ClientConnection *m_pendingLocalConnections[XUSER_MAX_COUNT]; + + bool addLocalPlayer(int idx); // Re-arrange the screen and start the connection + void addPendingLocalConnection(int idx, ClientConnection *connection); + void connectionDisconnected(int idx, DisconnectPacket::eDisconnectReason reason) { m_connectionFailed[idx] = true; m_connectionFailedReason[idx] = reason; } + + shared_ptr createExtraLocalPlayer(int idx, const wstring& name, int pad, int iDimension, ClientConnection *clientConnection = NULL,MultiPlayerLevel *levelpassedin=NULL); + void createPrimaryLocalPlayer(int iPad); + bool setLocalPlayerIdx(int idx); + int getLocalPlayerIdx(); + void removeLocalPlayerIdx(int idx); + void storeExtraLocalPlayer(int idx); + void updatePlayerViewportAssignments(); + int unoccupiedQuadrant; // 4J - added + + shared_ptr cameraTargetPlayer; + ParticleEngine *particleEngine; + User *user; + wstring serverDomain; + Canvas *parent; + bool appletMode; + + // 4J - per player ? + volatile bool pause; + + Textures *textures; + Font *font, *altFont; + Screen *screen; + ProgressRenderer *progressRenderer; + GameRenderer *gameRenderer; +private: + BackgroundDownloader *bgLoader; + + int ticks; + // 4J-PB - moved to per player + + //int missTime; + + int orgWidth, orgHeight; +public: + AchievementPopup *achievementPopup; +public: + Gui *gui; + // 4J - move to the per player structure? + bool noRender; + + HumanoidModel *humanoidModel; + HitResult *hitResult; + Options *options; +protected: + MinecraftApplet *minecraftApplet; +public: + SoundEngine *soundEngine; + MouseHandler *mouseHandler; +public: + TexturePackRepository *skins; + File workingDirectory; +private: + LevelStorageSource *levelSource; +public: + static const int frameTimes_length = 512; + static __int64 frameTimes[frameTimes_length]; + static const int tickTimes_length = 512; + static __int64 tickTimes[tickTimes_length]; + static int frameTimePos; + static __int64 warezTime; +private: + int rightClickDelay; +public: + // 4J- this should really be in localplayer + StatsCounter* stats[4]; + +private: + wstring connectToIp; + int connectToPort; + +public: + void clearConnectionFailed(); + void connectTo(const wstring& server, int port); + +private: + void renderLoadingScreen(); + +public: + void blit(int x, int y, int sx, int sy, int w, int h); + +private: + static File workDir; + +public: + static File getWorkingDirectory(); + static File getWorkingDirectory(const wstring& applicationName); +private: + static OS getPlatform(); +public: + LevelStorageSource *getLevelSource(); + void setScreen(Screen *screen); +private: + void checkGlError(const wstring& string); + +#ifdef __ORBIS__ + PsPlusUpsellWrapper *m_pPsPlusUpsell; +#endif + +public: + void destroy(); + volatile bool running; + wstring fpsString; + void run(); + // 4J-PB - split the run into 3 parts so we can run it from our xbox game loop + static Minecraft *GetInstance(); + void run_middle(); + void run_end(); + + void emergencySave(); + + // 4J - removed + //bool wasDown ; +private: + // void checkScreenshot(); // 4J - removed + // String grabHugeScreenshot(File workDir2, int width, int height, int ssWidth, int ssHeight); // 4J - removed + + // 4J - per player thing? + __int64 lastTimer; + + void renderFpsMeter(__int64 tickTime); +public: + void stop(); + // 4J removed + // bool mouseGrabbed; + // void grabMouse(); + // void releaseMouse(); + // 4J-PB - moved these into localplayer + //void handleMouseDown(int button, bool down); + //void handleMouseClick(int button); + + void pauseGame(); + // void toggleFullScreen(); // 4J - removed +private: + void resize(int width, int height); + +public: + // 4J - Moved to per player + //bool isRaining ; + + // 4J - Moved to per player + //__int64 lastTickTime; + +private: + // 4J- per player? + int recheckPlayerIn; + void verify(); + +public: + // 4J - added bFirst parameter, which is true for the first active viewport in splitscreen + // 4J - added bUpdateTextures, which is true if the actual renderer textures are to be updated - this will be true for the last time this tick runs with bFirst true + void tick(bool bFirst, bool bUpdateTextures); +private: + void reloadSound(); +public: + bool isClientSide(); + void selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, const wstring& levelName, LevelSettings *levelSettings); + //void toggleDimension(int targetDimension); + bool saveSlot(int slot, const wstring& name); + bool loadSlot(const wstring& userName, int slot); + void releaseLevel(int message); + // 4J Stu - Added the doForceStatsSave param + //void setLevel(Level *level, bool doForceStatsSave = true); + //void setLevel(Level *level, const wstring& message, bool doForceStatsSave = true); + void setLevel(MultiPlayerLevel *level, int message = -1, shared_ptr forceInsertPlayer = nullptr, bool doForceStatsSave = true,bool bPrimaryPlayerSignedOut=false); + // 4J-PB - added to force in the 'other' level when the main player creates the level at game load time + void forceaddLevel(MultiPlayerLevel *level); + void prepareLevel(int title); // 4J - changed to public + void fileDownloaded(const wstring& name, File *file); + // OpenGLCapabilities getOpenGLCapabilities(); // 4J - removed + + wstring gatherStats1(); + wstring gatherStats2(); + wstring gatherStats3(); + wstring gatherStats4(); + + void respawnPlayer(int iPad,int dimension,int newEntityId); + static void start(const wstring& name, const wstring& sid); + static void startAndConnectTo(const wstring& name, const wstring& sid, const wstring& url); + ClientConnection *getConnection(int iPad); // 4J Stu added iPad param + static void main(); + static bool renderNames(); + static bool useFancyGraphics(); + static bool useAmbientOcclusion(); + static bool renderDebug(); + bool handleClientSideCommand(const wstring& chatMessage); + + static int maxSupportedTextureSize(); + void delayTextureReload(); + static __int64 currentTimeMillis(); + +#ifdef _DURANGO + static void inGameSignInCheckAllPrivilegesCallback(LPVOID lpParam, bool hasPrivileges, int iPad); +#endif + static int InGame_SignInReturned(void *pParam,bool bContinue, int iPad); + // 4J-PB + Screen * getScreen(); + + // 4J Stu + void forceStatsSave(int idx); + + CRITICAL_SECTION m_setLevelCS; +private: + // A bit field that store whether a particular quadrant is in the full tutorial or not + BYTE m_inFullTutorialBits; +public: + bool isTutorial(); + void playerStartedTutorial(int iPad); + void playerLeftTutorial(int iPad); + + // 4J Added + MultiPlayerLevel *getLevel(int dimension); + + void tickAllConnections(); + + Level *animateTickLevel; // 4J added + + // 4J - When a client requests a texture, it should add it to here while we are waiting for it + vector m_pendingTextureRequests; + vector m_pendingGeometryRequests; // additional skin box geometry + + // 4J Added + bool addPendingClientTextureRequest(const wstring &textureName); + void handleClientTextureReceived(const wstring &textureName); + void clearPendingClientTextureRequests() { m_pendingTextureRequests.clear(); } + bool addPendingClientGeometryRequest(const wstring &textureName); + void handleClientGeometryReceived(const wstring &textureName); + void clearPendingClientGeometryRequests() { m_pendingGeometryRequests.clear(); } + + unsigned int getCurrentTexturePackId(); + ColourTable *getColourTable(); + +#if defined __ORBIS__ + static int MustSignInReturnedPSN(void *pParam, int iPad, C4JStorage::EMessageResult result); +#endif +}; diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp new file mode 100644 index 0000000..ceb9554 --- /dev/null +++ b/Minecraft.Client/MinecraftServer.cpp @@ -0,0 +1,1685 @@ +#include "stdafx.h" +//#include "Minecraft.h" + +#include + +#include "Options.h" +#include "MinecraftServer.h" +#include "ConsoleInput.h" +#include "PlayerList.h" +#include "ServerLevel.h" +#include "DerivedServerLevel.h" +#include "EntityTracker.h" +#include "ServerConnection.h" +#include "Settings.h" +#include "ServerChunkCache.h" +#include "ServerLevelListener.h" +#include "..\Minecraft.World\AABB.h" +#include "..\Minecraft.World\Vec3.h" +#include "..\Minecraft.World\net.minecraft.network.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\Minecraft.World\net.minecraft.world.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\Pos.h" +#include "..\Minecraft.World\System.h" +#include "..\Minecraft.World\StringHelpers.h" +#ifdef SPLIT_SAVES +#include "..\Minecraft.World\ConsoleSaveFileSplit.h" +#endif +#include "..\Minecraft.World\ConsoleSaveFileOriginal.h" +#include "..\Minecraft.World\Socket.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "ProgressRenderer.h" +#include "ServerPlayer.h" +#include "GameRenderer.h" +#include "..\Minecraft.World\ThreadName.h" +#include "..\Minecraft.World\IntCache.h" +#include "..\Minecraft.World\CompressedTileStorage.h" +#include "..\Minecraft.World\SparseLightStorage.h" +#include "..\Minecraft.World\SparseDataStorage.h" +#include "..\Minecraft.World\compression.h" +#ifdef _XBOX +#include "Common\XUI\XUI_DebugSetCamera.h" +#endif +#include "PS3\PS3Extras\ShutdownManager.h" +#include "ServerCommandDispatcher.h" +#include "..\Minecraft.World\BiomeSource.h" +#include "PlayerChunkMap.h" +#include "Common\Telemetry\TelemetryManager.h" + +#define DEBUG_SERVER_DONT_SPAWN_MOBS 0 + +//4J Added +MinecraftServer *MinecraftServer::server = NULL; +bool MinecraftServer::setTimeAtEndOfTick = false; +__int64 MinecraftServer::setTime = 0; +bool MinecraftServer::setTimeOfDayAtEndOfTick = false; +__int64 MinecraftServer::setTimeOfDay = 0; +bool MinecraftServer::m_bPrimaryPlayerSignedOut=false; +bool MinecraftServer::s_bServerHalted=false; +bool MinecraftServer::s_bSaveOnExitAnswered=false; +int MinecraftServer::s_slowQueuePlayerIndex = 0; +int MinecraftServer::s_slowQueueLastTime = 0; +bool MinecraftServer::s_slowQueuePacketSent = false; + +unordered_map MinecraftServer::ironTimers; + +MinecraftServer::MinecraftServer() +{ + // 4J - added initialisers + connection = NULL; + settings = NULL; + players = NULL; + commands = NULL; + running = true; + m_bLoaded = false; + stopped = false; + tickCount = 0; + wstring progressStatus; + progress = 0; + motd = L""; + + m_isServerPaused = false; + m_serverPausedEvent = new C4JThread::Event; + + m_saveOnExit = false; + m_suspending = false; + + m_ugcPlayersVersion = 0; + m_texturePackId = 0; + maxBuildHeight = Level::maxBuildHeight; + m_postUpdateThread = NULL; + + commandDispatcher = new ServerCommandDispatcher(); +} + +MinecraftServer::~MinecraftServer() +{ +} + +bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed) +{ + // 4J - removed +#if 0 + commands = new ConsoleCommands(this); + + Thread t = new Thread() { + public void run() { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + String line = null; + try { + while (!stopped && running && (line = br.readLine()) != null) { + handleConsoleInput(line, MinecraftServer.this); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + t.setDaemon(true); + t.start(); + + + LogConfigurator.initLogger(); + logger.info("Starting minecraft server version " + VERSION); + + if (Runtime.getRuntime().maxMemory() / 1024 / 1024 < 512) { + logger.warning("**** NOT ENOUGH RAM!"); + logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + } + + logger.info("Loading properties"); +#endif + settings = new Settings(new File(L"server.properties")); + + app.DebugPrintf("\n*** SERVER SETTINGS ***\n"); + app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); + app.DebugPrintf("ServerSettings: game-type is %s\n",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode"); + app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off"); + app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off"); + app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off"); + app.DebugPrintf("\n"); + + // TODO 4J Stu - Init a load of settings based on data passed as params + //settings->setBooleanAndSave( L"host-friends-only", (app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0) ); + + // 4J - Unused + //localIp = settings->getString(L"server-ip", L""); + //onlineMode = settings->getBoolean(L"online-mode", true); + //motd = settings->getString(L"motd", L"A Minecraft Server"); + //motd.replace('', '$'); + + setAnimals(settings->getBoolean(L"spawn-animals", true)); + setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true)); + setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // settings->getBoolean(L"pvp", true); + + // 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always + // Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite + setFlightAllowed(true); //settings->getBoolean(L"allow-flight", false); + + // 4J Stu - Enabling flight to stop it kicking us when we use it +#ifdef _DEBUG_MENUS_ENABLED + setFlightAllowed(true); +#endif + +#if 1 + connection = new ServerConnection(this); + Socket::Initialise(connection); // 4J - added +#else + // 4J - removed + InetAddress localAddress = null; + if (localIp.length() > 0) localAddress = InetAddress.getByName(localIp); + port = settings.getInt("server-port", DEFAULT_MINECRAFT_PORT); + + logger.info("Starting Minecraft server on " + (localIp.length() == 0 ? "*" : localIp) + ":" + port); + try { + connection = new ServerConnection(this, localAddress, port); + } catch (IOException e) { + logger.warning("**** FAILED TO BIND TO PORT!"); + logger.log(Level.WARNING, "The exception was: " + e.toString()); + logger.warning("Perhaps a server is already running on that port?"); + return false; + } + + if (!onlineMode) { + logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); + logger.warning("The server will make no attempt to authenticate usernames. Beware."); + logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose."); + logger.warning("To change this, set \"online-mode\" to \"true\" in the server.settings file."); + } +#endif + setPlayers(new PlayerList(this)); + + // 4J-JEV: Need to wait for levelGenerationOptions to load. + while ( app.getLevelGenerationOptions() != NULL && !app.getLevelGenerationOptions()->hasLoadedData() ) + Sleep(1); + + if ( app.getLevelGenerationOptions() != NULL && !app.getLevelGenerationOptions()->ready() ) + { + // TODO: Stop loading, add error message. + } + + __int64 levelNanoTime = System::nanoTime(); + + wstring levelName = settings->getString(L"level-name", L"world"); + wstring levelTypeString; + + bool gameRuleUseFlatWorld = false; + if(app.getLevelGenerationOptions() != NULL) + { + gameRuleUseFlatWorld = app.getLevelGenerationOptions()->getuseFlatWorld(); + } + if(gameRuleUseFlatWorld || app.GetGameHostOption(eGameHostOption_LevelType)>0) + { + levelTypeString = settings->getString(L"level-type", L"flat"); + } + else + { + levelTypeString = settings->getString(L"level-type",L"default"); + } + + LevelType *pLevelType = LevelType::getLevelType(levelTypeString); + if (pLevelType == NULL) + { + pLevelType = LevelType::lvl_normal; + } + + ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer; + mcprogress->progressStart(IDS_PROGRESS_INITIALISING_SERVER); + + if( findSeed ) + { +#ifdef __PSVITA__ + seed = BiomeSource::findSeed(pLevelType, &running); +#else + seed = BiomeSource::findSeed(pLevelType); +#endif + } + + setMaxBuildHeight(settings->getInt(L"max-build-height", Level::maxBuildHeight)); + setMaxBuildHeight(((getMaxBuildHeight() + 8) / 16) * 16); + setMaxBuildHeight(Mth::clamp(getMaxBuildHeight(), 64, Level::maxBuildHeight)); + //settings->setProperty(L"max-build-height", maxBuildHeight); + +#if 0 + wstring levelSeedString = settings->getString(L"level-seed", L""); + __int64 levelSeed = (new Random())->nextLong(); + if (levelSeedString.length() > 0) + { + long newSeed = _fromString<__int64>(levelSeedString); + if (newSeed != 0) { + levelSeed = newSeed; + } + } +#endif +// logger.info("Preparing level \"" + levelName + "\""); + m_bLoaded = loadLevel(new McRegionLevelStorageSource(File(L".")), levelName, seed, pLevelType, initData); +// logger.info("Done (" + (System.nanoTime() - levelNanoTime) + "ns)! For help, type \"help\" or \"?\""); + + // 4J delete passed in save data now - this is only required for the tutorial which is loaded by passing data directly in rather than using the storage manager + if( initData->saveData ) + { + delete initData->saveData->data; + initData->saveData->data = 0; + initData->saveData->fileSize = 0; + } + + g_NetworkManager.ServerReady(); // 4J added + return m_bLoaded; + +} + +// 4J - added - extra thread to post processing on separate thread during level creation +int MinecraftServer::runPostUpdate(void* lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::ePostProcessThread); + + MinecraftServer *server = (MinecraftServer *)lpParam; + Entity::useSmallIds(); // This thread can end up spawning entities as resources + IntCache::CreateNewThreadStorage(); + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + Compression::UseDefaultThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + // Update lights for both levels until we are signalled to terminate + do + { + EnterCriticalSection(&server->m_postProcessCS); + if( server->m_postProcessRequests.size() ) + { + MinecraftServer::postProcessRequest request = server->m_postProcessRequests.back(); + server->m_postProcessRequests.pop_back(); + LeaveCriticalSection(&server->m_postProcessCS); + static int count = 0; + PIXBeginNamedEvent(0,"Post processing %d ", (count++)%8); + request.chunkSource->postProcess(request.chunkSource, request.x, request.z ); + PIXEndNamedEvent(); + } + else + { + LeaveCriticalSection(&server->m_postProcessCS); + } + Sleep(1); + } while (!server->m_postUpdateTerminate && ShutdownManager::ShouldRun(ShutdownManager::ePostProcessThread)); +//#ifndef __PS3__ + // One final pass through updates to make sure we're done + EnterCriticalSection(&server->m_postProcessCS); + int maxRequests = server->m_postProcessRequests.size(); + while(server->m_postProcessRequests.size() && ShutdownManager::ShouldRun(ShutdownManager::ePostProcessThread) ) + { + MinecraftServer::postProcessRequest request = server->m_postProcessRequests.back(); + server->m_postProcessRequests.pop_back(); + LeaveCriticalSection(&server->m_postProcessCS); + request.chunkSource->postProcess(request.chunkSource, request.x, request.z ); +#ifdef __PS3__ +#ifndef _CONTENT_PACKAGE + if((server->m_postProcessRequests.size() % 10) == 0) + printf("processing request %00d\n", server->m_postProcessRequests.size()); +#endif + Sleep(1); +#endif + EnterCriticalSection(&server->m_postProcessCS); + } + LeaveCriticalSection(&server->m_postProcessCS); +//#endif //__PS3__ + Tile::ReleaseThreadStorage(); + IntCache::ReleaseThreadStorage(); + AABB::ReleaseThreadStorage(); + Vec3::ReleaseThreadStorage(); + Level::destroyLightingCache(); + + ShutdownManager::HasFinished(ShutdownManager::ePostProcessThread); + + return 0; +} + +void MinecraftServer::addPostProcessRequest(ChunkSource *chunkSource, int x, int z) +{ + EnterCriticalSection(&m_postProcessCS); + m_postProcessRequests.push_back(MinecraftServer::postProcessRequest(x,z,chunkSource)); + LeaveCriticalSection(&m_postProcessCS); +} + +void MinecraftServer::postProcessTerminate(ProgressRenderer *mcprogress) +{ + DWORD status = 0; + + EnterCriticalSection(&server->m_postProcessCS); + size_t postProcessItemCount = server->m_postProcessRequests.size(); + LeaveCriticalSection(&server->m_postProcessCS); + + do + { + status = m_postUpdateThread->WaitForCompletion(50); + if( status == WAIT_TIMEOUT ) + { + EnterCriticalSection(&server->m_postProcessCS); + size_t postProcessItemRemaining = server->m_postProcessRequests.size(); + LeaveCriticalSection(&server->m_postProcessCS); + + if( postProcessItemCount ) + { + mcprogress->progressStagePercentage((postProcessItemCount - postProcessItemRemaining) * 100 / postProcessItemCount); + } + CompressedTileStorage::tick(); + SparseLightStorage::tick(); + SparseDataStorage::tick(); + } + } while ( status == WAIT_TIMEOUT ); + delete m_postUpdateThread; + m_postUpdateThread = NULL; + DeleteCriticalSection(&m_postProcessCS); +} + +bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring& name, __int64 levelSeed, LevelType *pLevelType, NetworkGameInitData *initData) +{ +// 4J - TODO - do with new save stuff +// if (storageSource->requiresConversion(name)) +// { +// assert(false); +// } + ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer; + + // 4J TODO - free levels here if there are already some? + levels = ServerLevelArray(3); + + int gameTypeId = settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL); + GameType *gameType = LevelSettings::validateGameType(gameTypeId); + app.DebugPrintf("Default game type: %d\n" , gameTypeId); + + LevelSettings *levelSettings = new LevelSettings(levelSeed, gameType, app.GetGameHostOption(eGameHostOption_Structures)>0?true:false, isHardcore(), true, pLevelType, initData->xzSize, initData->hellScale); + if( app.GetGameHostOption(eGameHostOption_BonusChest ) ) levelSettings->enableStartingBonusItems(); + + // 4J - temp - load existing level + shared_ptr storage = nullptr; + bool levelChunksNeedConverted = false; + if( initData->saveData != NULL ) + { + // We are loading a file from disk with the data passed in + +#ifdef SPLIT_SAVES + ConsoleSaveFileOriginal oldFormatSave( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform ); + ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( &oldFormatSave ); + + //ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform ); +#else + ConsoleSaveFile* pSave = new ConsoleSaveFileOriginal( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform ); +#endif + if(pSave->isSaveEndianDifferent()) + levelChunksNeedConverted = true; + pSave->ConvertToLocalPlatform(); // check if we need to convert this file from PS3->PS4 + + storage = shared_ptr(new McRegionLevelStorage(pSave, File(L"."), name, true)); + } + else + { + // We are loading a save from the storage manager +#ifdef SPLIT_SAVES + bool bLevelGenBaseSave = false; + LevelGenerationOptions *levelGen = app.getLevelGenerationOptions(); + if( levelGen != NULL && levelGen->requiresBaseSave()) + { + DWORD fileSize = 0; + LPVOID pvSaveData = levelGen->getBaseSaveData(fileSize); + if(pvSaveData && fileSize != 0) bLevelGenBaseSave = true; + } + ConsoleSaveFileSplit *newFormatSave = NULL; + if(bLevelGenBaseSave) + { + ConsoleSaveFileOriginal oldFormatSave( L"" ); + newFormatSave = new ConsoleSaveFileSplit( &oldFormatSave ); + } + else + { + newFormatSave = new ConsoleSaveFileSplit( L"" ); + } + + storage = shared_ptr(new McRegionLevelStorage(newFormatSave, File(L"."), name, true)); +#else + storage = shared_ptr(new McRegionLevelStorage(new ConsoleSaveFileOriginal( L"" ), File(L"."), name, true)); +#endif + } + +// McRegionLevelStorage *storage = new McRegionLevelStorage(new ConsoleSaveFile( L"" ), L"", L"", 0); // original +// McRegionLevelStorage *storage = new McRegionLevelStorage(File(L"."), name, true); // TODO + for (unsigned int i = 0; i < levels.length; i++) + { + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) + { + return false; + } + +// String levelName = name; +// if (i == 1) levelName += "_nether"; + int dimension = 0; + if (i == 1) dimension = -1; + if (i == 2) dimension = 1; + if (i == 0) + { + levels[i] = new ServerLevel(this, storage, name, dimension, levelSettings); + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *mapOptions = app.getLevelGenerationOptions(); + Pos *spawnPos = mapOptions->getSpawnPos(); + if( spawnPos != NULL ) + { + levels[i]->setSpawnPos( spawnPos ); + } + + levels[i]->getLevelData()->setHasBeenInCreative(mapOptions->isFromDLC()); + } + } + else levels[i] = new DerivedServerLevel(this, storage, name, dimension, levelSettings, levels[0]); +// levels[i]->addListener(new ServerLevelListener(this, levels[i])); // 4J - have moved this to the ServerLevel ctor so that it is set up in time for the first chunk to load, which might actually happen there + + // 4J Stu - We set the levels difficulty based on the minecraft options + //levels[i]->difficulty = settings->getBoolean(L"spawn-monsters", true) ? Difficulty::EASY : Difficulty::PEACEFUL; + Minecraft *pMinecraft = Minecraft::GetInstance(); +// m_lastSentDifficulty = pMinecraft->options->difficulty; + levels[i]->difficulty = app.GetGameHostOption(eGameHostOption_Difficulty); //pMinecraft->options->difficulty; + app.DebugPrintf("MinecraftServer::loadLevel - Difficulty = %d\n",levels[i]->difficulty); + +#if DEBUG_SERVER_DONT_SPAWN_MOBS + levels[i]->setSpawnSettings(false, false); +#else + levels[i]->setSpawnSettings(settings->getBoolean(L"spawn-monsters", true), animals); +#endif + levels[i]->getLevelData()->setGameType(gameType); + players->setLevel(levels); + } + + if( levels[0]->isNew ) + { + mcprogress->progressStage(IDS_PROGRESS_GENERATING_SPAWN_AREA); + } + else + { + mcprogress->progressStage(IDS_PROGRESS_LOADING_SPAWN_AREA); + } + app.SetGameHostOption( eGameHostOption_HasBeenInCreative, gameType == GameType::CREATIVE || levels[0]->getHasBeenInCreative() ); + app.SetGameHostOption( eGameHostOption_Structures, levels[0]->isGenerateMapFeatures() ); + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + + // 4J - Make a new thread to do post processing + InitializeCriticalSection(&m_postProcessCS); + + // 4J-PB - fix for 108310 - TCR #001 BAS Game Stability: TU12: Code: Compliance: Crash after creating world on "journey" seed. + // Stack gets very deep with some sand tower falling, so increased the stacj to 256K from 128k on other platforms (was already set to that on PS3 and Orbis) + + m_postUpdateThread = new C4JThread(runPostUpdate, this, "Post processing", 256*1024); + + m_postUpdateTerminate = false; + m_postUpdateThread->SetProcessor(CPU_CORE_POST_PROCESSING); + m_postUpdateThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); + m_postUpdateThread->Run(); + + __int64 startTime = System::currentTimeMillis(); + + // 4J Stu - Added this to temporarily make starting games on vita faster +#ifdef __PSVITA__ + int r = 48; +#else + int r = 196; +#endif + + // 4J JEV: load gameRules. + ConsoleSavePath filepath(GAME_RULE_SAVENAME); + ConsoleSaveFile *csf = getLevel(0)->getLevelStorage()->getSaveFile(); + if( csf->doesFileExist(filepath) ) + { + DWORD numberOfBytesRead; + byteArray ba_gameRules; + + FileEntry *fe = csf->createFile(filepath); + + ba_gameRules.length = fe->getFileSize(); + ba_gameRules.data = new BYTE[ ba_gameRules.length ]; + + csf->setFilePointer(fe,0,NULL,FILE_BEGIN); + csf->readFile(fe, ba_gameRules.data, ba_gameRules.length, &numberOfBytesRead); + assert(numberOfBytesRead == ba_gameRules.length); + + app.m_gameRules.loadGameRules(ba_gameRules.data, ba_gameRules.length); + csf->closeHandle(fe); + } + + __int64 lastTime = System::currentTimeMillis(); + + // 4J Stu - This loop is changed in 1.0.1 to only process the first level (ie the overworld), but I think we still want to do them all + int i = 0; + for (int i = 0; i < levels.length ; i++) + { +// logger.info("Preparing start region for level " + i); + if (i == 0 || settings->getBoolean(L"allow-nether", true)) + { + ServerLevel *level = levels[i]; + if(levelChunksNeedConverted) + { +// storage->getSaveFile()->convertLevelChunks(level) + } + +#if 0 + __int64 lastStorageTickTime = System::currentTimeMillis(); + + // Test code to enable full creation of levels at start up + int halfsidelen = ( i == 0 ) ? 27 : 9; + for( int x = -halfsidelen; x < halfsidelen; x++ ) + { + for( int z = -halfsidelen; z < halfsidelen; z++ ) + { + int total = halfsidelen * halfsidelen * 4; + int pos = z + halfsidelen + ( ( x + halfsidelen ) * 2 * halfsidelen ); + mcprogress->progressStagePercentage((pos) * 100 / total); + level->cache->create(x,z, true); // 4J - added parameter to disable postprocessing here + + if( System::currentTimeMillis() - lastStorageTickTime > 50 ) + { + CompressedTileStorage::tick(); + SparseLightStorage::tick(); + SparseDataStorage::tick(); + lastStorageTickTime = System::currentTimeMillis(); + } + } + } +#else + __int64 lastStorageTickTime = System::currentTimeMillis(); + Pos *spawnPos = level->getSharedSpawnPos(); + + int twoRPlusOne = r*2 + 1; + int total = twoRPlusOne * twoRPlusOne; + for (int x = -r; x <= r && running; x += 16) + { + for (int z = -r; z <= r && running; z += 16) + { + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) + { + delete spawnPos; + m_postUpdateTerminate = true; + postProcessTerminate(mcprogress); + return false; + } +// printf(">>>%d %d %d\n",i,x,z); +// __int64 now = System::currentTimeMillis(); +// if (now < lastTime) lastTime = now; +// if (now > lastTime + 1000) + { + int pos = (x + r) * twoRPlusOne + (z + 1); +// setProgress(L"Preparing spawn area", (pos) * 100 / total); + mcprogress->progressStagePercentage((pos+r) * 100 / total); +// lastTime = now; + } + static int count = 0; + PIXBeginNamedEvent(0,"Creating %d ", (count++)%8); + level->cache->create((spawnPos->x + x) >> 4, (spawnPos->z + z) >> 4, true); // 4J - added parameter to disable postprocessing here + PIXEndNamedEvent(); +// while (level->updateLights() && running) +// ; + if( System::currentTimeMillis() - lastStorageTickTime > 50 ) + { + CompressedTileStorage::tick(); + SparseLightStorage::tick(); + SparseDataStorage::tick(); + lastStorageTickTime = System::currentTimeMillis(); + } + } + } + + // 4J - removed this as now doing the recheckGaps call when each chunk is post-processed, so can happen on things outside of the spawn area too +#if 0 + // 4J - added this code to propagate lighting properly in the spawn area before we go sharing it with the local client or across the network + for (int x = -r; x <= r && running; x += 16) + { + for (int z = -r; z <= r && running; z += 16) + { + PIXBeginNamedEvent(0,"Lighting gaps for %d %d",x,z); + level->getChunkAt(spawnPos->x + x, spawnPos->z + z)->recheckGaps(true); + PIXEndNamedEvent(); + } + } +#endif + + delete spawnPos; +#endif + } + } +// printf("Main thread complete at %dms\n",System::currentTimeMillis() - startTime); + + // Wait for post processing, then lighting threads, to end (post-processing may make more lighting changes) + m_postUpdateTerminate = true; + + postProcessTerminate(mcprogress); + + + // stronghold position? + if(levels[0]->dimension->id==0) + { + + app.DebugPrintf("===================================\n"); + + if(!levels[0]->getLevelData()->getHasStronghold()) + { + int x,z; + if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z)) + { + levels[0]->getLevelData()->setXStronghold(x); + levels[0]->getLevelData()->setZStronghold(z); + levels[0]->getLevelData()->setHasStronghold(); + + app.DebugPrintf("=== FOUND stronghold in terrain features list\n"); + + } + else + { + // can't find the stronghold position in the terrain feature list. Do we have to run a post-process? + app.DebugPrintf("=== Can't find stronghold in terrain features list\n"); + } + } + else + { + app.DebugPrintf("=== Leveldata has stronghold position\n"); + } + app.DebugPrintf("===================================\n"); + } + +// printf("Post processing complete at %dms\n",System::currentTimeMillis() - startTime); + +// printf("Lighting complete at %dms\n",System::currentTimeMillis() - startTime); + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + + if( levels[1]->isNew ) + { + levels[1]->save(true, mcprogress); + } + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + + if( levels[2]->isNew ) + { + levels[2]->save(true, mcprogress); + } + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + + // 4J - added - immediately save newly created level, like single player game + // 4J Stu - We also want to immediately save the tutorial + if ( levels[0]->isNew ) + saveGameRules(); + + if( levels[0]->isNew ) + { + levels[0]->save(true, mcprogress); + } + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + + if( levels[0]->isNew || levels[1]->isNew || levels[2]->isNew ) + { + levels[0]->saveToDisc(mcprogress, false); + } + + if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false; + +/* +* int r = 24; for (int x = -r; x <= r; x++) { +* setProgress("Preparing spawn area", (x + r) * 100 / (r + r + 1)); for (int z +* = -r; z <= r; z++) { if (!running) return; level.cache.create((level.xSpawn +* >> 4) + x, (level.zSpawn >> 4) + z); while (running && level.updateLights()) +* ; } } +*/ + endProgress(); + + return true; +} + +void MinecraftServer::setProgress(const wstring& status, int progress) +{ + progressStatus = status; + this->progress = progress; +// logger.info(status + ": " + progress + "%"); +} + +void MinecraftServer::endProgress() +{ + progressStatus = L""; + this->progress = 0; +} + +void MinecraftServer::saveAllChunks() +{ +// logger.info("Saving chunks"); + for (unsigned int i = 0; i < levels.length; i++) + { + // 4J Stu - Due to the way save mounting is handled on XboxOne, we can actually save after the player has signed out. +#ifndef _XBOX_ONE + if( m_bPrimaryPlayerSignedOut ) break; +#endif + // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat + // with the data from the nethers leveldata. + // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. + ServerLevel *level = levels[levels.length - 1 - i]; + if( level ) // 4J - added check as level can be NULL if we end up in stopServer really early on due to network failure + { + level->save(true, Minecraft::GetInstance()->progressRenderer); + + // Only close the level storage when we have saved the last level, otherwise we need to recreate the region files + // when saving the next levels + if( i == (levels.length - 1)) + { + level->closeLevelStorage(); + } + } + } +} + +// 4J-JEV: Added +void MinecraftServer::saveGameRules() +{ +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getLevelStorage()->getSaveFile(); + FileEntry *fe = csf->createFile(ConsoleSavePath(GAME_RULE_SAVENAME)); + csf->setFilePointer(fe, 0, NULL, FILE_BEGIN); + DWORD length; + csf->writeFile(fe, ba.data, ba.length, &length ); + + delete [] ba.data; + + csf->closeHandle(fe); + } + } +} + +void MinecraftServer::Suspend() +{ + PIXBeginNamedEvent(0,"Suspending server"); + m_suspending = true; + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime; + float fElapsedTime = 0.0f; + QueryPerformanceFrequency( &qwTicksPerSec ); + float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + // Save the start time + QueryPerformanceCounter( &qwTime ); + if(m_bLoaded && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled())) + { + if (players != NULL) + { + players->saveAll(NULL); + } + for (unsigned int j = 0; j < levels.length; j++) + { + if( s_bServerHalted ) break; + // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat + // with the data from the nethers leveldata. + // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. + ServerLevel *level = levels[levels.length - 1 - j]; + level->Suspend(); + } + if( !s_bServerHalted ) + { + saveGameRules(); + levels[0]->saveToDisc(NULL, true); + } + } + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + // 4J-JEV: Flush stats and call PlayerSessionExit. + for (int iPad = 0; iPad < XUSER_MAX_COUNT; iPad++) + { + if (ProfileManager.IsSignedIn(iPad)) + { + TelemetryManager->RecordPlayerSessionExit(iPad, DisconnectPacket::eDisconnect_Quitting); + } + } + + m_suspending = false; + app.DebugPrintf("Suspend server: Elapsed time %f\n", fElapsedTime); + PIXEndNamedEvent(); +} + +bool MinecraftServer::IsSuspending() +{ + return m_suspending; +} + +void MinecraftServer::stopServer() +{ + + // 4J-PB - need to halt the rendering of the data, since we're about to remove it +#ifdef __PS3__ + if( ShutdownManager::ShouldRun(ShutdownManager::eServerThread ) ) // This thread will take itself out if we are shutting down +#endif + { + Minecraft::GetInstance()->gameRenderer->DisableUpdateThread(); + } + + connection->stop(); + + app.DebugPrintf("Stopping server\n"); +// logger.info("Stopping server"); + // 4J-PB - If the primary player has signed out, then don't attempt to save anything + + // also need to check for a profile switch here - primary player signs out, and another player signs in before dismissing the dash +#ifdef _DURANGO + // On Durango check if the primary user is signed in OR mid-sign-out + if(ProfileManager.GetUser(0, true) != nullptr) +#else + if((m_bPrimaryPlayerSignedOut==false) && ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad())) +#endif + { +#if defined(_XBOX_ONE) || defined(__ORBIS__) + // Always save on exit! Except if saves are disabled. + if(!saveOnExitAnswered()) m_saveOnExit = true; +#endif + // if trial version or saving is disabled, then don't save anything + if(m_saveOnExit && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled())) + { + if (players != NULL) + { + players->saveAll(Minecraft::GetInstance()->progressRenderer, true); + } + // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat + // with the data from the nethers leveldata. + // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. + //for (unsigned int i = levels.length - 1; i >= 0; i--) + //{ + // ServerLevel *level = levels[i]; + // if (level != NULL) + // { + saveAllChunks(); + // } + //} + + saveGameRules(); + app.m_gameRules.unloadCurrentGameRules(); + if( levels[0] != NULL ) // This can be null if stopServer happens very quickly due to network error + { + levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, false); + } + } + } + // reset the primary player signout flag + m_bPrimaryPlayerSignedOut=false; + s_bServerHalted = false; + + // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete + // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving. +#if defined(_DURANGO) || defined(__ORBIS__) || defined(__PSVITA__) + while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + { + Sleep(10); + } +#endif + + // 4J-PB remove the server levels + unsigned int iServerLevelC=levels.length; + for (unsigned int i = 0; i < iServerLevelC; i++) + { + if(levels[i]!=NULL) + { + delete levels[i]; + levels[i] = NULL; + } + } + +#if defined(__PS3__) || defined(__ORBIS__) + // Clear the update flags as it's possible they could be out of sync, causing a crash when starting a new world after the first new level ticks + // Fix for PS3 #1538 - [IN GAME] If the user 'Exit without saving' from inside the Nether or The End, the title can hang when loading back into the save. +#endif + + delete connection; + connection = NULL; + delete players; + players = NULL; + delete settings; + settings = NULL; + + g_NetworkManager.ServerStopped(); +} + +void MinecraftServer::halt() +{ + running = false; +} + +void MinecraftServer::setMaxBuildHeight(int maxBuildHeight) +{ + this->maxBuildHeight = maxBuildHeight; +} + +int MinecraftServer::getMaxBuildHeight() +{ + return maxBuildHeight; +} + +PlayerList *MinecraftServer::getPlayers() +{ + return players; +} + +void MinecraftServer::setPlayers(PlayerList *players) +{ + this->players = players; +} + +ServerConnection *MinecraftServer::getConnection() +{ + return connection; +} + +bool MinecraftServer::isAnimals() +{ + return animals; +} + +void MinecraftServer::setAnimals(bool animals) +{ + this->animals = animals; +} + +bool MinecraftServer::isNpcsEnabled() +{ + return npcs; +} + +void MinecraftServer::setNpcsEnabled(bool npcs) +{ + this->npcs = npcs; +} + +bool MinecraftServer::isPvpAllowed() +{ + return pvp; +} + +void MinecraftServer::setPvpAllowed(bool pvp) +{ + this->pvp = pvp; +} + +bool MinecraftServer::isFlightAllowed() +{ + return allowFlight; +} + +void MinecraftServer::setFlightAllowed(bool allowFlight) +{ + this->allowFlight = allowFlight; +} + +bool MinecraftServer::isNetherEnabled() +{ + return true; //settings.getBoolean("allow-nether", true); +} + +bool MinecraftServer::isHardcore() +{ + return false; +} + +CommandDispatcher *MinecraftServer::getCommandDispatcher() +{ + return commandDispatcher; +} + +extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b; +void MinecraftServer::run(__int64 seed, void *lpParameter) +{ + NetworkGameInitData *initData = NULL; + DWORD initSettings = 0; + bool findSeed = false; + if(lpParameter != NULL) + { + initData = (NetworkGameInitData *)lpParameter; + initSettings = app.GetGameHostOption(eGameHostOption_All); + findSeed = initData->findSeed; + m_texturePackId = initData->texturePackId; + } +// try { // 4J - removed try/catch/finally + if (initServer(seed, initData, initSettings,findSeed)) + { + ServerLevel *levelNormalDimension = levels[0]; + // 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there + Minecraft *pMinecraft = Minecraft::GetInstance(); + LevelData *pLevelData=levelNormalDimension->getLevelData(); + + if(pLevelData && pLevelData->getHasStronghold()==false) + { + int x,z; + if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z)) + { + pLevelData->setXStronghold(x); + pLevelData->setZStronghold(z); + pLevelData->setHasStronghold(); + } + } + + __int64 lastTime = System::currentTimeMillis(); + __int64 unprocessedTime = 0; + while (running && !s_bServerHalted) + { + __int64 now = System::currentTimeMillis(); + + // 4J Stu - When we pause the server, we don't want to count that as time passed + // 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused + //Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost + //if(m_isServerPaused) lastTime = now; + + __int64 passedTime = now - lastTime; + if (passedTime > MS_PER_TICK * 40) + { +// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?"); + passedTime = MS_PER_TICK * 40; + } + if (passedTime < 0) + { +// logger.warning("Time ran backwards! Did the system time change?"); + passedTime = 0; + } + unprocessedTime += passedTime; + lastTime = now; + + // 4J Added ability to pause the server + if( !m_isServerPaused ) + { + bool didTick = false; + if (levels[0]->allPlayersAreSleeping()) + { + tick(); + unprocessedTime = 0; + } + else + { +// int tickcount = 0; +// __int64 beforeall = System::currentTimeMillis(); + while (unprocessedTime > MS_PER_TICK) + { + unprocessedTime -= MS_PER_TICK; +// __int64 before = System::currentTimeMillis(); + tick(); +// __int64 after = System::currentTimeMillis(); +// PIXReportCounter(L"Server time",(float)(after-before)); + + // 4J Ensure that the slow queue owner keeps cycling if it's not been used in a while + int time = GetTickCount(); + if( ( s_slowQueuePacketSent ) || ( (time - s_slowQueueLastTime) > ( 2 * MINECRAFT_SERVER_SLOW_QUEUE_DELAY ) ) ) + { +// app.DebugPrintf("Considering cycling: (%d) %d - %d -> %d > %d\n",s_slowQueuePacketSent, time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY)); + MinecraftServer::cycleSlowQueueIndex(); + s_slowQueuePacketSent = false; + s_slowQueueLastTime = time; + } +// else +// { +// app.DebugPrintf("Not considering cycling: %d - %d -> %d > %d\n",time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY)); +// } + } +// __int64 afterall = System::currentTimeMillis(); +// PIXReportCounter(L"Server time all",(float)(afterall-beforeall)); +// PIXReportCounter(L"Server ticks",(float)tickcount); + } + } + else + { + // 4J Stu - TU1-hotfix + //Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost + // The connections should tick at the same frequency even when paused + while (unprocessedTime > MS_PER_TICK) + { + unprocessedTime -= MS_PER_TICK; + // Keep ticking the connections to stop them timing out + connection->tick(); + } + } + if(MinecraftServer::setTimeAtEndOfTick) + { + MinecraftServer::setTimeAtEndOfTick = false; + for (unsigned int i = 0; i < levels.length; i++) + { +// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether + { + ServerLevel *level = levels[i]; + level->setTime( MinecraftServer::setTime ); + level->setOverrideTimeOfDay( -1 ); + } + } + } + if(MinecraftServer::setTimeOfDayAtEndOfTick) + { + MinecraftServer::setTimeOfDayAtEndOfTick = false; + for (unsigned int i = 0; i < levels.length; i++) + { + if (i == 0 || settings->getBoolean(L"allow-nether", true)) + { + ServerLevel *level = levels[i]; + //level->setTime( MinecraftServer::setTime ); + level->setOverrideTimeOfDay( MinecraftServer::setTimeOfDay ); + } + } + } + + // Process delayed actions + eXuiServerAction eAction; + LPVOID param; + for(int i=0;isaveAll(NULL); + } + + for (unsigned int j = 0; j < levels.length; j++) + { + if( s_bServerHalted ) break; + // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat + // with the data from the nethers leveldata. + // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. + ServerLevel *level = levels[levels.length - 1 - j]; + PIXBeginNamedEvent(0, "Saving level %d",levels.length - 1 - j); + level->save(false, NULL, true); + PIXEndNamedEvent(); + } + if( !s_bServerHalted ) + { + PIXBeginNamedEvent(0,"Saving game rules"); + saveGameRules(); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Save to disc"); + levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true); + PIXEndNamedEvent(); + } + PIXEndNamedEvent(); + + QueryPerformanceCounter( &qwNewTime ); + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime); + } + break; +#endif + case eXuiServerAction_SaveGame: + app.EnterSaveNotificationSection(); + if (players != NULL) + { + players->saveAll(Minecraft::GetInstance()->progressRenderer); + } + + players->broadcastAll( shared_ptr( new UpdateProgressPacket(20) ) ); + + for (unsigned int j = 0; j < levels.length; j++) + { + if( s_bServerHalted ) break; + // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat + // with the data from the nethers leveldata. + // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting. + ServerLevel *level = levels[levels.length - 1 - j]; + level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame)); + + players->broadcastAll( shared_ptr( new UpdateProgressPacket(33 + (j*33) ) ) ); + } + if( !s_bServerHalted ) + { + saveGameRules(); + + levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame)); + } + app.LeaveSaveNotificationSection(); + break; + case eXuiServerAction_DropItem: + // Find the player, and drop the id at their feet + { + shared_ptr player = players->players.at(0); + size_t id = (size_t) param; + player->drop( shared_ptr( new ItemInstance(id, 1, 0 ) ) ); + } + break; + case eXuiServerAction_SpawnMob: + { + shared_ptr player = players->players.at(0); + eINSTANCEOF factory = (eINSTANCEOF)((size_t)param); + shared_ptr mob = dynamic_pointer_cast(EntityIO::newByEnumType(factory,player->level )); + mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0); + mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set) + player->level->addEntity(mob); + } + break; + case eXuiServerAction_PauseServer: + m_isServerPaused = ( (size_t) param == TRUE ); + if( m_isServerPaused ) + { + m_serverPausedEvent->Set(); + } + break; + case eXuiServerAction_ToggleRain: + { + bool isRaining = levels[0]->getLevelData()->isRaining(); + levels[0]->getLevelData()->setRaining(!isRaining); + levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2); + } + break; + case eXuiServerAction_ToggleThunder: + { + bool isThundering = levels[0]->getLevelData()->isThundering(); + levels[0]->getLevelData()->setThundering(!isThundering); + levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2); + } + break; + case eXuiServerAction_ServerSettingChanged_Gamertags: + players->broadcastAll( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)) ) ); + break; + case eXuiServerAction_ServerSettingChanged_BedrockFog: + players->broadcastAll( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)) ) ); + break; + + case eXuiServerAction_ServerSettingChanged_Difficulty: + players->broadcastAll( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty) ) ); + break; + case eXuiServerAction_ExportSchematic: +#ifndef _CONTENT_PACKAGE + app.EnterSaveNotificationSection(); + + //players->broadcastAll( shared_ptr( new UpdateProgressPacket(20) ) ); + + if( !s_bServerHalted ) + { + ConsoleSchematicFile::XboxSchematicInitParam *initData = (ConsoleSchematicFile::XboxSchematicInitParam *)param; +#ifdef _XBOX + File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics"); +#else + File targetFileDir(L"Schematics"); +#endif + if(!targetFileDir.exists()) targetFileDir.mkdir(); + + wchar_t filename[128]; + swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1)); + + File dataFile = File( targetFileDir, wstring(filename) ); + if(dataFile.exists()) dataFile._delete(); + FileOutputStream fos = FileOutputStream(dataFile); + DataOutputStream dos = DataOutputStream(&fos); + ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType); + dos.close(); + + delete initData; + } + app.LeaveSaveNotificationSection(); +#endif + break; + case eXuiServerAction_SetCameraLocation: +#ifndef _CONTENT_PACKAGE + { + DebugSetCameraPosition *pos = (DebugSetCameraPosition *)param; + + app.DebugPrintf( "DEBUG: Player=%i\n", pos->player ); + app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n", + pos->m_camX, pos->m_camY, pos->m_camZ, + pos->m_yRot, pos->m_elev + ); + + shared_ptr player = players->players.at(pos->player); + player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ, + pos->m_yRot, pos->m_elev ); + + // Doesn't work + //player->setYHeadRot(pos->m_yRot); + //player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev); + } +#endif + break; + } + + app.SetXuiServerAction(i,eXuiServerAction_Idle); + } + + Sleep(1); + } + } + //else + //{ + // while (running) + // { + // handleConsoleInputs(); + // Sleep(10); + // } + //} +#if 0 + } catch (Throwable t) { + t.printStackTrace(); + logger.log(Level.SEVERE, "Unexpected exception", t); + while (running) { + handleConsoleInputs(); + try { + Thread.sleep(10); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } + } finally { + try { + stopServer(); + stopped = true; + } catch (Throwable t) { + t.printStackTrace(); + } finally { + System::exit(0); + } + } +#endif + + // 4J Stu - Stop the server when the loops complete, as the finally would do + stopServer(); + stopped = true; +} + +void MinecraftServer::broadcastStartSavingPacket() +{ + players->broadcastAll( shared_ptr( new GameEventPacket(GameEventPacket::START_SAVING, 0) ) );; +} + +void MinecraftServer::broadcastStopSavingPacket() +{ + if( !s_bServerHalted ) + { + players->broadcastAll( shared_ptr( new GameEventPacket(GameEventPacket::STOP_SAVING, 0) ) );; + } +} + +void MinecraftServer::tick() +{ + vector toRemove; + for (AUTO_VAR(it, ironTimers.begin()); it != ironTimers.end(); it++ ) + { + int t = it->second; + if (t > 0) + { + ironTimers[it->first] = t - 1; + } + else + { + toRemove.push_back(it->first); + } + } + for (unsigned int i = 0; i < toRemove.size(); i++) + { + ironTimers.erase(toRemove[i]); + } + + AABB::resetPool(); + Vec3::resetPool(); + + tickCount++; + + // 4J We need to update client difficulty levels based on the servers + Minecraft *pMinecraft = Minecraft::GetInstance(); + // 4J-PB - sending this on the host changing the difficulty in the menus +/* if(m_lastSentDifficulty != pMinecraft->options->difficulty) + { + m_lastSentDifficulty = pMinecraft->options->difficulty; + players->broadcastAll( shared_ptr( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, pMinecraft->options->difficulty) ) ); + }*/ + + for (unsigned int i = 0; i < levels.length; i++) + { +// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether + { + ServerLevel *level = levels[i]; + + // 4J Stu - We set the levels difficulty based on the minecraft options + level->difficulty = app.GetGameHostOption(eGameHostOption_Difficulty); //pMinecraft->options->difficulty; + +#if DEBUG_SERVER_DONT_SPAWN_MOBS + level->setSpawnSettings(false, false); +#else + level->setSpawnSettings(level->difficulty > 0 && !Minecraft::GetInstance()->isTutorial(), animals); +#endif + + if (tickCount % 20 == 0) + { + players->broadcastAll( shared_ptr( new SetTimePacket(level->getTime() ) ), level->dimension->id); + } +// #ifndef __PS3__ + static __int64 stc = 0; + __int64 st0 = System::currentTimeMillis(); + PIXBeginNamedEvent(0,"Level tick %d",i); + ((Level *)level)->tick(); + __int64 st1 = System::currentTimeMillis(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Update lights %d",i); + // 4J - used to be in a while loop, but we don't want the server locking up for a big chunk of time (could end up trying to process 1,000,000 lights...) + // Instead call this once, which will try and process up to 2000 lights per tick +// printf("lights: %d\n",level->getLightsToUpdate()); + while(level->updateLights() ) + ; + __int64 st2 = System::currentTimeMillis(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Entity tick %d",i); + // 4J added to stop ticking entities in levels when players are not in those levels. + // Note: now changed so that we also tick if there are entities to be removed, as this also happens as a result of calling tickEntities. If we don't do this, then the + // entities get removed at the first point that there is a player count in the level - this has been causing a problem when going from normal dimension -> nether -> normal, + // as the player is getting flagged as to be removed (from the normal dimension) when going to the nether, but Actually gets removed only when it returns + if( ( players->getPlayerCount(level) > 0) || ( level->hasEntitiesToRemove() ) ) + { +#ifdef __PSVITA__ + // AP - the PlayerList->viewDistance initially starts out at 3 to make starting a level speedy + // the problem with this is that spawned monsters are always generated on the edge of the known map + // which means they wont process (unless they are surrounded by 2 visible chunks). This means + // they wont checkDespawn so they are NEVER removed which results in monsters not spawning. + // This bit of hack will modify the view distance once the level is up and running. + int newViewDistance = 5; + level->getServer()->getPlayers()->setViewDistance(newViewDistance); + level->getTracker()->updateMaxRange(); + level->getChunkMap()->setRadius(level->getServer()->getPlayers()->getViewDistance()); +#endif + level->tickEntities(); + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Entity tracker tick"); + level->getTracker()->tick(); + PIXEndNamedEvent(); + + __int64 st3 = System::currentTimeMillis(); +// printf(">>>>>>>>>>>>>>>>>>>>>> Tick %d %d %d : %d\n", st1 - st0, st2 - st1, st3 - st2, st0 - stc ); + stc = st0; +// #endif// __PS3__ + } + } + Entity::tickExtraWandering(); // 4J added + + PIXBeginNamedEvent(0,"Connection tick"); + connection->tick(); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Players tick"); + players->tick(); + PIXEndNamedEvent(); + + // 4J - removed +#if 0 + for (int i = 0; i < tickables.size(); i++) { + tickables.get(i)-tick(); + } +#endif + +// try { // 4J - removed try/catch + handleConsoleInputs(); +// } catch (Exception e) { +// logger.log(Level.WARNING, "Unexpected exception while parsing console command", e); +// } +} + +void MinecraftServer::handleConsoleInput(const wstring& msg, ConsoleInputSource *source) +{ + consoleInput.push_back(new ConsoleInput(msg, source)); +} + +void MinecraftServer::handleConsoleInputs() +{ + while (consoleInput.size() > 0) + { + AUTO_VAR(it, consoleInput.begin()); + ConsoleInput *input = *it; + consoleInput.erase(it); +// commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands? + } +} + +void MinecraftServer::main(__int64 seed, void *lpParameter) +{ +#if __PS3__ + ShutdownManager::HasStarted(ShutdownManager::eServerThread ); +#endif + server = new MinecraftServer(); + server->run(seed, lpParameter); + delete server; + server = NULL; + ShutdownManager::HasFinished(ShutdownManager::eServerThread ); +} + +void MinecraftServer::HaltServer(bool bPrimaryPlayerSignedOut) +{ + s_bServerHalted = true; + if( server != NULL ) + { + m_bPrimaryPlayerSignedOut=bPrimaryPlayerSignedOut; + server->halt(); + } +} + +File *MinecraftServer::getFile(const wstring& name) +{ + return new File(name); +} + +void MinecraftServer::info(const wstring& string) +{ +} + +void MinecraftServer::warn(const wstring& string) +{ +} + +wstring MinecraftServer::getConsoleName() +{ + return L"CONSOLE"; +} + +ServerLevel *MinecraftServer::getLevel(int dimension) +{ + if (dimension == -1) return levels[1]; + else if (dimension == 1) return levels[2]; + else return levels[0]; +} + +// 4J added +void MinecraftServer::setLevel(int dimension, ServerLevel *level) +{ + if (dimension == -1) levels[1] = level; + else if (dimension == 1) levels[2] = level; + else levels[0] = level; +} + +// 4J Added +bool MinecraftServer::canSendOnSlowQueue(INetworkPlayer *player) +{ + if( player == NULL ) return false; + + int time = GetTickCount(); + if( player->GetSessionIndex() == s_slowQueuePlayerIndex && (time - s_slowQueueLastTime) > MINECRAFT_SERVER_SLOW_QUEUE_DELAY ) + { +// app.DebugPrintf("Slow queue OK for player #%d\n", player->GetSessionIndex()); + return true; + } + + return false; +} + +void MinecraftServer::cycleSlowQueueIndex() +{ + if( !g_NetworkManager.IsInSession() ) return; + + int startingIndex = s_slowQueuePlayerIndex; + INetworkPlayer *currentPlayer = NULL; + DWORD currentPlayerCount = 0; + do + { + currentPlayerCount = g_NetworkManager.GetPlayerCount(); + if( startingIndex >= currentPlayerCount ) startingIndex = 0; + ++s_slowQueuePlayerIndex; + + if( currentPlayerCount > 0 ) + { + s_slowQueuePlayerIndex %= currentPlayerCount; + // Fix for #9530 - NETWORKING: Attempting to fill a multiplayer game beyond capacity results in a softlock for the last players to join. + // The QNet session might be ending while we do this, so do a few more checks that the player is real + currentPlayer = g_NetworkManager.GetPlayerByIndex( s_slowQueuePlayerIndex ); + } + else + { + s_slowQueuePlayerIndex = 0; + } + } while ( g_NetworkManager.IsInSession() && + currentPlayerCount > 0 && + s_slowQueuePlayerIndex != startingIndex && + currentPlayer != NULL && + currentPlayer->IsLocal() + ); +// app.DebugPrintf("Cycled slow queue index to %d\n", s_slowQueuePlayerIndex); +} + +// 4J added - sets up a vector of flags to indicate which entities (with small Ids) have been removed from the level, but are still haven't constructed a network packet +// to tell a remote client about it. These small Ids shouldn't be re-used. Most of the time this method shouldn't actually do anything, in which case it will return false +// and nothing is set up. +bool MinecraftServer::flagEntitiesToBeRemoved(unsigned int *flags) +{ + bool removedFound = false; + for( unsigned int i = 0; i < levels.length; i++ ) + { + ServerLevel *level = levels[i]; + if( level ) + { + level->flagEntitiesToBeRemoved( flags, &removedFound ); + } + } + return removedFound; +} \ No newline at end of file diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h new file mode 100644 index 0000000..e61001a --- /dev/null +++ b/Minecraft.Client/MinecraftServer.h @@ -0,0 +1,245 @@ +#pragma once +#include "ConsoleInputSource.h" +#include "..\Minecraft.World\ArrayWithLength.h" +#include "..\Minecraft.World\SharedConstants.h" +#include "..\Minecraft.World\C4JThread.h" + +class ServerConnection; +class Settings; +class PlayerList; +class EntityTracker; +class ConsoleInput; +class ConsoleCommands; +class LevelStorageSource; +class ChunkSource; +class INetworkPlayer; +class LevelRuleset; +class LevelType; +class ProgressRenderer; +class CommandDispatcher; + +#define MINECRAFT_SERVER_SLOW_QUEUE_DELAY 250 + +typedef struct _LoadSaveDataThreadParam +{ + LPVOID data; + __int64 fileSize; + const wstring saveName; + _LoadSaveDataThreadParam(LPVOID data, __int64 filesize, const wstring &saveName) : data( data ), fileSize( filesize ), saveName( saveName ) {} +} LoadSaveDataThreadParam; + +typedef struct _NetworkGameInitData +{ + __int64 seed; + LoadSaveDataThreadParam *saveData; + DWORD settings; + LevelGenerationOptions *levelGen; + DWORD texturePackId; + bool findSeed; + unsigned int xzSize; + unsigned char hellScale; + ESavePlatform savePlatform; + + _NetworkGameInitData() + { + seed = 0; + saveData = NULL; + settings = 0; + levelGen = NULL; + texturePackId = 0; + findSeed = false; + xzSize = LEVEL_LEGACY_WIDTH; + hellScale = HELL_LEVEL_LEGACY_SCALE; + savePlatform = SAVE_FILE_PLATFORM_LOCAL; + } +} NetworkGameInitData; + +using namespace std; + +// 4J Stu - 1.0.1 updates the server to implement the ServerInterface class, but I don't think we will use any of the functions that defines so not implementing here +class MinecraftServer : public ConsoleInputSource +{ +public: + static const wstring VERSION; + static const int TICK_STATS_SPAN = SharedConstants::TICKS_PER_SECOND * 5; + +// static Logger logger = Logger.getLogger("Minecraft"); + static unordered_map ironTimers; + +private: + static const int DEFAULT_MINECRAFT_PORT = 25565; + static const int MS_PER_TICK = 1000 / SharedConstants::TICKS_PER_SECOND; + + // 4J Stu - Added 1.0.1, Not needed + //wstring localIp; + //int port; +public: + ServerConnection *connection; + Settings *settings; + ServerLevelArray levels; + +private: + PlayerList *players; + + // 4J Stu - Added 1.0.1, Not needed + //long[] tickTimes = new long[TICK_STATS_SPAN]; + //long[][] levelTickTimes; +private: + ConsoleCommands *commands; + bool running; + bool m_bLoaded; +public: + bool stopped; + int tickCount; + +public: + wstring progressStatus; + int progress; +private: +// vector tickables = new ArrayList(); // 4J - removed + CommandDispatcher *commandDispatcher; + vector consoleInput; // 4J - was synchronizedList - TODO - investigate +public: + bool onlineMode; + bool animals; + bool npcs; + bool pvp; + bool allowFlight; + wstring motd; + int maxBuildHeight; + +private: + // 4J Added + //int m_lastSentDifficulty; + +public: + // 4J Stu - This value should be incremented every time the list of players with friends-only UGC settings changes + // It is sent with PreLoginPacket and compared when it comes back in the LoginPacket + DWORD m_ugcPlayersVersion; + + // This value is used to store the texture pack id for the currently loaded world + DWORD m_texturePackId; + +public: + MinecraftServer(); + ~MinecraftServer(); +private: + // 4J Added - LoadSaveDataThreadParam + bool initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed); + void postProcessTerminate(ProgressRenderer *mcprogress); + bool loadLevel(LevelStorageSource *storageSource, const wstring& name, __int64 levelSeed, LevelType *pLevelType, NetworkGameInitData *initData); + void setProgress(const wstring& status, int progress); + void endProgress(); + void saveAllChunks(); + void saveGameRules(); + void stopServer(); + +public: + void setMaxBuildHeight(int maxBuildHeight); + int getMaxBuildHeight(); + PlayerList *getPlayers(); + void setPlayers(PlayerList *players); + ServerConnection *getConnection(); + bool isAnimals(); + void setAnimals(bool animals); + bool isNpcsEnabled(); + void setNpcsEnabled(bool npcs); + bool isPvpAllowed(); + void setPvpAllowed(bool pvp); + bool isFlightAllowed(); + void setFlightAllowed(bool allowFlight); + bool isNetherEnabled(); + bool isHardcore(); + CommandDispatcher *getCommandDispatcher(); + +public: + void halt(); + void run(__int64 seed, void *lpParameter); + + void broadcastStartSavingPacket(); + void broadcastStopSavingPacket(); + +private: + void tick(); +public: + void handleConsoleInput(const wstring& msg, ConsoleInputSource *source); + void handleConsoleInputs(); +// void addTickable(Tickable tickable); // 4J removed + static void main(__int64 seed, void *lpParameter); + static void HaltServer(bool bPrimaryPlayerSignedOut=false); + + File *getFile(const wstring& name); + void info(const wstring& string); + void warn(const wstring& string); + wstring getConsoleName(); + ServerLevel *getLevel(int dimension); + void setLevel(int dimension, ServerLevel *level); // 4J added + static MinecraftServer *getInstance() { return server; } // 4J added + static bool serverHalted() { return s_bServerHalted; } + static bool saveOnExitAnswered() { return s_bSaveOnExitAnswered; } + static void resetFlags() { s_bServerHalted = false; s_bSaveOnExitAnswered = false; } + + bool flagEntitiesToBeRemoved(unsigned int *flags); // 4J added +private: + //4J Added + static MinecraftServer *server; + + static bool setTimeOfDayAtEndOfTick; + static __int64 setTimeOfDay; + static bool setTimeAtEndOfTick; + static __int64 setTime; + + static bool m_bPrimaryPlayerSignedOut; // 4J-PB added to tell the stopserver not to save the game - another player may have signed in in their place, so ProfileManager.IsSignedIn isn't enough + static bool s_bServerHalted; // 4J Stu Added so that we can halt the server even before it's been created properly + static bool s_bSaveOnExitAnswered; // 4J Stu Added so that we only ask this question once when we exit + + // 4J - added so that we can have a separate thread for post processing chunks on level creation + static int runPostUpdate(void* lpParam); + C4JThread* m_postUpdateThread; + bool m_postUpdateTerminate; + class postProcessRequest + { + public: + int x, z; + ChunkSource *chunkSource; + postProcessRequest(int x, int z, ChunkSource *chunkSource) : x(x), z(z), chunkSource(chunkSource) {} + }; + vector m_postProcessRequests; + CRITICAL_SECTION m_postProcessCS; +public: + void addPostProcessRequest(ChunkSource *chunkSource, int x, int z); + +public: + static PlayerList *getPlayerList() { if( server != NULL ) return server->players; else return NULL; } + static void SetTimeOfDay(__int64 time) { setTimeOfDayAtEndOfTick = true; setTimeOfDay = time; } + static void SetTime(__int64 time) { setTimeAtEndOfTick = true; setTime = time; } + + C4JThread::Event* m_serverPausedEvent; +private: + // 4J Added + bool m_isServerPaused; + + // 4J Added - A static that stores the QNet index of the player that is next allowed to send a packet in the slow queue + static int s_slowQueuePlayerIndex; + static int s_slowQueueLastTime; +public: + static bool s_slowQueuePacketSent; + + bool IsServerPaused() { return m_isServerPaused; } + +private: + // 4J Added + bool m_saveOnExit; + bool m_suspending; + +public: + //static int getSlowQueueIndex() { return s_slowQueuePlayerIndex; } + static bool canSendOnSlowQueue(INetworkPlayer *player); + static void cycleSlowQueueIndex(); + + void setSaveOnExit(bool save) { m_saveOnExit = save; s_bSaveOnExitAnswered = true; } + void Suspend(); + bool IsSuspending(); + + // 4J Stu - A load of functions were all added in 1.0.1 in the ServerInterface, but I don't think we need any of them +}; diff --git a/Minecraft.Client/Minimap.cpp b/Minecraft.Client/Minimap.cpp new file mode 100644 index 0000000..c18cd26 --- /dev/null +++ b/Minecraft.Client/Minimap.cpp @@ -0,0 +1,262 @@ +#include "stdafx.h" +#include "Minecraft.h" +#include "Minimap.h" +#include "Font.h" +#include "Options.h" +#include "Textures.h" +#include "Tesselator.h" +#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h" +#include "..\Minecraft.World\net.minecraft.world.level.material.h" + +#ifdef __ORBIS__ +short Minimap::LUT[256]; // 4J added +#else +int Minimap::LUT[256]; // 4J added +#endif +bool Minimap::genLUT = true; // 4J added + +Minimap::Minimap(Font *font, Options *options, Textures *textures, bool optimised) +{ +#ifdef __PS3__ + // we're using the RSX now to upload textures to vram, so we need the main ram textures allocated from io space + this->pixels = intArray((int*)RenderManager.allocIOMem(w*h*sizeof(int)), 16*16); + +#elif defined __ORBIS__ + this->pixels = shortArray(w*h); +#else + this->pixels = intArray(w*h); +#endif + this->options = options; + this->font = font; + BufferedImage *img = new BufferedImage(w, h, BufferedImage::TYPE_INT_ARGB); +#ifdef __ORBIS__ + mapTexture = textures->getTexture(img, C4JRender::TEXTURE_FORMAT_RxGyBzAw5551, false ); // 4J - make sure we aren't mipmapping as we never set the data for mipmaps +#else + mapTexture = textures->getTexture(img, C4JRender::TEXTURE_FORMAT_RxGyBzAw, false ); // 4J - make sure we aren't mipmapping as we never set the data for mipmaps +#endif + delete img; + for (int i = 0; i < w * h; i++) + { + pixels[i] = 0x00000000; + } + + // 4J added - generate the colour mapping that we'll be needing as a LUT to minimise processing we actually need to do during normal rendering + if( genLUT ) + { + reloadColours(); + } + renderCount = 0; // 4J added + m_optimised = optimised; +} + +void Minimap::reloadColours() +{ + ColourTable *colourTable = Minecraft::GetInstance()->getColourTable(); + // 4J note that this code has been extracted pretty much as it was in Minimap::render, although with some byte order changes + for( int i = 0; i < (14 * 4); i++ ) // 14 material colours currently, 4 brightnesses of each + { + if (i / 4 == 0) + { + // 4J - changed byte order to save having to reorder later +#ifdef __ORBIS__ + LUT[i] = 0; +#else + LUT[i] = (((i + i / w) & 1) * 8 + 16); +#endif + //pixels[i] = (((i + i / w) & 1) * 8 + 16) << 24; + } + else + { + int color = colourTable->getColor( MaterialColor::colors[i / 4]->col ); + int brightness = i & 3; + + int br = 220; + if (brightness == 2) br = 255; + if (brightness == 0) br = 180; + + int r = ((color >> 16) & 0xff) * br / 255; + int g = ((color >> 8) & 0xff) * br / 255; + int b = ((color) & 0xff) * br / 255; + + // 4J - changed byte order to save having to reorder later +#if ( defined _DURANGO || defined _WIN64 || __PSVITA__ ) + LUT[i] = 255 << 24 | b << 16 | g << 8 | r; +#elif defined _XBOX + LUT[i] = 255 << 24 | r << 16 | g << 8 | b; +#elif defined __ORBIS__ + r >>= 3; g >>= 3; b >>= 3; + LUT[i] = 1 << 15 | ( r << 10 ) | ( g << 5 ) | b; +#else + LUT[i] = r << 24 | g << 16 | b << 8 | 255; +#endif + + //pixels[i] = (255) << 24 | r << 16 | g << 8 | b; + } + + } + genLUT = false; +} + +// 4J added entityId +void Minimap::render(shared_ptr player, Textures *textures, shared_ptr data, int entityId) +{ + // 4J - only update every 8 renders, as an optimisation + // We don't want to use this for ItemFrame renders of maps, as then we can't have different maps together + if( !m_optimised || ( renderCount & 7 ) == 0 ) + { + for (int i = 0; i < w * h; i++) + { + int val = data->colors[i]; + // 4J - moved the code that used to run here into a LUT that is generated once in the ctor above + pixels[i] = LUT[val]; + } + } + renderCount++; + + // 4J - changed - have changed texture generation here to put the bytes in the right order already, so we don't have to do any copying round etc. in the texture replacement itself + textures->replaceTextureDirect(pixels, w, h, mapTexture); + + int x = 0; + int y = 0; + Tesselator *t = Tesselator::getInstance(); + + float vo = 0; + + glBindTexture(GL_TEXTURE_2D, mapTexture); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_ALPHA_TEST); + t->begin(); + // 4J - moved to -0.02 to stop z fighting ( was -0.01) + // AP - Vita still has issues so push it a bit more + float Offset = -0.02f; +#ifdef __PSVITA__ + Offset = -0.03f; +#endif + t->vertexUV((float)(x + 0 + vo), (float)( y + h - vo), (float)( Offset), (float)( 0), (float)( 1)); + t->vertexUV((float)(x + w - vo), (float)( y + h - vo), (float)( Offset), (float)( 1), (float)( 1)); + t->vertexUV((float)(x + w - vo), (float)( y + 0 + vo), (float)( Offset), (float)( 1), (float)( 0)); + t->vertexUV((float)(x + 0 + vo), (float)( y + 0 + vo), (float)( Offset), (float)( 0), (float)( 0)); + t->end(); + glEnable(GL_ALPHA_TEST); + glDisable(GL_BLEND); + + + textures->bind(textures->loadTexture(TN_MISC_MAPICONS));//L"/misc/mapicons.png")); + + AUTO_VAR(itEnd, data->decorations.end()); + +#ifdef _LARGE_WORLDS + vector m_edgeIcons; +#endif + + // 4J-PB - stack the map icons + float fIconZ=-0.04f;// 4J - moved to -0.04 (was -0.02) to stop z fighting + for( vector::iterator it = data->decorations.begin(); it != itEnd; it++ ) + { + MapItemSavedData::MapDecoration *dec = *it; + + if(!dec->visible) continue; + + char imgIndex = dec->img; + +#ifdef _LARGE_WORLDS + // For edge icons, use a different texture + if(imgIndex >= 16) + { + m_edgeIcons.push_back(dec); + continue; + } +#endif + + // 4J Stu - For item frame renders, the player is NULL. We do not want to show player icons on the frames. + if(player == NULL && (imgIndex != 12)) continue; + else if (player != NULL && imgIndex == 12) continue; + else if( imgIndex == 12 && dec->entityId != entityId) continue; + + glPushMatrix(); + glTranslatef(x + dec->x / 2.0f + w / 2, y + dec->y / 2.0f + h / 2, fIconZ); + glRotatef(dec->rot * 360 / 16.0f, 0, 0, 1); + glScalef(4, 4, 3); + glTranslatef(-1.0f / 8.0f, +1.0f / 8.0f, 0); + + float u0 = (imgIndex % 4 + 0) / 4.0f; + float v0 = (imgIndex / 4 + 0) / 4.0f; + float u1 = (imgIndex % 4 + 1) / 4.0f; + float v1 = (imgIndex / 4 + 1) / 4.0f; + + t->begin(); + t->vertexUV((float)(-1), (float)( +1), (float)( 0), (float)( u0), (float)( v0)); + t->vertexUV((float)(+1), (float)( +1), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(+1), (float)( -1), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(-1), (float)( -1), (float)( 0), (float)( u0), (float)( v1)); + t->end(); + glPopMatrix(); + fIconZ-=0.01f; + } + +#ifdef _LARGE_WORLDS + // For players on the edge of the world + textures->bind(textures->loadTexture(TN_MISC_ADDITIONALMAPICONS)); + + fIconZ=-0.04f;// 4J - moved to -0.04 (was -0.02) to stop z fighting + for( AUTO_VAR(it,m_edgeIcons.begin()); it != m_edgeIcons.end(); it++ ) + { + MapItemSavedData::MapDecoration *dec = *it; + + char imgIndex = dec->img; + imgIndex -= 16; + + // 4J Stu - For item frame renders, the player is NULL. We do not want to show player icons on the frames. + if(player == NULL && (imgIndex != 12)) continue; + else if (player != NULL && imgIndex == 12) continue; + else if( imgIndex == 12 && dec->entityId != entityId) continue; + + glPushMatrix(); + glTranslatef(x + dec->x / 2.0f + w / 2, y + dec->y / 2.0f + h / 2, fIconZ); + glRotatef(dec->rot * 360 / 16.0f, 0, 0, 1); + glScalef(4, 4, 3); + glTranslatef(-1.0f / 8.0f, +1.0f / 8.0f, 0); + + float u0 = (imgIndex % 4 + 0) / 4.0f; + float v0 = (imgIndex / 4 + 0) / 4.0f; + float u1 = (imgIndex % 4 + 1) / 4.0f; + float v1 = (imgIndex / 4 + 1) / 4.0f; + + t->begin(); + t->vertexUV((float)(-1), (float)( +1), (float)( 0), (float)( u0), (float)( v0)); + t->vertexUV((float)(+1), (float)( +1), (float)( 0), (float)( u1), (float)( v0)); + t->vertexUV((float)(+1), (float)( -1), (float)( 0), (float)( u1), (float)( v1)); + t->vertexUV((float)(-1), (float)( -1), (float)( 0), (float)( u0), (float)( v1)); + t->end(); + glPopMatrix(); + fIconZ-=0.01f; + } +#endif + + glPushMatrix(); +// glRotatef(0, 1, 0, 0); + glTranslatef(0, 0, -0.06f); + glScalef(1, 1, 1); +// 4J Stu - Don't render the text name, except in debug +//#if 1 +//#ifdef _DEBUG +// font->draw(data->id, x, y, 0xff000000); +//#else + // 4J Stu - TU-1 hotfix + // DCR: Render the players current position here instead + if(player != NULL) + { + wchar_t playerPosText[32]; + ZeroMemory(&playerPosText, sizeof(wchar_t) * 32); + int posx = floor(player->x); + int posy = floor(player->y); + int posz = floor(player->z); + swprintf(playerPosText, 32, L"X: %d, Y: %d, Z: %d", posx, posy, posz); + + font->draw(playerPosText, x, y, Minecraft::GetInstance()->getColourTable()->getColour(eMinecraftColour_Map_Text)); + } +//#endif + glPopMatrix(); + +} diff --git a/Minecraft.Client/Minimap.h b/Minecraft.Client/Minimap.h new file mode 100644 index 0000000..758d8a8 --- /dev/null +++ b/Minecraft.Client/Minimap.h @@ -0,0 +1,35 @@ +#pragma once +#include "..\Minecraft.World\MapItem.h" +class Options; +class Font; +class Textures; +class Player; +class MapItemSavedData; + +class Minimap +{ +private: + static const int w = MapItem::IMAGE_WIDTH; + static const int h = MapItem::IMAGE_HEIGHT; +#ifdef __ORBIS__ + static short LUT[256]; // 4J added +#else + static int LUT[256]; // 4J added +#endif + static bool genLUT; // 4J added + int renderCount; // 4J added + bool m_optimised; // 4J Added +#ifdef __ORBIS__ + shortArray pixels; +#else + intArray pixels; +#endif + int mapTexture; + Options *options; + Font *font; + +public: + Minimap(Font *font, Options *options, Textures *textures, bool optimised = true); // 4J Added optimised param + static void reloadColours(); + void render(shared_ptr player, Textures *textures, shared_ptr data, int entityId); // 4J added entityId param +}; diff --git a/Minecraft.Client/MobRenderer.cpp b/Minecraft.Client/MobRenderer.cpp new file mode 100644 index 0000000..8874ab9 --- /dev/null +++ b/Minecraft.Client/MobRenderer.cpp @@ -0,0 +1,515 @@ +#include "stdafx.h" +#include "MobRenderer.h" +#include "MultiPlayerLocalPlayer.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "..\Minecraft.World\Mth.h" +#include "entityRenderDispatcher.h" + +MobRenderer::MobRenderer(Model *model, float shadow) : EntityRenderer() +{ + this->model = model; + this->shadowRadius = shadow; + + this->armor = NULL; +} + +void MobRenderer::setArmor(Model *armor) +{ + this->armor = armor; +} + +float MobRenderer::rotlerp(float from, float to, float a) +{ + float diff = to - from; + while (diff < -180) + diff += 360; + while (diff >= 180) + diff -= 360; + return from + a * diff; +} + +void MobRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - added - this used to use generics so the input parameter could be a mob (or derived type), but we aren't + // able to do that so dynamically casting to get the more specific type here. + shared_ptr mob = dynamic_pointer_cast(_mob); + glPushMatrix(); + glDisable(GL_CULL_FACE); + + model->attackTime = getAttackAnim(mob, a); + if (armor != NULL) armor->attackTime = model->attackTime; + model->riding = mob->isRiding(); + if (armor != NULL) armor->riding = model->riding; + model->young = mob->isBaby(); + if (armor != NULL) armor->young = model->young; + + // 4J - removed try/catch +// try +// { + float bodyRot = rotlerp(mob->yBodyRotO, mob->yBodyRot, a); + float headRot = rotlerp(mob->yHeadRotO, mob->yHeadRot, a); + + if (mob->isRiding() && dynamic_pointer_cast(mob->riding)) + { + shared_ptr riding = dynamic_pointer_cast(mob->riding); + bodyRot = rotlerp(riding->yBodyRotO, riding->yBodyRot, a); + + float headDiff = Mth::wrapDegrees(headRot - bodyRot); + if (headDiff < -85) headDiff = -85; + if (headDiff >= 85) headDiff = +85; + bodyRot = headRot - headDiff; + if (headDiff * headDiff > 50 * 50) + { + bodyRot += headDiff * 0.2f; + } + } + + float headRotx = (mob->xRotO + (mob->xRot - mob->xRotO) * a); + + setupPosition(mob, x, y, z); + + float bob = getBob(mob, a); + setupRotations(mob, bob, bodyRot, a); + + float _scale = 1 / 16.0f; + glEnable(GL_RESCALE_NORMAL); + glScalef(-1, -1, 1); + + scale(mob, a); + glTranslatef(0, -24 * _scale - 0.125f / 16.0f, 0); + + + float ws = mob->walkAnimSpeedO + (mob->walkAnimSpeed - mob->walkAnimSpeedO) * a; + float wp = mob->walkAnimPos - mob->walkAnimSpeed * (1 - a); + if (mob->isBaby()) + { + wp *= 3.0f; + } + + if (ws > 1) ws = 1; + + MemSect(31); + bindTexture(mob->customTextureUrl, mob->getTexture()); + MemSect(0); + glEnable(GL_ALPHA_TEST); + + model->prepareMobModel(mob, wp, ws, a); + renderModel(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + int armorType = prepareArmor(mob, i, a); + if (armorType > 0) + { + armor->prepareMobModel(mob, wp, ws, a); + armor->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + if ((armorType & 0xf0) == 16) + { + prepareSecondPassArmor(mob, i, a); + armor->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, true); + } + // 4J - added condition here for rendering player as part of the gui. Avoiding rendering the glint here as it involves using its own blending, and for gui rendering + // we are globally blending to be able to offer user configurable gui opacity. Note that I really don't know why GL_BLEND is turned off at the end of the first + // armour layer anyway, or why alpha testing is turned on... but we definitely don't want to be turning blending off during the gui render. + if( !entityRenderDispatcher->isGuiRender ) + { + if ((armorType & 0xf) == 0xf) //MGH - fix for missing enchantment glow + { + float time = mob->tickCount + a; + bindTexture(TN__BLUR__MISC_GLINT); // 4J was "%blur%/misc/glint.png" + glEnable(GL_BLEND); + float br = 0.5f; + glColor4f(br, br, br, 1); + glDepthFunc(GL_EQUAL); + glDepthMask(false); + + for (int j = 0; j < 2; j++) + { + glDisable(GL_LIGHTING); + float brr = 0.76f; + glColor4f(0.5f * brr, 0.25f * brr, 0.8f * brr, 1); + glBlendFunc(GL_SRC_COLOR, GL_ONE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + float uo = time * (0.001f + j * 0.003f) * 20; + float ss = 1 / 3.0f; + glScalef(ss, ss, ss); + glRotatef(30 - (j) * 60.0f, 0, 0, 1); + glTranslatef(0, uo, 0); + glMatrixMode(GL_MODELVIEW); + armor->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + + glColor4f(1, 1, 1, 1); + glMatrixMode(GL_TEXTURE); + glDepthMask(true); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + + } + glDisable(GL_BLEND); + } + glEnable(GL_ALPHA_TEST); + } + } + + glDepthMask(true); + + additionalRendering(mob, a); + float br = mob->getBrightness(a); + int overlayColor = getOverlayColor(mob, br, a); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + + if (((overlayColor >> 24) & 0xff) > 0 || mob->hurtTime > 0 || mob->deathTime > 0) + { + glDisable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_EQUAL); + + // 4J - changed these renders to not use the compiled version of their models, because otherwise the render states set + // about (in particular the depth & alpha test) don't work with our command buffer versions + if (mob->hurtTime > 0 || mob->deathTime > 0) + { + glColor4f(br, 0, 0, 0.4f); + model->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a) >= 0) + { + glColor4f(br, 0, 0, 0.4f); + armor->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + if (((overlayColor >> 24) & 0xff) > 0) + { + float r = ((overlayColor >> 16) & 0xff) / 255.0f; + float g = ((overlayColor >> 8) & 0xff) / 255.0f; + float b = ((overlayColor) & 0xff) / 255.0f; + float aa = ((overlayColor >> 24) & 0xff) / 255.0f; + glColor4f(r, g, b, aa); + model->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + for (int i = 0; i < MAX_ARMOR_LAYERS; i++) + { + if (prepareArmorOverlay(mob, i, a) >= 0) + { + glColor4f(r, g, b, aa); + armor->render(mob, wp, ws, bob, headRot - bodyRot, headRotx, _scale, false); + } + } + } + + glDepthFunc(GL_LEQUAL); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glEnable(GL_TEXTURE_2D); + } + glDisable(GL_RESCALE_NORMAL); +// } +//catch (Exception e) { + // // System.out.println("Failed: " + modelNames[model]); + // e.printStackTrace(); + // } + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_CULL_FACE); + + glPopMatrix(); + + MemSect(31); + renderName(mob, x, y, z); + MemSect(0); +} + +void MobRenderer::renderModel(shared_ptr mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale) +{ + shared_ptr player = dynamic_pointer_cast(Minecraft::GetInstance()->player); + + bindTexture(mob->customTextureUrl, mob->getTexture()); + if (!mob->isInvisible()) + { + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); + } + else if ( !mob->isInvisibleTo(player) ) + { + glPushMatrix(); + glColor4f(1, 1, 1, 0.15f); + glDepthMask(false); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GREATER, 1.0f / 255.0f); + model->render(mob, wp, ws, bob, headRotMinusBodyRot, headRotx, scale, true); + glDisable(GL_BLEND); + glAlphaFunc(GL_GREATER, .1f); + glPopMatrix(); + glDepthMask(true); + } + else + { + model->setupAnim(wp, ws, bob, headRotMinusBodyRot, headRotx, scale);//, mob); + } +} + +void MobRenderer::setupPosition(shared_ptr mob, double x, double y, double z) +{ + glTranslatef((float) x, (float) y, (float) z); +} + +void MobRenderer::setupRotations(shared_ptr mob, float bob, float bodyRot, float a) +{ + glRotatef(180 - bodyRot, 0, 1, 0); + if (mob->deathTime > 0) + { + float fall = (mob->deathTime + a - 1) / 20.0f * 1.6f; + fall = (float)sqrt(fall); + if (fall > 1) fall = 1; + glRotatef(fall * getFlipDegrees(mob), 0, 0, 1); + } +} + +float MobRenderer::getAttackAnim(shared_ptr mob, float a) +{ + return mob->getAttackAnim(a); +} + +float MobRenderer::getBob(shared_ptr mob, float a) +{ + return (mob->tickCount + a); +} + +void MobRenderer::additionalRendering(shared_ptr mob, float a) +{ +} + +int MobRenderer::prepareArmorOverlay(shared_ptr mob, int layer, float a) +{ + return prepareArmor(mob, layer, a); +} + +int MobRenderer::prepareArmor(shared_ptr mob, int layer, float a) +{ + return -1; +} + +void MobRenderer::prepareSecondPassArmor(shared_ptr mob, int layer, float a) +{ +} + +float MobRenderer::getFlipDegrees(shared_ptr mob) +{ + return 90; +} + +int MobRenderer::getOverlayColor(shared_ptr mob, float br, float a) +{ + return 0; +} + +void MobRenderer::scale(shared_ptr mob, float a) +{ +} + +void MobRenderer::renderName(shared_ptr mob, double x, double y, double z) +{ + if (Minecraft::renderDebug()) + { + //renderNameTag(mob, _toString(mob->entityId), x, y, z, 64); + } +} + +// 4J Added parameter for color here so that we can colour players names +void MobRenderer::renderNameTag(shared_ptr mob, const wstring& OriginalName, double x, double y, double z, int maxDist, int color /*= 0xffffffff*/) +{ + + if ( app.GetGameSettings(eGameSetting_DisplayHUD)==0 ) + { + // 4J-PB - turn off gamertag render + return; + } + + if(app.GetGameHostOption(eGameHostOption_Gamertags)==0) + { + // turn off gamertags if the host has set them off + return; + } + + float dist = mob->distanceTo(entityRenderDispatcher->cameraEntity); + + if (dist > maxDist) + { + return; + } + + Font *font = getFont(); + + float size = 1.60f; + float s = 1 / 60.0f * size; + + glPushMatrix(); + glTranslatef((float) x + 0, (float) y + 2.3f, (float) z); + glNormal3f(0, 1, 0); + + glRotatef(-this->entityRenderDispatcher->playerRotY, 0, 1, 0); + glRotatef(this->entityRenderDispatcher->playerRotX, 1, 0, 0); + + glScalef(-s, -s, s); + glDisable(GL_LIGHTING); + + // 4J Stu - If it's beyond readable distance, then just render a coloured box + int readableDist = PLAYER_NAME_READABLE_FULLSCREEN; + if( !RenderManager.IsHiDef() ) + { + readableDist = PLAYER_NAME_READABLE_DISTANCE_SD; + } + else if ( app.GetLocalPlayerCount() > 2 ) + { + readableDist = PLAYER_NAME_READABLE_DISTANCE_SPLITSCREEN; + } + + float textOpacity = 1.0f; + if( dist >= readableDist ) + { + int diff = dist - readableDist; + + textOpacity /= (diff/2); + + if( diff > readableDist ) textOpacity = 0.0f; + } + + if( textOpacity < 0.0f ) textOpacity = 0.0f; + if( textOpacity > 1.0f ) textOpacity = 1.0f; + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Tesselator *t = Tesselator::getInstance(); + + int offs = 0; + shared_ptr player = dynamic_pointer_cast(mob); + if (player != NULL && app.isXuidDeadmau5( player->getXuid() ) ) offs = -10; + + wstring playerName; + WCHAR wchName[2]; + +#if defined(__PS3__) || defined(__ORBIS__) + // Check we have all the font characters for this player name + switch(player->GetPlayerNameValidState()) + { + case Player::ePlayerNameValid_NotSet: + if(font->AllCharactersValid(OriginalName)) + { + playerName=OriginalName; + player->SetPlayerNameValidState(true); + } + else + { + memset(wchName,0,sizeof(WCHAR)*2); + swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); + playerName=wchName; + player->SetPlayerNameValidState(false); + } + break; + case Player::ePlayerNameValid_True: + playerName=OriginalName; + break; + case Player::ePlayerNameValid_False: + memset(wchName,0,sizeof(WCHAR)*2); + swprintf(wchName, 2, L"%d",player->getPlayerIndex()+1); + playerName=wchName; + break; + } + +#else + playerName=OriginalName; +#endif + + if( textOpacity > 0.0f ) + { + glColor4f(1.0f,1.0f,1.0f,textOpacity); + + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + + glDisable(GL_TEXTURE_2D); + + t->begin(); + int w = font->width(playerName) / 2; + + if( textOpacity < 1.0f ) + { + t->color(color, 255 * textOpacity); + } + else + { + t->color(0.0f, 0.0f, 0.0f, 0.25f); + } + t->vertex((float)(-w - 1), (float)( -1 + offs), (float)( 0)); + t->vertex((float)(-w - 1), (float)( +8 + offs + 1), (float)( 0)); + t->vertex((float)(+w + 1), (float)( +8 + offs + 1), (float)( 0)); + t->vertex((float)(+w + 1), (float)( -1 + offs), (float)( 0)); + t->end(); + + glEnable(GL_DEPTH_TEST); + glDepthMask(true); + glDepthFunc(GL_ALWAYS); + glLineWidth(2.0f); + t->begin(GL_LINE_STRIP); + t->color(color, 255 * textOpacity); + t->vertex((float)(-w - 1), (float)( -1 + offs), (float)( 0)); + t->vertex((float)(-w - 1), (float)( +8 + offs + 1), (float)( 0)); + t->vertex((float)(+w + 1), (float)( +8 + offs + 1), (float)( 0)); + t->vertex((float)(+w + 1), (float)( -1 + offs), (float)( 0)); + t->vertex((float)(-w - 1), (float)( -1 + offs), (float)( 0)); + t->end(); + glDepthFunc(GL_LEQUAL); + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_TEXTURE_2D); + font->draw(playerName, -font->width(playerName) / 2, offs, 0x20ffffff); + glEnable(GL_DEPTH_TEST); + + glDepthMask(true); + } + + if( textOpacity < 1.0f ) + { + glColor4f(1.0f,1.0f,1.0f,1.0f); + glDisable(GL_TEXTURE_2D); + glDepthFunc(GL_ALWAYS); + t->begin(); + int w = font->width(playerName) / 2; + t->color(color, 255); + t->vertex((float)(-w - 1), (float)( -1 + offs), (float)( 0)); + t->vertex((float)(-w - 1), (float)( +8 + offs), (float)( 0)); + t->vertex((float)(+w + 1), (float)( +8 + offs), (float)( 0)); + t->vertex((float)(+w + 1), (float)( -1 + offs), (float)( 0)); + t->end(); + glDepthFunc(GL_LEQUAL); + glEnable(GL_TEXTURE_2D); + + glTranslatef(0.0f, 0.0f, -0.04f); + } + + if( textOpacity > 0.0f ) + { + int textColor = ( ( (int)(textOpacity*255) << 24 ) | 0xffffff ); + font->draw(playerName, -font->width(playerName) / 2, offs, textColor); + } + + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); + glColor4f(1, 1, 1, 1); + glPopMatrix(); +} diff --git a/Minecraft.Client/MobRenderer.h b/Minecraft.Client/MobRenderer.h new file mode 100644 index 0000000..a8b2b93 --- /dev/null +++ b/Minecraft.Client/MobRenderer.h @@ -0,0 +1,46 @@ +#pragma once +#include "EntityRenderer.h" +class Mob; +using namespace std; + +#define PLAYER_NAME_READABLE_FULLSCREEN 16 +#define PLAYER_NAME_READABLE_DISTANCE_SPLITSCREEN 8 +#define PLAYER_NAME_READABLE_DISTANCE_SD 8 + +// 4J - this used to be a generic : public class MobRenderer extends EntityRenderer +class MobRenderer : public EntityRenderer +{ +private: + static const int MAX_ARMOR_LAYERS = 4; + +protected: + Model *model; + Model *armor; + +public: + MobRenderer(Model *model, float shadow); + virtual void setArmor(Model *armor); +private: + float rotlerp(float from, float to, float a); +public: + virtual void render(shared_ptr mob, double x, double y, double z, float rot, float a); +protected: + virtual void renderModel(shared_ptr mob, float wp, float ws, float bob, float headRotMinusBodyRot, float headRotx, float scale); + virtual void setupPosition(shared_ptr mob, double x, double y, double z); + virtual void setupRotations(shared_ptr mob, float bob, float bodyRot, float a); + virtual float getAttackAnim(shared_ptr mob, float a); + virtual float getBob(shared_ptr mob, float a); + virtual void additionalRendering(shared_ptr mob, float a); + virtual int prepareArmorOverlay(shared_ptr mob, int layer, float a); + virtual int prepareArmor(shared_ptr mob, int layer, float a); + virtual void prepareSecondPassArmor(shared_ptr mob, int layer, float a); + virtual float getFlipDegrees(shared_ptr mob); + virtual int getOverlayColor(shared_ptr mob, float br, float a); + virtual void scale(shared_ptr mob, float a); + virtual void renderName(shared_ptr mob, double x, double y, double z); + virtual void renderNameTag(shared_ptr mob, const wstring& name, double x, double y, double z, int maxDist, int color = 0xff000000); + +public: + // 4J Added + virtual Model *getModel() { return model; } +}; diff --git a/Minecraft.Client/MobSkinMemTextureProcessor.cpp b/Minecraft.Client/MobSkinMemTextureProcessor.cpp new file mode 100644 index 0000000..a6fdf52 --- /dev/null +++ b/Minecraft.Client/MobSkinMemTextureProcessor.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "MobSkinMemTextureProcessor.h" + +BufferedImage *MobSkinMemTextureProcessor::process(BufferedImage *in) +{ + if (in == NULL) return NULL; + + width = 64; + height = 32; + + BufferedImage *out = new BufferedImage(width, height, BufferedImage::TYPE_INT_ARGB); + Graphics *g = out->getGraphics(); + g->drawImage(in, 0, 0, NULL); + g->dispose(); + + pixels = out->getData(); + + setNoAlpha(0, 0, 32, 16); + setForceAlpha(32, 0, 64, 32); + setNoAlpha(0, 16, 64, 32); + bool hasAlpha = false; + for (int x = 32; x < 64; x++) + for (int y = 0; y < 16; y++) + { + int pix = pixels[x + y * 64]; + if (((pix >> 24) & 0xff) < 128) hasAlpha = true; + } + + // 4J-PB - looks like the code below is wrong, and really should be looping from 0 to <32 + if (!hasAlpha) + { + for (int x = 32; x < 64; x++) + for (int y = 0; y < 16; y++) + { + int pix = pixels[x + y * 64]; + if (((pix >> 24) & 0xff) < 128) hasAlpha = true; + } + } + + return out; +} + +void MobSkinMemTextureProcessor::setForceAlpha(int x0, int y0, int x1, int y1) +{ + if (hasAlpha(x0, y0, x1, y1)) return; + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + pixels[x + y * width] &= 0x00ffffff; + } +} + +void MobSkinMemTextureProcessor::setNoAlpha(int x0, int y0, int x1, int y1) +{ + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + pixels[x + y * width] |= 0xff000000; + } +} + +bool MobSkinMemTextureProcessor::hasAlpha(int x0, int y0, int x1, int y1) +{ + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + int pix = pixels[x + y * width]; + if (((pix >> 24) & 0xff) < 128) return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/MobSkinMemTextureProcessor.h b/Minecraft.Client/MobSkinMemTextureProcessor.h new file mode 100644 index 0000000..a28f80a --- /dev/null +++ b/Minecraft.Client/MobSkinMemTextureProcessor.h @@ -0,0 +1,16 @@ +#pragma once +#include "MemTextureProcessor.h" + +class MobSkinMemTextureProcessor : public MemTextureProcessor +{ +private: + int *pixels; + int width, height; +public: + virtual BufferedImage *process(BufferedImage *in); + +private: + void setForceAlpha(int x0, int y0, int x1, int y1); + void setNoAlpha(int x0, int y0, int x1, int y1); + bool hasAlpha(int x0, int y0, int x1, int y1); +}; \ No newline at end of file diff --git a/Minecraft.Client/MobSkinTextureProcessor.cpp b/Minecraft.Client/MobSkinTextureProcessor.cpp new file mode 100644 index 0000000..dffb467 --- /dev/null +++ b/Minecraft.Client/MobSkinTextureProcessor.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "MobSkinTextureProcessor.h" + +BufferedImage *MobSkinTextureProcessor::process(BufferedImage *in) +{ + if (in == NULL) return NULL; + + width = 64; + height = 32; + + BufferedImage *out = new BufferedImage(width, height, BufferedImage::TYPE_INT_ARGB); + Graphics *g = out->getGraphics(); + g->drawImage(in, 0, 0, NULL); + g->dispose(); + + pixels = out->getData(); + + setNoAlpha(0, 0, 32, 16); + setForceAlpha(32, 0, 64, 32); + setNoAlpha(0, 16, 64, 32); + bool hasAlpha = false; + for (int x = 32; x < 64; x++) + for (int y = 0; y < 16; y++) + { + int pix = pixels[x + y * 64]; + if (((pix >> 24) & 0xff) < 128) hasAlpha = true; + } + + if (!hasAlpha) + { + for (int x = 32; x < 64; x++) + for (int y = 0; y < 16; y++) + { + int pix = pixels[x + y * 64]; + if (((pix >> 24) & 0xff) < 128) hasAlpha = true; + } + } + + return out; +} + +void MobSkinTextureProcessor::setForceAlpha(int x0, int y0, int x1, int y1) +{ + if (hasAlpha(x0, y0, x1, y1)) return; + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + pixels[x + y * width] &= 0x00ffffff; + } +} + +void MobSkinTextureProcessor::setNoAlpha(int x0, int y0, int x1, int y1) +{ + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + pixels[x + y * width] |= 0xff000000; + } +} + +bool MobSkinTextureProcessor::hasAlpha(int x0, int y0, int x1, int y1) +{ + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + { + int pix = pixels[x + y * width]; + if (((pix >> 24) & 0xff) < 128) return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.Client/MobSkinTextureProcessor.h b/Minecraft.Client/MobSkinTextureProcessor.h new file mode 100644 index 0000000..ee51a8a --- /dev/null +++ b/Minecraft.Client/MobSkinTextureProcessor.h @@ -0,0 +1,16 @@ +#pragma once +#include "HttpTextureProcessor.h" + +class MobSkinTextureProcessor : public HttpTextureProcessor +{ +private: + int *pixels; + int width, height; +public: + virtual BufferedImage *process(BufferedImage *in); + +private: + void setForceAlpha(int x0, int y0, int x1, int y1); + void setNoAlpha(int x0, int y0, int x1, int y1); + bool hasAlpha(int x0, int y0, int x1, int y1); +}; \ No newline at end of file diff --git a/Minecraft.Client/MobSpawnerRenderer.cpp b/Minecraft.Client/MobSpawnerRenderer.cpp new file mode 100644 index 0000000..4ee772d --- /dev/null +++ b/Minecraft.Client/MobSpawnerRenderer.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "MobSpawnerRenderer.h" +#include "TileEntityRenderDispatcher.h" +#include "EntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" + +void MobSpawnerRenderer::render(shared_ptr _spawner, double x, double y, double z, float a, bool setColor, float alpha, bool useCompiled) +{ + // 4J - dynamic cast required because we aren't using templates/generics in our version + shared_ptr spawner = dynamic_pointer_cast(_spawner); + + glPushMatrix(); + glTranslatef((float) x + 0.5f, (float) y, (float) z + 0.5f); + + shared_ptr e = spawner->getDisplayEntity(); + if (e != NULL) + { + e->setLevel(spawner->level); + float s = 7 / 16.0f; + glTranslatef(0, 0.4f, 0); + glRotatef((float) (spawner->oSpin + (spawner->spin - spawner->oSpin) * a) * 10, 0, 1, 0); + glRotatef(-30, 1, 0, 0); + glTranslatef(0, -0.4f, 0); + glScalef(s, s, s); + e->moveTo(x, y, z, 0, 0); + EntityRenderDispatcher::instance->render(e, 0, 0, 0, 0, a); + } + glPopMatrix(); +} diff --git a/Minecraft.Client/MobSpawnerRenderer.h b/Minecraft.Client/MobSpawnerRenderer.h new file mode 100644 index 0000000..ad7dfe6 --- /dev/null +++ b/Minecraft.Client/MobSpawnerRenderer.h @@ -0,0 +1,11 @@ +#pragma once +#include "TileEntityRenderer.h" +using namespace std; + +class MobSpawnerRenderer : public TileEntityRenderer +{ +private: + unordered_map > models; +public: + virtual void render(shared_ptr _spawner, double x, double y, double z, float a, bool setColor, float alpha=1.0f, bool useCompiled = true); // 4J added setColor param +}; diff --git a/Minecraft.Client/Model.cpp b/Minecraft.Client/Model.cpp new file mode 100644 index 0000000..63ba0c5 --- /dev/null +++ b/Minecraft.Client/Model.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "TexOffs.h" +#include "Model.h" + + +Model::Model() +{ + riding = false; + young=true; + texWidth=64; + texHeight=32; +} + +void Model::setMapTex(wstring id, int x, int y) +{ + mappedTexOffs[id]=new TexOffs(x, y); +} + +TexOffs *Model::getMapTex(wstring id) +{ + // 4J-PB - assuming there will always be this one + return mappedTexOffs[id]; +} \ No newline at end of file diff --git a/Minecraft.Client/Model.h b/Minecraft.Client/Model.h new file mode 100644 index 0000000..e4161d0 --- /dev/null +++ b/Minecraft.Client/Model.h @@ -0,0 +1,34 @@ +#pragma once +using namespace std; +#include "..\Minecraft.World\Random.h" +#include "..\Minecraft.Client\SkinBox.h" +class Mob; +class ModelPart; +class TexOffs; + + +class Model +{ +public: + float attackTime; + bool riding; + vector cubes; + bool young; + unordered_map mappedTexOffs; + int texWidth; + int texHeight; + + Model(); // 4J added + virtual void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) {} + virtual void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0) {} + virtual void prepareMobModel(shared_ptr mob, float time, float r, float a) {} + virtual ModelPart *getRandomCube(Random random) {return cubes.at(random.nextInt((int)cubes.size()));} + virtual ModelPart * AddOrRetrievePart(SKIN_BOX *pBox) { return NULL;} + + void setMapTex(wstring id, int x, int y); + TexOffs *getMapTex(wstring id); + +protected: + float yHeadOffs; + float zHeadOffs; +}; diff --git a/Minecraft.Client/ModelPart.cpp b/Minecraft.Client/ModelPart.cpp new file mode 100644 index 0000000..8938662 --- /dev/null +++ b/Minecraft.Client/ModelPart.cpp @@ -0,0 +1,322 @@ +#include "stdafx.h" +#include "TexOffs.h" +#include "ModelPart.h" +#include "Cube.h" + +const float ModelPart::RAD = (180.0f / PI); + +void ModelPart::_init() +{ + xTexSize = 64.0f; + yTexSize = 32.0f; + list = 0; + compiled=false; + bMirror = false; + visible = true; + neverRender = false; + x=y=z = 0.0f; + xRot=yRot=zRot = 0.0f; +} + +ModelPart::ModelPart() +{ + _init(); +} + +ModelPart::ModelPart(Model *model, const wstring& id) +{ + construct(model, id); +} + +ModelPart::ModelPart(Model *model) +{ + construct(model); +} + +ModelPart::ModelPart(Model *model, int xTexOffs, int yTexOffs) +{ + construct(model, xTexOffs, yTexOffs); +} + + +void ModelPart::construct(Model *model, const wstring& id) +{ + _init(); + this->model = model; + model->cubes.push_back(this); + this->id = id; + setTexSize(model->texWidth, model->texHeight); +} + +void ModelPart::construct(Model *model) +{ + _init(); + construct(model, L""); +} + +void ModelPart::construct(Model *model, int xTexOffs, int yTexOffs) +{ + _init(); + construct(model); + texOffs(xTexOffs, yTexOffs); +} + + +void ModelPart::addChild(ModelPart *child) +{ + //if (children == NULL) children = new ModelPartArray; + children.push_back(child); +} + +ModelPart * ModelPart::retrieveChild(SKIN_BOX *pBox) +{ + for(AUTO_VAR(it, children.begin()); it != children.end(); ++it) + { + ModelPart *child=*it; + + for(AUTO_VAR(itcube, child->cubes.begin()); itcube != child->cubes.end(); ++itcube) + { + Cube *pCube=*itcube; + + if((pCube->x0==pBox->fX) && + (pCube->y0==pBox->fY) && + (pCube->z0==pBox->fZ) && + (pCube->x1==(pBox->fX + pBox->fW)) && + (pCube->y1==(pBox->fY + pBox->fH)) && + (pCube->z1==(pBox->fZ + pBox->fD)) + ) + { + return child; + break; + } + } + } + + return NULL; +} + +ModelPart *ModelPart::mirror() +{ + bMirror = !bMirror; + return this; +} + +ModelPart *ModelPart::texOffs(int xTexOffs, int yTexOffs) +{ + this->xTexOffs = xTexOffs; + this->yTexOffs = yTexOffs; + return this; +} + +ModelPart *ModelPart::addBox(wstring id, float x0, float y0, float z0, int w, int h, int d) +{ + id = this->id + L"." + id; + TexOffs *offs = model->getMapTex(id); + texOffs(offs->x, offs->y); + cubes.push_back((new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0))->setId(id)); + return this; +} + +ModelPart *ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d) +{ + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0)); + return this; +} + +void ModelPart::addHumanoidBox(float x0, float y0, float z0, int w, int h, int d, float g) +{ + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, g, 63, true)); +} + +ModelPart *ModelPart::addBoxWithMask(float x0, float y0, float z0, int w, int h, int d, int faceMask) +{ + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, 0, faceMask)); + return this; +} + +void ModelPart::addBox(float x0, float y0, float z0, int w, int h, int d, float g) +{ + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, g)); +} + + +void ModelPart::addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex) +{ + cubes.push_back(new Cube(this, xTexOffs, yTexOffs, x0, y0, z0, w, h, d, (float)tex)); +} + +void ModelPart::setPos(float x, float y, float z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +void ModelPart::render(float scale, bool usecompiled, bool bHideParentBodyPart) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + if (xRot != 0 || yRot != 0 || zRot != 0) + { + glPushMatrix(); + glTranslatef(x * scale, y * scale, z * scale); + if (zRot != 0) glRotatef(zRot * RAD, 0, 0, 1); + if (yRot != 0) glRotatef(yRot * RAD, 0, 1, 0); + if (xRot != 0) glRotatef(xRot * RAD, 1, 0, 0); + + if(!bHideParentBodyPart) + { + if( usecompiled ) + { + glCallList(list); + } + else + { + Tesselator *t = Tesselator::getInstance(); + for (unsigned int i = 0; i < cubes.size(); i++) + { + cubes[i]->render(t, scale); + } + } + } + //if (children != NULL) + { + for (unsigned int i = 0; i < children.size(); i++) + { + children.at(i)->render(scale,usecompiled); + } + } + + glPopMatrix(); + } + else if (x != 0 || y != 0 || z != 0) + { + glTranslatef(x * scale, y * scale, z * scale); + if(!bHideParentBodyPart) + { + if( usecompiled ) + { + glCallList(list); + } + else + { + Tesselator *t = Tesselator::getInstance(); + for (unsigned int i = 0; i < cubes.size(); i++) + { + cubes[i]->render(t, scale); + } + } + } + //if (children != NULL) + { + for (unsigned int i = 0; i < children.size(); i++) + { + children.at(i)->render(scale,usecompiled); + } + } + glTranslatef(-x * scale, -y * scale, -z * scale); + } + else + { + if(!bHideParentBodyPart) + { + if( usecompiled ) + { + glCallList(list); + } + else + { + Tesselator *t = Tesselator::getInstance(); + for (unsigned int i = 0; i < cubes.size(); i++) + { + cubes[i]->render(t, scale); + } + } + } + //if (children != NULL) + { + for (unsigned int i = 0; i < children.size(); i++) + { + children.at(i)->render(scale,usecompiled); + } + } + } +} + +void ModelPart::renderRollable(float scale, bool usecompiled) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + glPushMatrix(); + glTranslatef(x * scale, y * scale, z * scale); + if (yRot != 0) glRotatef(yRot * RAD, 0, 1, 0); + if (xRot != 0) glRotatef(xRot * RAD, 1, 0, 0); + if (zRot != 0) glRotatef(zRot * RAD, 0, 0, 1); + glCallList(list); + glPopMatrix(); + +} + +void ModelPart::translateTo(float scale) +{ + if (neverRender) return; + if (!visible) return; + if (!compiled) compile(scale); + + if (xRot != 0 || yRot != 0 || zRot != 0) + { + glTranslatef(x * scale, y * scale, z * scale); + if (zRot != 0) glRotatef(zRot * RAD, 0, 0, 1); + if (yRot != 0) glRotatef(yRot * RAD, 0, 1, 0); + if (xRot != 0) glRotatef(xRot * RAD, 1, 0, 0); + } + else if (x != 0 || y != 0 || z != 0) + { + glTranslatef(x * scale, y * scale, z * scale); + } + else + { + } +} + +void ModelPart::compile(float scale) +{ + list = MemoryTracker::genLists(1); + + glNewList(list, GL_COMPILE); + // Set a few render states that aren't configured by default + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(true); + Tesselator *t = Tesselator::getInstance(); + + for (unsigned int i = 0; i < cubes.size(); i++) + { + cubes.at(i)->render(t, scale); + } + + glEndList(); + + compiled = true; +} + +ModelPart *ModelPart::setTexSize(int xs, int ys) +{ + this->xTexSize = (float)xs; + this->yTexSize = (float)ys; + return this; +} + +void ModelPart::mimic(ModelPart *o) +{ + x = o->x; + y = o->y; + z = o->z; + xRot = o->xRot; + yRot = o->yRot; + zRot = o->zRot; +} diff --git a/Minecraft.Client/ModelPart.h b/Minecraft.Client/ModelPart.h new file mode 100644 index 0000000..65d6a03 --- /dev/null +++ b/Minecraft.Client/ModelPart.h @@ -0,0 +1,62 @@ +#pragma once +#include "..\Minecraft.World\ArrayWithLength.h" +#include "Vertex.h" +#include "Polygon.h" +#include "Model.h" +#include "..\Minecraft.Client\SkinBox.h" + +class Cube; + +class ModelPart +{ +public: + float xTexSize; + float yTexSize; + float x, y, z; + float xRot, yRot, zRot; + bool bMirror; + bool visible; + bool neverRender; + vector cubes; + vector children; + static const float RAD; + +private: + wstring id; + int xTexOffs, yTexOffs; + boolean compiled; + int list; + Model *model; + +public: + void _init(); // 4J added + ModelPart(); + ModelPart(Model *model, const wstring &id); + ModelPart(Model *model); + ModelPart(Model *model, int xTexOffs, int yTexOffs); + + // MGH - had to add these for PS3, as calling constructors from others was only introduced in c++11 - https://en.wikipedia.org/wiki/C++11#Object_construction_improvement + void construct(Model *model, const wstring &id); + void construct(Model *model); + void construct(Model *model, int xTexOffs, int yTexOffs); + + void addChild(ModelPart *child); + ModelPart * retrieveChild(SKIN_BOX *pBox); + ModelPart *mirror(); + ModelPart *texOffs(int xTexOffs, int yTexOffs); + ModelPart *addBox(wstring id, float x0, float y0, float z0, int w, int h, int d); + ModelPart *addBox(float x0, float y0, float z0, int w, int h, int d); + ModelPart *addBoxWithMask(float x0, float y0, float z0, int w, int h, int d, int faceMask); // 4J added + void addBox(float x0, float y0, float z0, int w, int h, int d, float g); + void addHumanoidBox(float x0, float y0, float z0, int w, int h, int d, float g); // 4J - to flip the poly 3 uvs so the skin maps correctly + void addTexBox(float x0, float y0, float z0, int w, int h, int d, int tex); + void setPos(float x, float y, float z); + void render(float scale, bool usecompiled,bool bHideParentBodyPart=false); + void renderRollable(float scale, bool usecompiled); + void translateTo(float scale); + ModelPart *setTexSize(int xs, int ys); + void mimic(ModelPart *o); + void compile(float scale); + int getfU() {return xTexOffs;} + int getfV() {return yTexOffs;} + }; diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp new file mode 100644 index 0000000..8c6c90e --- /dev/null +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "MultiPlayerChunkCache.h" +#include "ServerChunkCache.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\Arrays.h" +#include "..\Minecraft.World\StringHelpers.h" +#include "MinecraftServer.h" +#include "ServerLevel.h" +#include "..\Minecraft.World\Tile.h" +#include "..\Minecraft.World\WaterLevelChunk.h" + +MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) +{ + XZSIZE = level->dimension->getXZSize(); // 4J Added + XZOFFSET = XZSIZE/2; // 4J Added + m_XZSize = XZSIZE; + hasData = new bool[XZSIZE * XZSIZE]; + memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE); + + emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0); + + // For normal world dimension, create a chunk that can be used to create the illusion of infinite water at the edge of the world + if( level->dimension->id == 0 ) + { + byteArray bytes = byteArray(16 * 16 * 128); + + // Superflat.... make grass, not water... + if(level->getLevelData()->getGenerator() == LevelType::lvl_flat) + { + for( int x = 0; x < 16; x++ ) + for( int y = 0; y < 128; y++ ) + for( int z = 0; z < 16; z++ ) + { + unsigned char tileId = 0; + if( y == 3 ) tileId = Tile::grass_Id; + else if( y <= 2 ) tileId = Tile::dirt_Id; + + bytes[x << 11 | z << 7 | y] = tileId; + } + } + else + { + for( int x = 0; x < 16; x++ ) + for( int y = 0; y < 128; y++ ) + for( int z = 0; z < 16; z++ ) + { + unsigned char tileId = 0; + if( y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id; + else if( y < level->getSeaLevel() ) tileId = Tile::calmWater_Id; + + bytes[x << 11 | z << 7 | y] = tileId; + } + } + + waterChunk = new WaterLevelChunk(level, bytes, 0, 0); + + delete[] bytes.data; + + if(level->getLevelData()->getGenerator() == LevelType::lvl_flat) + { + for( int x = 0; x < 16; x++ ) + for( int y = 0; y < 128; y++ ) + for( int z = 0; z < 16; z++ ) + { + if( y >= 3 ) + { + ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15); + } + } + } + else + { + for( int x = 0; x < 16; x++ ) + for( int y = 0; y < 128; y++ ) + for( int z = 0; z < 16; z++ ) + { + if( y >= ( level->getSeaLevel() - 1 ) ) + { + ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15); + } + else + { + ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,2); + } + } + } + } + else + { + waterChunk = NULL; + } + + this->level = level; + + this->cache = new LevelChunk *[XZSIZE * XZSIZE]; + memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *)); + InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); +} + +MultiPlayerChunkCache::~MultiPlayerChunkCache() +{ + delete emptyChunk; + delete waterChunk; + delete cache; + delete hasData; + + AUTO_VAR(itEnd, loadedChunkList.end()); + for (AUTO_VAR(it, loadedChunkList.begin()); it != itEnd; it++) + delete *it; + + DeleteCriticalSection(&m_csLoadCreate); +} + + +bool MultiPlayerChunkCache::hasChunk(int x, int z) +{ + // This cache always claims to have chunks, although it might actually just return empty data if it doesn't have anything + return true; +} + +// 4J added - find out if we actually really do have a chunk in our cache +bool MultiPlayerChunkCache::reallyHasChunk(int x, int z) +{ + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level - if we aren't, then consider that we do have that chunk as we'll be able to use the water chunk there + if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true; + if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true; + int idx = ix * XZSIZE + iz; + + LevelChunk *chunk = cache[idx]; + if( chunk == NULL ) + { + return false; + } + return hasData[idx]; +} + +void MultiPlayerChunkCache::drop(int x, int z) +{ + // 4J Stu - We do want to drop any entities in the chunks, especially for the case when a player is dead as they will + // not get the RemoveEntity packet if an entity is removed. + LevelChunk *chunk = getChunk(x, z); + if (!chunk->isEmpty()) + { + // Added parameter here specifies that we don't want to delete tile entities, as they won't get recreated unless they've got update packets + // The tile entities are in general only created on the client by virtue of the chunk rebuild + chunk->unload(false); + + // 4J - We just want to clear out the entities in the chunk, but everything else should be valid + chunk->loaded = true; + } +} + +LevelChunk *MultiPlayerChunkCache::create(int x, int z) +{ + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level + if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); + if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); + int idx = ix * XZSIZE + iz; + LevelChunk *chunk = cache[idx]; + LevelChunk *lastChunk = chunk; + + if( chunk == NULL ) + { + EnterCriticalSection(&m_csLoadCreate); + + //LevelChunk *chunk; + if( g_NetworkManager.IsHost() ) // force here to disable sharing of data + { + // 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted. + if (MinecraftServer::getInstance()->serverHalted()) return NULL; + + // If we're the host, then don't create the chunk, share data from the server's copy +#ifdef _LARGE_WORLDS + LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z); +#else + LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z); +#endif + chunk = new LevelChunk(level, x, z, serverChunk); + // Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it + level->setTilesDirty( x * 16 , 0 , z * 16 , x * 16 + 15, 127, z * 16 + 15); + hasData[idx] = true; + } + else + { + // Passing an empty array into the LevelChunk ctor, which it now detects and sets up the chunk as compressed & empty + byteArray bytes; + + chunk = new LevelChunk(level, bytes, x, z); + + // 4J - changed to use new methods for lighting + chunk->setSkyLightDataAllBright(); +// Arrays::fill(chunk->skyLight->data, (byte) 255); + } + + chunk->loaded = true; + + LeaveCriticalSection(&m_csLoadCreate); + +#if ( defined _WIN64 || defined __LP64__ ) + if( InterlockedCompareExchangeRelease64((LONG64 *)&cache[idx],(LONG64)chunk,(LONG64)lastChunk) == (LONG64)lastChunk ) +#else + if( InterlockedCompareExchangeRelease((LONG *)&cache[idx],(LONG)chunk,(LONG)lastChunk) == (LONG)lastChunk ) +#endif // _DURANGO + { + // If we're sharing with the server, we'll need to calculate our heightmap now, which isn't shared. If we aren't sharing with the server, + // then this will be calculated when the chunk data arrives. + if( g_NetworkManager.IsHost() ) + { + chunk->recalcHeightmapOnly(); + } + + // Successfully updated the cache + EnterCriticalSection(&m_csLoadCreate); + loadedChunkList.push_back(chunk); + LeaveCriticalSection(&m_csLoadCreate); + } + else + { + // Something else must have updated the cache. Return that chunk and discard this one. This really shouldn't be happening + // in multiplayer + delete chunk; + return cache[idx]; + } + + } + else + { + chunk->load(); + } + + return chunk; +} + +LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z) +{ + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level + if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); + if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk ); + int idx = ix * XZSIZE + iz; + + LevelChunk *chunk = cache[idx]; + if( chunk == NULL ) + { + return emptyChunk; + } + else + { + return chunk; + } +} + +bool MultiPlayerChunkCache::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool MultiPlayerChunkCache::tick() +{ + return false; +} + +bool MultiPlayerChunkCache::shouldSave() +{ + return false; +} + +void MultiPlayerChunkCache::postProcess(ChunkSource *parent, int x, int z) +{ +} + +vector *MultiPlayerChunkCache::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + return NULL; +} + +TilePos *MultiPlayerChunkCache::findNearestMapFeature(Level *level, const wstring &featureName, int x, int y, int z) +{ + return NULL; +} + +wstring MultiPlayerChunkCache::gatherStats() +{ + EnterCriticalSection(&m_csLoadCreate); + int size = (int)loadedChunkList.size(); + LeaveCriticalSection(&m_csLoadCreate); + return L"MultiplayerChunkCache: " + _toString(size); + +} + +void MultiPlayerChunkCache::dataReceived(int x, int z) +{ + int ix = x + XZOFFSET; + int iz = z + XZOFFSET; + // Check we're in range of the stored level + if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return; + if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return; + int idx = ix * XZSIZE + iz; + hasData[idx] = true; +} \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h new file mode 100644 index 0000000..8fc427a --- /dev/null +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -0,0 +1,47 @@ +#pragma once +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\RandomLevelSource.h" + +using namespace std; +class ServerChunkCache; + +// 4J - various alterations here to make this thread safe, and operate as a fixed sized cache +class MultiPlayerChunkCache : public ChunkSource +{ + friend class LevelRenderer; +private: + LevelChunk *emptyChunk; + LevelChunk *waterChunk; + + vector loadedChunkList; + + LevelChunk **cache; + // 4J - added for multithreaded support + CRITICAL_SECTION m_csLoadCreate; + // 4J - size of cache is defined by size of one side - must be even + int XZSIZE; + int XZOFFSET; + bool *hasData; + + Level *level; + +public: + MultiPlayerChunkCache(Level *level); + ~MultiPlayerChunkCache(); + virtual bool hasChunk(int x, int z); + virtual bool reallyHasChunk(int x, int z); + virtual void drop(int x, int z); + virtual LevelChunk *create(int x, int z); + virtual LevelChunk *getChunk(int x, int z); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual void postProcess(ChunkSource *parent, int x, int z); + virtual wstring gatherStats(); + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring &featureName, int x, int y, int z); + virtual void dataReceived(int x, int z); // 4J added + + virtual LevelChunk **getCache() { return cache; } // 4J added +}; \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerGameMode.cpp b/Minecraft.Client/MultiPlayerGameMode.cpp new file mode 100644 index 0000000..4f9bb22 --- /dev/null +++ b/Minecraft.Client/MultiPlayerGameMode.cpp @@ -0,0 +1,487 @@ +#include "stdafx.h" +#include "MultiPlayerGameMode.h" +#include "CreativeMode.h" +#include "MultiPlayerLocalPlayer.h" +#include "MultiPlayerLevel.h" +#include "Minecraft.h" +#include "ClientConnection.h" +#include "LevelRenderer.h" +#include "Common\Network\GameNetworkManager.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.item.h" +#include "..\Minecraft.World\net.minecraft.world.entity.player.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.h" + +MultiPlayerGameMode::MultiPlayerGameMode(Minecraft *minecraft, ClientConnection *connection) +{ + // 4J - added initialisers + xDestroyBlock = -1; + yDestroyBlock = -1; + zDestroyBlock = -1; + destroyProgress = 0; + oDestroyProgress = 0; + destroyTicks = 0; + destroyDelay = 0; + isDestroying = false; + carriedItem = 0; + localPlayerMode = GameType::SURVIVAL; + this->minecraft = minecraft; + this->connection = connection; +} + +void MultiPlayerGameMode::creativeDestroyBlock(Minecraft *minecraft, MultiPlayerGameMode *gameMode, int x, int y, int z, int face) +{ + if (!minecraft->level->extinguishFire(minecraft->player, x, y, z, face)) + { + gameMode->destroyBlock(x, y, z, face); + } +} + +void MultiPlayerGameMode::adjustPlayer(shared_ptr player) +{ + localPlayerMode->updatePlayerAbilities(&player->abilities); +} + +bool MultiPlayerGameMode::isCutScene() +{ + return false; +} + +void MultiPlayerGameMode::setLocalMode(GameType *mode) +{ + localPlayerMode = mode; + localPlayerMode->updatePlayerAbilities(&minecraft->player->abilities); +} + +void MultiPlayerGameMode::initPlayer(shared_ptr player) +{ + player->yRot = -180; +} + +bool MultiPlayerGameMode::canHurtPlayer() +{ + return localPlayerMode->isSurvival(); +} + +bool MultiPlayerGameMode::destroyBlock(int x, int y, int z, int face) +{ + if (localPlayerMode->isReadOnly()) + { + return false; + } + + Level *level = minecraft->level; + Tile *oldTile = Tile::tiles[level->getTile(x, y, z)]; + + if (oldTile == NULL) return false; + +#ifdef _WINDOWS64 + if (g_NetworkManager.IsHost()) + { + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT)); + return true; + } +#endif + + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, x, y, z, oldTile->id + (level->getData(x, y, z) << Tile::TILE_NUM_SHIFT)); + + int data = level->getData(x, y, z); + bool changed = level->setTile(x, y, z, 0); + if (changed) + { + oldTile->destroy(level, x, y, z, data); + } + + if (!localPlayerMode->isCreative()) + { + shared_ptr item = minecraft->player->getSelectedItem(); + if (item != NULL) + { + item->mineBlock(level, oldTile->id, x, y, z, minecraft->player); + if (item->count == 0) + { + minecraft->player->removeSelectedItem(); + } + } + } + + return changed; +} + +void MultiPlayerGameMode::startDestroyBlock(int x, int y, int z, int face) +{ + if(!minecraft->player->isAllowedToMine()) return; + if (localPlayerMode->isReadOnly()) + { + return; + } + + if (localPlayerMode->isCreative()) + { + connection->send(shared_ptr( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) )); + creativeDestroyBlock(minecraft, this, x, y, z, face); + destroyDelay = 5; + } + else if (!isDestroying || x != xDestroyBlock || y != yDestroyBlock || z != zDestroyBlock) + { + connection->send( shared_ptr( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) ) ); + int t = minecraft->level->getTile(x, y, z); + if (t > 0 && destroyProgress == 0) Tile::tiles[t]->attack(minecraft->level, x, y, z, minecraft->player); + if (t > 0 && + (Tile::tiles[t]->getDestroyProgress(minecraft->player, minecraft->player->level, x, y, z) >= 1 || + (app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, (int)(destroyProgress * 10) - 1); + } + } + +} + +void MultiPlayerGameMode::stopDestroyBlock() +{ + if (isDestroying) + { + connection->send(shared_ptr(new PlayerActionPacket(PlayerActionPacket::ABORT_DESTROY_BLOCK, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1))); + } + + isDestroying = false; + destroyProgress = 0; + minecraft->level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, -1); +} + +void MultiPlayerGameMode::continueDestroyBlock(int x, int y, int z, int face) +{ + if(!minecraft->player->isAllowedToMine()) return; + ensureHasSentCarriedItem(); +// connection.send(new PlayerActionPacket(PlayerActionPacket.CONTINUE_DESTROY_BLOCK, x, y, z, face)); + + if (destroyDelay > 0) + { + destroyDelay--; + return; + } + + if (localPlayerMode->isCreative()) + { + destroyDelay = 5; + connection->send(shared_ptr( new PlayerActionPacket(PlayerActionPacket::START_DESTROY_BLOCK, x, y, z, face) ) ); + creativeDestroyBlock(minecraft, this, x, y, z, face); + return; + } + + if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock) + { + int t = minecraft->level->getTile(x, y, z); + if (t == 0) + { + isDestroying = false; + return; + } + Tile *tile = Tile::tiles[t]; + + destroyProgress += tile->getDestroyProgress(minecraft->player, minecraft->player->level, x, y, z); + + if (destroyTicks % 4 == 0) + { + if (tile != NULL) + { + int iStepSound=tile->soundType->getStepSound(); + + minecraft->soundEngine->play(iStepSound, x + 0.5f, y + 0.5f, z + 0.5f, (tile->soundType->getVolume() + 1) / 8, tile->soundType->getPitch() * 0.5f); + } + } + + destroyTicks++; + + if (destroyProgress >= 1) + { + isDestroying = false; + connection->send( shared_ptr( new PlayerActionPacket(PlayerActionPacket::STOP_DESTROY_BLOCK, x, y, z, face) ) ); + destroyBlock(x, y, z, face); + destroyProgress = 0; + oDestroyProgress = 0; + destroyTicks = 0; + destroyDelay = 5; + } + + minecraft->level->destroyTileProgress(minecraft->player->entityId, xDestroyBlock, yDestroyBlock, zDestroyBlock, (int)(destroyProgress * 10) - 1); + } + else + { + startDestroyBlock(x, y, z, face); + } + +} + +float MultiPlayerGameMode::getPickRange() +{ + if (localPlayerMode->isCreative()) + { + return 5.0f; + } + return 4.5f; +} + +void MultiPlayerGameMode::tick() +{ + ensureHasSentCarriedItem(); + oDestroyProgress = destroyProgress; + //minecraft->soundEngine->playMusicTick(); +} + +void MultiPlayerGameMode::ensureHasSentCarriedItem() +{ + int newItem = minecraft->player->inventory->selected; + if (newItem != carriedItem) + { + carriedItem = newItem; + connection->send( shared_ptr( new SetCarriedItemPacket(carriedItem) ) ); + } +} + +bool MultiPlayerGameMode::useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly, bool *pbUsedItem) +{ + if( pbUsedItem ) *pbUsedItem = false; // Did we actually use the held item? + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if(!bTestUseOnly) + { + ensureHasSentCarriedItem(); + } + float clickX = (float) hit->x - x; + float clickY = (float) hit->y - y; + float clickZ = (float) hit->z - z; + bool didSomething = false; + int t = level->getTile(x, y, z); + + if (t > 0 && player->isAllowedToUse(Tile::tiles[t])) + { + if(bTestUseOnly) + { + switch(t) + { + case Tile::recordPlayer_Id: + case Tile::bed_Id: // special case for a bed + if (Tile::tiles[t]->TestUse(level, x, y, z, player )) + { + return true; + } + else if (t==Tile::bed_Id) // 4J-JEV: You can still use items on record players (ie. set fire to them). + { + // bed is too far away, or something + return false; + } + break; + default: + if (Tile::tiles[t]->TestUse()) return true; + break; + } + } + else + { + if (Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ)) didSomething = true; + } + } + + if (!didSomething && item != NULL && dynamic_cast(item->getItem())) + { + TileItem *tile = dynamic_cast(item->getItem()); + if (!tile->mayPlace(level, x, y, z, face, player, item)) return false; + } + + // 4J Stu - In Java we send the use packet before the above check for item being NULL + // so the following never gets executed but the packet still gets sent (for opening chests etc) + if(item != NULL) + { + if(!didSomething && player->isAllowedToUse(item)) + { + if (localPlayerMode->isCreative()) + { + int aux = item->getAuxValue(); + int count = item->count; + didSomething = item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnly); + item->setAuxValue(aux); + item->count = count; + } + else + { + didSomething = item->useOn(player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnly); + } + if( didSomething ) + { + if( pbUsedItem ) *pbUsedItem = true; + } + } + } + else + { + // 4J - Bit of a hack, however seems preferable to any larger changes which would have more chance of causing unwanted side effects. + // If we aren't going to be actually performing the use method locally, then call this method with its "soundOnly" parameter set to true. + // This is an addition from the java version, and as its name suggests, doesn't actually perform the use locally but just makes any sounds that + // are meant to be directly caused by this. If we don't do this, then the sounds never happen as the tile's use method is only called on the + // server, and that won't allow any sounds that are directly made, or broadcast back level events to us that would make the sound, since we are + // the source of the event. + if( ( t > 0 ) && ( !bTestUseOnly ) && player->isAllowedToUse(Tile::tiles[t]) ) + { + Tile::tiles[t]->use(level, x, y, z, player, face, clickX, clickY, clickZ, true); + } + } + + // 4J Stu - Do the action before we send the packet, so that our predicted count is sent in the packet and the server + // doesn't think it has to update us + // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water. + if(!bTestUseOnly) + { + connection->send( shared_ptr( new UseItemPacket(x, y, z, face, player->inventory->getSelected(), clickX, clickY, clickZ) ) ); + } + return didSomething; +} + +bool MultiPlayerGameMode::useItem(shared_ptr player, Level *level, shared_ptr item, bool bTestUseOnly) +{ + if(!player->isAllowedToUse(item)) return false; + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if(!bTestUseOnly) + { + ensureHasSentCarriedItem(); + } + + // 4J Stu - Do the action before we send the packet, so that our predicted count is sent in the packet and the server + // doesn't think it has to update us, or can update us if we are wrong + // Fix for #13120 - Using a bucket of water or lava in the spawn area (centre of the map) causes the inventory to get out of sync + bool result = false; + + // 4J-PB added for tooltips to test use only + if(bTestUseOnly) + { + result = item->TestUse(level, player); + } + else + { + int oldCount = item->count; + shared_ptr itemInstance = item->use(level, player); + if ((itemInstance != NULL && itemInstance != item) || (itemInstance != NULL && itemInstance->count != oldCount)) + { + player->inventory->items[player->inventory->selected] = itemInstance; + if (itemInstance->count == 0) + { + player->inventory->items[player->inventory->selected] = nullptr; + } + result = true; + } + } + + if(!bTestUseOnly) + { + connection->send( shared_ptr( new UseItemPacket(-1, -1, -1, 255, player->inventory->getSelected(), 0, 0, 0) ) ); + } + return result; +} + +shared_ptr MultiPlayerGameMode::createPlayer(Level *level) +{ + return shared_ptr( new MultiplayerLocalPlayer(minecraft, level, minecraft->user, connection) ); +} + +void MultiPlayerGameMode::attack(shared_ptr player, shared_ptr entity) +{ + ensureHasSentCarriedItem(); + connection->send( shared_ptr( new InteractPacket(player->entityId, entity->entityId, InteractPacket::ATTACK) ) ); + player->attack(entity); +} + +bool MultiPlayerGameMode::interact(shared_ptr player, shared_ptr entity) +{ + ensureHasSentCarriedItem(); + connection->send(shared_ptr( new InteractPacket(player->entityId, entity->entityId, InteractPacket::INTERACT) ) ); + return player->interact(entity); +} + +shared_ptr MultiPlayerGameMode::handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, bool quickKeyHeld, shared_ptr player) +{ + short changeUid = player->containerMenu->backup(player->inventory); + + shared_ptr clicked = player->containerMenu->clicked(slotNum, buttonNum, quickKeyHeld?AbstractContainerMenu::CLICK_QUICK_MOVE:AbstractContainerMenu::CLICK_PICKUP, player); + connection->send( shared_ptr( new ContainerClickPacket(containerId, slotNum, buttonNum, quickKeyHeld, clicked, changeUid) ) ); + + return clicked; +} + +void MultiPlayerGameMode::handleInventoryButtonClick(int containerId, int buttonId) +{ + connection->send(shared_ptr( new ContainerButtonClickPacket(containerId, buttonId) )); +} + +void MultiPlayerGameMode::handleCreativeModeItemAdd(shared_ptr clicked, int slot) +{ + if (localPlayerMode->isCreative()) + { + connection->send(shared_ptr( new SetCreativeModeSlotPacket(slot, clicked) ) ); + } +} + +void MultiPlayerGameMode::handleCreativeModeItemDrop(shared_ptr clicked) +{ + if (localPlayerMode->isCreative() && clicked != NULL) + { + connection->send(shared_ptr( new SetCreativeModeSlotPacket(-1, clicked) ) ); + } +} + +void MultiPlayerGameMode::releaseUsingItem(shared_ptr player) +{ + ensureHasSentCarriedItem(); + connection->send(shared_ptr( new PlayerActionPacket(PlayerActionPacket::RELEASE_USE_ITEM, 0, 0, 0, 255) ) ); + player->releaseUsingItem(); +} + +bool MultiPlayerGameMode::hasExperience() +{ + return true; +} + +bool MultiPlayerGameMode::hasMissTime() +{ + return !localPlayerMode->isCreative(); +} + +bool MultiPlayerGameMode::hasInfiniteItems() +{ + return localPlayerMode->isCreative(); +} + +bool MultiPlayerGameMode::hasFarPickRange() +{ + return localPlayerMode->isCreative(); +} + +bool MultiPlayerGameMode::handleCraftItem(int recipe, shared_ptr player) +{ + short changeUid = player->containerMenu->backup(player->inventory); + + connection->send( shared_ptr( new CraftItemPacket(recipe, changeUid) ) ); + + return true; +} + +void MultiPlayerGameMode::handleDebugOptions(unsigned int uiVal, shared_ptr player) +{ + player->SetDebugOptions(uiVal); + connection->send( shared_ptr( new DebugOptionsPacket(uiVal) ) ); +} diff --git a/Minecraft.Client/MultiPlayerGameMode.h b/Minecraft.Client/MultiPlayerGameMode.h new file mode 100644 index 0000000..c84410f --- /dev/null +++ b/Minecraft.Client/MultiPlayerGameMode.h @@ -0,0 +1,67 @@ +#pragma once +#include "GameMode.h" +class ClientConnection; +class GameType; +class Vec3; + +class MultiPlayerGameMode +{ +private: + int xDestroyBlock; + int yDestroyBlock; + int zDestroyBlock; + float destroyProgress; + float oDestroyProgress; + int destroyTicks; // 4J was float but doesn't seem to need to be + int destroyDelay; + bool isDestroying; + GameType *localPlayerMode; + ClientConnection *connection; + +protected: + Minecraft *minecraft; + +public: + MultiPlayerGameMode(Minecraft *minecraft, ClientConnection *connection); + static void creativeDestroyBlock(Minecraft *minecraft, MultiPlayerGameMode *gameMode, int x, int y, int z, int face); + void adjustPlayer(shared_ptr player); + bool isCutScene(); + void setLocalMode(GameType *mode); + virtual void initPlayer(shared_ptr player); + virtual bool canHurtPlayer(); + virtual bool destroyBlock(int x, int y, int z, int face); + virtual void startDestroyBlock(int x, int y, int z, int face); + virtual void stopDestroyBlock(); + virtual void continueDestroyBlock(int x, int y, int z, int face); + virtual float getPickRange(); + virtual void tick(); +private: + int carriedItem; + +private: + void ensureHasSentCarriedItem(); +public: + virtual bool useItemOn(shared_ptr player, Level *level, shared_ptr item, int x, int y, int z, int face, Vec3 *hit, bool bTestUseOnly=false, bool *pbUsedItem=NULL); + virtual bool useItem(shared_ptr player, Level *level, shared_ptr item, bool bTestUseOnly=false); + virtual shared_ptr createPlayer(Level *level); + virtual void attack(shared_ptr player, shared_ptr entity); + virtual bool interact(shared_ptr player, shared_ptr entity); + virtual shared_ptr handleInventoryMouseClick(int containerId, int slotNum, int buttonNum, bool quickKeyHeld, shared_ptr player); + virtual void handleInventoryButtonClick(int containerId, int buttonId); + virtual void handleCreativeModeItemAdd(shared_ptr clicked, int slot); + virtual void handleCreativeModeItemDrop(shared_ptr clicked); + virtual void releaseUsingItem(shared_ptr player); + virtual bool hasExperience(); + virtual bool hasMissTime(); + virtual bool hasInfiniteItems(); + virtual bool hasFarPickRange(); + + // 4J Stu - Added so we can send packets for this in the network game + virtual bool handleCraftItem(int recipe, shared_ptr player); + virtual void handleDebugOptions(unsigned int uiVal, shared_ptr player); + + // 4J Stu - Added for tutorial checks + virtual bool isInputAllowed(int mapping) { return true; } + virtual bool isTutorial() { return false; } + virtual Tutorial *getTutorial() { return NULL; } +}; \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerLevel.cpp b/Minecraft.Client/MultiPlayerLevel.cpp new file mode 100644 index 0000000..5e3b7b8 --- /dev/null +++ b/Minecraft.Client/MultiPlayerLevel.cpp @@ -0,0 +1,913 @@ +#include "stdafx.h" +#include "MultiPlayerLevel.h" +#include "MultiPlayerLocalPlayer.h" +#include "ClientConnection.h" +#include "MultiPlayerChunkCache.h" +#include "..\Minecraft.World\net.minecraft.world.level.storage.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\Pos.h" +#include "MinecraftServer.h" +#include "ServerLevel.h" +#include "Minecraft.h" +#include "..\Minecraft.World\PrimedTnt.h" +#include "..\Minecraft.World\Tile.h" +#include "..\Minecraft.World\TileEntity.h" + +MultiPlayerLevel::ResetInfo::ResetInfo(int x, int y, int z, int tile, int data) +{ + this->x = x; + this->y = y; + this->z = z; + ticks = TICKS_BEFORE_RESET; + this->tile = tile; + this->data = data; +} + +MultiPlayerLevel::MultiPlayerLevel(ClientConnection *connection, LevelSettings *levelSettings, int dimension, int difficulty) + : Level(shared_ptr(new MockedLevelStorage()), L"MpServer", Dimension::getNew(dimension), levelSettings, false) +{ + minecraft = Minecraft::GetInstance(); + + // 4J - this this used to be called in parent ctor via a virtual fn + chunkSource = createChunkSource(); + // 4J - optimisation - keep direct reference of underlying cache here + chunkSourceCache = chunkSource->getCache(); + chunkSourceXZSize = chunkSource->m_XZSize; + + // This also used to be called in parent ctor, but can't be called until chunkSource is created. Call now if required. + if (!levelData->isInitialized()) + { + initializeLevel(levelSettings); + levelData->setInitialized(true); + } + + if(connection !=NULL) + { + this->connections.push_back( connection ); + } + this->difficulty = difficulty; + // Fix for #62566 - TU7: Content: Gameplay: Compass needle stops pointing towards the original spawn point, once the player has entered the Nether. + // 4J Stu - We should never be setting a specific spawn position for a multiplayer, this should only be set by receiving a packet from the server + // (which happens when a player logs in) + //setSpawnPos(new Pos(8, 64, 8)); + // The base ctor already has made some storage, so need to delete that + if( this->savedDataStorage ) delete savedDataStorage; + if(connection !=NULL) + { + this->savedDataStorage = connection->savedDataStorage; + } + unshareCheckX = 0; + unshareCheckZ = 0; + compressCheckX = 0; + compressCheckZ = 0; + + // 4J Added, as there are some times when we don't want to add tile updates to the updatesToReset vector + m_bEnableResetChanges = true; +} + +MultiPlayerLevel::~MultiPlayerLevel() +{ + // Don't let the base class delete this, it comes from the connection for multiplayerlevels, and we'll delete there + this->savedDataStorage = NULL; +} + +void MultiPlayerLevel::unshareChunkAt(int x, int z) +{ + if( g_NetworkManager.IsHost() ) + { + Level::getChunkAt(x,z)->stopSharingTilesAndData(); + } +} + +void MultiPlayerLevel::shareChunkAt(int x, int z) +{ + if( g_NetworkManager.IsHost() ) + { + Level::getChunkAt(x,z)->startSharingTilesAndData(); + } +} + + +void MultiPlayerLevel::tick() +{ + PIXBeginNamedEvent(0,"Sky color changing"); + setTime(getTime() + 1); + /* 4J - change brought forward from 1.8.2 + int newDark = this->getSkyDarken(1); + if (newDark != skyDarken) + { + skyDarken = newDark; + for (unsigned int i = 0; i < listeners.size(); i++) + { + listeners[i]->skyColorChanged(); + } + }*/ + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Entity re-entry"); + EnterCriticalSection(&m_entitiesCS); + for (int i = 0; i < 10 && !reEntries.empty(); i++) + { + shared_ptr e = *(reEntries.begin()); + + if (find(entities.begin(), entities.end(), e) == entities.end() ) addEntity(e); + } + LeaveCriticalSection(&m_entitiesCS); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Connection ticking"); + // 4J HEG - Copy the connections vector to prevent crash when moving to Nether + vector connectionsTemp = connections; + for(AUTO_VAR(connection, connectionsTemp.begin()); connection < connectionsTemp.end(); ++connection ) + { + (*connection)->tick(); + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Updating resets"); + unsigned int lastIndexToRemove = 0; + bool eraseElements = false; + for (unsigned int i = 0; i < updatesToReset.size(); i++) + { + ResetInfo& r = updatesToReset[i]; + if (--r.ticks == 0) + { + Level::setTileAndDataNoUpdate(r.x, r.y, r.z, r.tile, r.data); + Level::sendTileUpdated(r.x, r.y, r.z); + + //updatesToReset.erase(updatesToReset.begin()+i); + eraseElements = true; + lastIndexToRemove = 0; + + i--; + } + } + // 4J Stu - As elements in the updatesToReset vector are inserted with a fixed initial lifetime, the elements at the front should always be the oldest + // Therefore we can always remove from the first element + if(eraseElements) + { + updatesToReset.erase(updatesToReset.begin(), updatesToReset.begin()+lastIndexToRemove); + } + PIXEndNamedEvent(); + + chunkCache->tick(); + tickTiles(); + + // 4J - added this section. Each tick we'll check a different block, and force it to share data if it has been + // more than 2 minutes since we last wanted to unshare it. This shouldn't really ever happen, and is added + // here as a safe guard against accumulated memory leaks should a lot of chunks become unshared over time. + + int ls = dimension->getXZSize(); + if( g_NetworkManager.IsHost() ) + { + if( Level::reallyHasChunk(unshareCheckX - ( ls / 2), unshareCheckZ - ( ls / 2 ) ) ) + { + LevelChunk *lc = Level::getChunk(unshareCheckX - ( ls / 2), unshareCheckZ - ( ls / 2 )); + if( g_NetworkManager.IsHost() ) + { + lc->startSharingTilesAndData(1000 * 60 * 2); + } + } + + unshareCheckX++; + if( unshareCheckX >= ls ) + { + unshareCheckX = 0; + unshareCheckZ++; + if( unshareCheckZ >= ls ) + { + unshareCheckZ = 0; + } + } + } + + // 4J added - also similar thing tosee if we can compress the lighting in any of these chunks. This is slightly different + // as it does try to make sure that at least one chunk has something done to it. + + // At most loop round at least one row the chunks, so we should be able to at least find a non-empty chunk to do something with in 2.7 seconds of ticks, and process the whole thing in about 2.4 minutes. + for( int i = 0; i < ls; i++ ) + { + compressCheckX++; + if( compressCheckX >= ls ) + { + compressCheckX = 0; + compressCheckZ++; + if( compressCheckZ >= ls ) + { + compressCheckZ = 0; + } + } + + if( Level::reallyHasChunk(compressCheckX - ( ls / 2), compressCheckZ - ( ls / 2 ) ) ) + { + LevelChunk *lc = Level::getChunk(compressCheckX - ( ls / 2), compressCheckZ - ( ls / 2 )); + lc->compressLighting(); + lc->compressBlocks(); + lc->compressData(); + break; + } + } + +#ifdef LIGHT_COMPRESSION_STATS + static int updateTick = 0; + + if( ( updateTick % 60 ) == 0 ) + { + unsigned int totalBLu = 0; + unsigned int totalBLl = 0; + unsigned int totalSLu = 0; + unsigned int totalSLl = 0; + unsigned int totalChunks = 0; + + for( int lcs_x = 0; lcs_x < ls; lcs_x++ ) + for( int lcs_z = 0; lcs_z < ls; lcs_z++ ) + { + if( Level::reallyHasChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 ) ) ) + { + LevelChunk *lc = Level::getChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 )); + totalChunks++; + totalBLu += lc->getBlockLightPlanesUpper(); + totalBLl += lc->getBlockLightPlanesLower(); + totalSLu += lc->getSkyLightPlanesUpper(); + totalSLl += lc->getSkyLightPlanesLower(); + } + } + if( totalChunks ) + { + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + + unsigned int totalBL = totalBLu + totalBLl; + unsigned int totalSL = totalSLu + totalSLl; + printf("%d: %d chunks, %d BL (%d + %d), %d SL (%d + %d ) (out of %d) - total %d %% (%dMB mem free)\n", + dimension->id, totalChunks, totalBL, totalBLu, totalBLl, totalSL, totalSLu, totalSLl, totalChunks * 256, ( 100 * (totalBL + totalSL) ) / ( totalChunks * 256 * 2),memStat.dwAvailPhys/(1024*1024) ); + } + } + updateTick++; + +#endif + +#ifdef DATA_COMPRESSION_STATS + static int updateTick = 0; + + if( ( updateTick % 60 ) == 0 ) + { + unsigned int totalData = 0; + unsigned int totalChunks = 0; + + for( int lcs_x = 0; lcs_x < ls; lcs_x++ ) + for( int lcs_z = 0; lcs_z < ls; lcs_z++ ) + { + if( Level::reallyHasChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 ) ) ) + { + LevelChunk *lc = Level::getChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 )); + totalChunks++; + totalData += lc->getDataPlanes(); + } + } + if( totalChunks ) + { + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + + printf("%d: %d chunks, %d data (out of %d) - total %d %% (%dMB mem free)\n", + dimension->id, totalChunks, totalData, totalChunks * 128, ( 100 * totalData)/ ( totalChunks * 128),memStat.dwAvailPhys/(1024*1024) ); + } + } + updateTick++; + +#endif + +#ifdef BLOCK_COMPRESSION_STATS + static int updateTick = 0; + + if( ( updateTick % 60 ) == 0 ) + { + unsigned int total = 0; + unsigned int totalChunks = 0; + unsigned int total0 = 0, total1 = 0, total2 = 0, total4 = 0, total8 = 0; + + printf("*****************************************************************************************************************************************\n"); + printf("TODO: Report upper chunk data as well\n"); + for( int lcs_x = 0; lcs_x < ls; lcs_x++ ) + for( int lcs_z = 0; lcs_z < ls; lcs_z++ ) + { + if( Level::reallyHasChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 ) ) ) + { + LevelChunk *lc = Level::getChunk(lcs_x - ( ls / 2), lcs_z - ( ls / 2 )); + totalChunks++; + int i0, i1, i2, i4, i8; + int thisSize = lc->getBlocksAllocatedSize(&i0, &i1, &i2, &i4, &i8); + total0 += i0; + total1 += i1; + total2 += i2; + total4 += i4; + total8 += i8; + printf("%d ",thisSize); + thisSize = ( thisSize + 0xfff ) & 0xfffff000; // round to 4096k blocks for actual memory consumption + total += thisSize; + } + } + printf("\n*****************************************************************************************************************************************\n"); + if( totalChunks ) + { + printf("%d (0) %d (1) %d (2) %d (4) %d (8)\n",total0/totalChunks,total1/totalChunks,total2/totalChunks,total4/totalChunks,total8/totalChunks); + MEMORYSTATUS memStat; + GlobalMemoryStatus(&memStat); + + printf("%d: %d chunks, %d KB (out of %dKB) : %d %% (%dMB mem free)\n", + dimension->id, totalChunks, total/1024, totalChunks * 32, ( ( total / 1024 ) * 100 ) / ( totalChunks * 32),memStat.dwAvailPhys/(1024*1024) ); + } + } + updateTick++; +#endif + + // super.tick(); + +} + +void MultiPlayerLevel::clearResetRegion(int x0, int y0, int z0, int x1, int y1, int z1) +{ + for (unsigned int i = 0; i < updatesToReset.size(); i++) + { + ResetInfo& r = updatesToReset[i]; + if (r.x >= x0 && r.y >= y0 && r.z >= z0 && r.x <= x1 && r.y <= y1 && r.z <= z1) + { + updatesToReset.erase(updatesToReset.begin()+i); + i--; + } + } +} + +ChunkSource *MultiPlayerLevel::createChunkSource() +{ + chunkCache = new MultiPlayerChunkCache(this); + + return chunkCache; +} + +void MultiPlayerLevel::validateSpawn() +{ + // Fix for #62566 - TU7: Content: Gameplay: Compass needle stops pointing towards the original spawn point, once the player has entered the Nether. + // 4J Stu - We should never be setting a specific spawn position for a multiplayer, this should only be set by receiving a packet from the server + // (which happens when a player logs in) + //setSpawnPos(new Pos(8, 64, 8)); +} + +void MultiPlayerLevel::tickTiles() +{ + chunksToPoll.clear(); // 4J - added or else we don't reset this set at all in a multiplayer level... think current java now resets in buildAndPrepareChunksToPoll rather than the calling functions + + PIXBeginNamedEvent(0,"Ticking tiles (multiplayer)"); + PIXBeginNamedEvent(0,"buildAndPrepareChunksToPoll"); + Level::tickTiles(); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Ticking client side tiles"); +#ifdef __PSVITA__ + // AP - see CustomSet.h for and explanation + for( int i = 0;i < chunksToPoll.end();i += 1 ) + { + ChunkPos cp = chunksToPoll.get(i); +#else + AUTO_VAR(itEndCtp, chunksToPoll.end()); + for (AUTO_VAR(it, chunksToPoll.begin()); it != itEndCtp; it++) + { + ChunkPos cp = *it; +#endif + int xo = cp.x * 16; + int zo = cp.z * 16; + + LevelChunk *lc = this->getChunk(cp.x, cp.z); + + tickClientSideTiles(xo, zo, lc); + } + PIXEndNamedEvent(); + PIXEndNamedEvent(); +} + +void MultiPlayerLevel::setChunkVisible(int x, int z, bool visible) +{ + if (visible) + { + chunkCache->create(x, z); + } + else + { + chunkCache->drop(x, z); + } + if (!visible) + { + this->setTilesDirty(x * 16, 0, z * 16, x * 16 + 15, Level::maxBuildHeight, z * 16 + 15); + } + +} + +bool MultiPlayerLevel::addEntity(shared_ptr e) +{ + bool ok = Level::addEntity(e); + forced.insert(e); + + if (!ok) + { + reEntries.insert(e); + } + + return ok; +} + +void MultiPlayerLevel::removeEntity(shared_ptr e) +{ + // 4J Stu - Add this remove from the reEntries collection to stop us continually removing and re-adding things, + // in particular the MultiPlayerLocalPlayer when they die + AUTO_VAR(it, reEntries.find(e)); + if (it!=reEntries.end()) + { + reEntries.erase(it); + } + + Level::removeEntity(e); + forced.erase(e); +} + +void MultiPlayerLevel::entityAdded(shared_ptr e) +{ + Level::entityAdded(e); + AUTO_VAR(it, reEntries.find(e)); + if (it!=reEntries.end()) + { + reEntries.erase(it); + } +} + +void MultiPlayerLevel::entityRemoved(shared_ptr e) +{ + Level::entityRemoved(e); + AUTO_VAR(it, forced.find(e)); + if (it!=forced.end()) + { + reEntries.insert(e); + } +} + +void MultiPlayerLevel::putEntity(int id, shared_ptr e) +{ + shared_ptr old = getEntity(id); + if (old != NULL) + { + removeEntity(old); + } + + forced.insert(e); + e->entityId = id; + if (!addEntity(e)) + { + this->reEntries.insert(e); + } + entitiesById[id] = e; +} + +shared_ptr MultiPlayerLevel::getEntity(int id) +{ + AUTO_VAR(it, entitiesById.find(id)); + if( it == entitiesById.end() ) return nullptr; + return it->second; +} + +shared_ptr MultiPlayerLevel::removeEntity(int id) +{ + shared_ptr e; + AUTO_VAR(it, entitiesById.find(id)); + if( it != entitiesById.end() ) + { + e = it->second; + entitiesById.erase(it); + forced.erase(e); + removeEntity(e); + } + else + { + } + return e; +} + +// 4J Added to remove the entities from the forced list +// This gets called when a chunk is unloaded, but we only do half an unload to remove entities slightly differently +void MultiPlayerLevel::removeEntities(vector > *list) +{ + for(AUTO_VAR(it, list->begin()); it < list->end(); ++it) + { + shared_ptr e = *it; + + AUTO_VAR(reIt, reEntries.find(e)); + if (reIt!=reEntries.end()) + { + reEntries.erase(reIt); + } + + forced.erase(e); + } + Level::removeEntities(list); +} + +bool MultiPlayerLevel::setDataNoUpdate(int x, int y, int z, int data) +{ + int t = getTile(x, y, z); + int d = getData(x, y, z); + // 4J - added - if this is the host, then stop sharing block data with the server at this point + unshareChunkAt(x,z); + + if (Level::setDataNoUpdate(x, y, z, data)) + { + //if(m_bEnableResetChanges) updatesToReset.push_back(ResetInfo(x, y, z, t, d)); + return true; + } + // Didn't actually need to stop sharing + shareChunkAt(x,z); + return false; +} + +bool MultiPlayerLevel::setTileAndDataNoUpdate(int x, int y, int z, int tile, int data) +{ + // First check if this isn't going to do anything, because if it isn't then the next stage (of unsharing data) is really quite + // expensive so far better to early out here + int t = getTile(x, y, z); + int d = getData(x, y, z); + + if( ( t == tile ) && ( d == data ) ) + { + // If we early-out, its important that we still do a checkLight here (which would otherwise have happened as part of Level::setTileAndDataNoUpdate) + // This is because since we are potentially sharing tile/data but not lighting data, it is possible that the server might tell a client + // of a lighting update that doesn't need actioned on the client just because the chunk's data was being shared with the server when it was set. However, + // the lighting data will potentially now be out of sync on the client. + checkLight(x,y,z); + return false; + } + // 4J - added - if this is the host, then stop sharing block data with the server at this point + unshareChunkAt(x,z); + + if (Level::setTileAndDataNoUpdate(x, y, z, tile, data)) + { + //if(m_bEnableResetChanges) updatesToReset.push_back(ResetInfo(x, y, z, t, d)); + return true; + } + // Didn't actually need to stop sharing + shareChunkAt(x,z); + return false; +} + +bool MultiPlayerLevel::setTileNoUpdate(int x, int y, int z, int tile) +{ + int t = getTile(x, y, z); + int d = getData(x, y, z); + // 4J - added - if this is the host, then stop sharing block data with the server at this point + unshareChunkAt(x,z); + + if (Level::setTileNoUpdate(x, y, z, tile)) + { + //if(m_bEnableResetChanges) updatesToReset.push_back(ResetInfo(x, y, z, t, d)); + return true; + } + // Didn't actually need to stop sharing + shareChunkAt(x,z); + return false; +} + +bool MultiPlayerLevel::doSetTileAndData(int x, int y, int z, int tile, int data) +{ + clearResetRegion(x, y, z, x, y, z); + + // 4J - Don't bother setting this to dirty if it isn't going to visually change - we get a lot of + // water changing from static to dynamic for instance. Note that this is only called from a client connection, + // and so the thing being notified of any update through tileUpdated is the renderer + int prevTile = getTile(x, y, z); + bool visuallyImportant = (!( ( ( prevTile == Tile::water_Id ) && ( tile == Tile::calmWater_Id ) ) || + ( ( prevTile == Tile::calmWater_Id ) && ( tile == Tile::water_Id ) ) || + ( ( prevTile == Tile::lava_Id ) && ( tile == Tile::calmLava_Id ) ) || + ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::calmLava_Id ) ) || + ( ( prevTile == Tile::calmLava_Id ) && ( tile == Tile::lava_Id ) ) ) ); + // If we're the host, need to tell the renderer for updates even if they don't change things as the host + // might have been sharing data and so set it already, but the renderer won't know to update + if( (Level::setTileAndData(x, y, z, tile, data) || g_NetworkManager.IsHost() ) ) + { + if( g_NetworkManager.IsHost() && visuallyImportant ) + { + sendTileUpdated(x,y,z); + + tileUpdated(x, y, z, tile); + } + return true; + } + return false; +} + +void MultiPlayerLevel::disconnect(bool sendDisconnect /*= true*/) +{ + if( sendDisconnect ) + { + for(AUTO_VAR(it, connections.begin()); it < connections.end(); ++it ) + { + (*it)->sendAndDisconnect( shared_ptr( new DisconnectPacket(DisconnectPacket::eDisconnect_Quitting) ) ); + } + } + else + { + for(AUTO_VAR(it, connections.begin()); it < connections.end(); ++it ) + { + (*it)->close(); + } + } +} + +void MultiPlayerLevel::tickWeather() +{ + if (dimension->hasCeiling) return; + + if (lightningTime > 0) + { + lightningTime--; + } + + oRainLevel = rainLevel; + if (levelData->isRaining()) + { + rainLevel += 0.01; + } + else + { + rainLevel -= 0.01; + } + if (rainLevel < 0) rainLevel = 0; + if (rainLevel > 1) rainLevel = 1; + + oThunderLevel = thunderLevel; + if (levelData->isThundering()) + { + thunderLevel += 0.01; + } + else + { + thunderLevel -= 0.01; + } + if (thunderLevel < 0) thunderLevel = 0; + if (thunderLevel > 1) thunderLevel = 1; + +} + +void MultiPlayerLevel::animateTick(int xt, int yt, int zt) +{ + // Get 8x8x8 chunk (ie not like the renderer or game chunks... maybe we need another word here...) that the player is in + // We then want to add a 3x3 region of chunks into a set that we'll be ticking over. Set is stored as unsigned ints which encode + // this chunk position + int cx = xt >> 3; + int cy = yt >> 3; + int cz = zt >> 3; + + for( int xx = -1; xx <= 1; xx++ ) + for( int yy = -1; yy <= 1; yy++ ) + for( int zz = -1; zz <= 1; zz++ ) + { + if( ( cy + yy ) < 0 ) continue; + if( ( cy + yy ) > 15 ) continue; + // Note - LEVEL_MAX_WIDTH is in game (16) tile chunks, and so our level goes from -LEVEL_MAX_WIDTH to LEVEL_MAX_WIDTH of our half-sized chunks + if( ( cx + xx ) >= LEVEL_MAX_WIDTH ) continue; + if( ( cx + xx ) < -LEVEL_MAX_WIDTH ) continue; + if( ( cz + zz ) >= LEVEL_MAX_WIDTH ) continue; + if( ( cz + zz ) < -LEVEL_MAX_WIDTH ) continue; + chunksToAnimate.insert( ( ( ( cx + xx ) & 0xff ) << 16 ) | ( ( ( cy + yy ) & 0xff ) << 8 ) | ( ( ( cz + zz ) & 0xff ) ) ); + } +} + +// 4J - the game used to tick 1000 tiles in a random region +/- 16 units round the player. We've got a 3x3 region of 8x8x8 chunks round each +// player. So the original game was ticking 1000 things in a 32x32x32 region ie had about a 1 in 32 chance of updating any one tile per tick. +// We're not dealing with quite such a big region round each player (24x24x24) but potentially we've got 4 players. Ultimately, we could end +// up ticking anywhere between 432 and 1728 tiles depending on how many players we've got, which seems like a good tradeoff from the original. +void MultiPlayerLevel::animateTickDoWork() +{ + const int ticksPerChunk = 16; // This ought to give us roughly the same 1000/32768 chance of a tile being animated as the original + + // Horrible hack to communicate with the level renderer, which is just attached as a listener to this level. This let's the particle + // rendering know to use this level (rather than try to work it out from the current player), and to not bother distance clipping particles + // which would again be based on the current player. + Minecraft::GetInstance()->animateTickLevel = this; + + MemSect(31); + Random *animateRandom = new Random(); + MemSect(0); + + for( int i = 0; i < ticksPerChunk; i++ ) + { + for( AUTO_VAR(it, chunksToAnimate.begin()); it != chunksToAnimate.end(); it++ ) + { + int packed = *it; + int cx = ( packed << 8 ) >> 24; + int cy = ( packed << 16 ) >> 24; + int cz = ( packed << 24 ) >> 24; + cx <<= 3; + cy <<= 3; + cz <<= 3; + int x = cx + random->nextInt(8); + int y = cy + random->nextInt(8); + int z = cz + random->nextInt(8); + int t = getTile(x, y, z); + if (random->nextInt(8) > y && t == 0 && dimension->hasBedrockFog()) // 4J - test for bedrock fog brought forward from 1.2.3 + { + addParticle(eParticleType_depthsuspend, x + random->nextFloat(), y + random->nextFloat(), z + random->nextFloat(), 0, 0, 0); + } + else if (t > 0) + { + Tile::tiles[t]->animateTick(this, x, y, z, animateRandom); + } + } + } + + Minecraft::GetInstance()->animateTickLevel = NULL; + delete animateRandom; + + chunksToAnimate.clear(); + +} + +void MultiPlayerLevel::playSound(shared_ptr entity, int iSound, float volume, float pitch) +{ + playLocalSound(entity->x, entity->y - entity->heightOffset, entity->z, iSound, volume, pitch); +} + +void MultiPlayerLevel::playLocalSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist) +{ + //float dd = 16; + if (volume > 1) fClipSoundDist *= volume; + + // 4J - find min distance to any players rather than just the current one + float minDistSq = FLT_MAX; + for( int i = 0; i < XUSER_MAX_COUNT; i++ ) + { + if( minecraft->localplayers[i] ) + { + float distSq = minecraft->localplayers[i]->distanceToSqr(x, y, z ); + if( distSq < minDistSq ) + { + minDistSq = distSq; + } + } + } + + if (minDistSq < fClipSoundDist * fClipSoundDist) + { + minecraft->soundEngine->play(iSound, (float) x, (float) y, (float) z, volume, pitch); + } +} + +void MultiPlayerLevel::removeAllPendingEntityRemovals() +{ + //entities.removeAll(entitiesToRemove); + + EnterCriticalSection(&m_entitiesCS); + for( AUTO_VAR(it, entities.begin()); it != entities.end(); ) + { + bool found = false; + for( AUTO_VAR(it2, entitiesToRemove.begin()); it2 != entitiesToRemove.end(); it2++ ) + { + if( (*it) == (*it2) ) + { + found = true; + break; + } + } + if( found ) + { + it = entities.erase(it); + } + else + { + it++; + } + } + LeaveCriticalSection(&m_entitiesCS); + + AUTO_VAR(endIt, entitiesToRemove.end()); + for (AUTO_VAR(it, entitiesToRemove.begin()); it != endIt; it++) + { + shared_ptr e = *it; + int xc = e->xChunk; + int zc = e->zChunk; + if (e->inChunk && hasChunk(xc, zc)) + { + getChunk(xc, zc)->removeEntity(e); + } + } + + // 4J Stu - Is there a reason do this in a separate loop? Thats what the Java does... + endIt = entitiesToRemove.end(); + for (AUTO_VAR(it, entitiesToRemove.begin()); it != endIt; it++) + { + entityRemoved(*it); + } + entitiesToRemove.clear(); + + //for (int i = 0; i < entities.size(); i++) + EnterCriticalSection(&m_entitiesCS); + vector >::iterator it = entities.begin(); + while( it != entities.end() ) + { + shared_ptr e = *it;//entities.at(i); + + if (e->riding != NULL) + { + if (e->riding->removed || e->riding->rider.lock() != e) + { + e->riding->rider = weak_ptr(); + e->riding = nullptr; + } + else + { + ++it; + continue; + } + } + + if (e->removed) + { + int xc = e->xChunk; + int zc = e->zChunk; + if (e->inChunk && hasChunk(xc, zc)) + { + getChunk(xc, zc)->removeEntity(e); + } + //entities.remove(i--); + + it = entities.erase( it ); + entityRemoved(e); + } + else + { + it++; + } + } + LeaveCriticalSection(&m_entitiesCS); +} + +void MultiPlayerLevel::removeClientConnection(ClientConnection *c, bool sendDisconnect) +{ + if( sendDisconnect ) + { + c->sendAndDisconnect( shared_ptr( new DisconnectPacket(DisconnectPacket::eDisconnect_Quitting) ) ); + } + + AUTO_VAR(it, find( connections.begin(), connections.end(), c )); + if( it != connections.end() ) + { + connections.erase( it ); + } +} + +void MultiPlayerLevel::tickAllConnections() +{ + PIXBeginNamedEvent(0,"Connection ticking"); + for(AUTO_VAR(it, connections.begin()); it < connections.end(); ++it ) + { + (*it)->tick(); + } + PIXEndNamedEvent(); +} + +void MultiPlayerLevel::dataReceivedForChunk(int x, int z) +{ + chunkCache->dataReceived(x, z); +} + +// 4J added - removes all tile entities in the given region from both level & levelchunks +void MultiPlayerLevel::removeUnusedTileEntitiesInRegion(int x0, int y0, int z0, int x1, int y1, int z1) +{ + EnterCriticalSection(&m_tileEntityListCS); + + for (unsigned int i = 0; i < tileEntityList.size();) + { + bool removed = false; + shared_ptr te = tileEntityList[i]; + if (te->x >= x0 && te->y >= y0 && te->z >= z0 && te->x < x1 && te->y < y1 && te->z < z1) + { + LevelChunk *lc = getChunk(te->x >> 4, te->z >> 4); + if (lc != NULL) + { + // Only remove tile entities where this is no longer a tile entity + int tileId = lc->getTile(te->x & 15, te->y, te->z & 15 ); + if( Tile::tiles[tileId] == NULL || !Tile::tiles[tileId]->isEntityTile()) + { + tileEntityList[i] = tileEntityList.back(); + tileEntityList.pop_back(); + + // 4J Stu - Chests can create new tile entities when being removed, so disable this + m_bDisableAddNewTileEntities = true; + lc->removeTileEntity(te->x & 15, te->y, te->z & 15); + m_bDisableAddNewTileEntities = false; + removed = true; + } + } + } + if( !removed ) i++; + } + + LeaveCriticalSection(&m_tileEntityListCS); +} + diff --git a/Minecraft.Client/MultiPlayerLevel.h b/Minecraft.Client/MultiPlayerLevel.h new file mode 100644 index 0000000..c07b558 --- /dev/null +++ b/Minecraft.Client/MultiPlayerLevel.h @@ -0,0 +1,102 @@ +#pragma once +using namespace std; +#include "..\Minecraft.World\HashExtension.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.entity.h" +#include "..\Minecraft.World\JavaIntHash.h" + +class ClientConnection; +class MultiPlayerChunkCache; + +using namespace std; + +class MultiPlayerLevel : public Level +{ +private: + static const int TICKS_BEFORE_RESET = 20 * 4; + + class ResetInfo + { + public: + int x, y, z, ticks, tile, data; + ResetInfo(int x, int y, int z, int tile, int data); + }; + + vector updatesToReset; // 4J - was linked list but vector seems more appropriate + bool m_bEnableResetChanges; // 4J Added +public: + void unshareChunkAt(int x, int z); // 4J - added + void shareChunkAt(int x, int z); // 4J - added + + void enableResetChanges(bool enable) { m_bEnableResetChanges = enable; } // 4J Added +private: + int unshareCheckX; // 4J - added + int unshareCheckZ; // 4J - added + int compressCheckX; // 4J - added + int compressCheckZ; // 4J - added + vector connections; // 4J Stu - Made this a vector as we can have more than one local connection + MultiPlayerChunkCache *chunkCache; + Minecraft *minecraft; + +public: + MultiPlayerLevel(ClientConnection *connection, LevelSettings *levelSettings, int dimension, int difficulty); + virtual ~MultiPlayerLevel(); + virtual void tick() ; + + void clearResetRegion(int x0, int y0, int z0, int x1, int y1, int z1); +protected: + ChunkSource *createChunkSource(); // 4J - was virtual, but was called from parent ctor +public: + virtual void validateSpawn(); +protected: + virtual void tickTiles(); +public: + void setChunkVisible(int x, int z, bool visible); + +private: + unordered_map, IntKeyHash2, IntKeyEq> entitiesById; // 4J - was IntHashMap + unordered_set > forced; + unordered_set > reEntries; + +public: + virtual bool addEntity(shared_ptr e); + virtual void removeEntity(shared_ptr e); +protected: + virtual void entityAdded(shared_ptr e); + virtual void entityRemoved(shared_ptr e); +public: + void putEntity(int id, shared_ptr e); + shared_ptr getEntity(int id); + shared_ptr removeEntity(int id); + virtual void removeEntities(vector > *list); // 4J Added override + virtual bool setDataNoUpdate(int x, int y, int z, int data); + virtual bool setTileAndDataNoUpdate(int x, int y, int z, int tile, int data); + virtual bool setTileNoUpdate(int x, int y, int z, int tile); + bool doSetTileAndData(int x, int y, int z, int tile, int data); + virtual void disconnect(bool sendDisconnect = true); + void animateTick(int xt, int yt, int zt); +protected: + virtual void tickWeather(); + + static const int ANIMATE_TICK_MAX_PARTICLES = 500; + +public: + void animateTickDoWork(); // 4J added + unordered_set chunksToAnimate; // 4J added + +public: + void removeAllPendingEntityRemovals(); + + virtual void playSound(shared_ptr entity, int iSound, float volume, float pitch); + + virtual void playLocalSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist=16.0f); + + // 4J Stu - Added so we can have multiple local connections + void addClientConnection(ClientConnection *c) { connections.push_back( c ); } + void removeClientConnection(ClientConnection *c, bool sendDisconnect); + + void tickAllConnections(); + + void dataReceivedForChunk(int x, int z); // 4J added + void removeUnusedTileEntitiesInRegion(int x0, int y0, int z0, int x1, int y1, int z1); // 4J added +}; diff --git a/Minecraft.Client/MultiPlayerLocalPlayer.cpp b/Minecraft.Client/MultiPlayerLocalPlayer.cpp new file mode 100644 index 0000000..bb2630e --- /dev/null +++ b/Minecraft.Client/MultiPlayerLocalPlayer.cpp @@ -0,0 +1,369 @@ +#include "stdafx.h" +//#include "..\Minecraft.World\JavaMath.h" +#include "MultiplayerLocalPlayer.h" +#include "ClientConnection.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.network.h" +#include "..\Minecraft.World\Mth.h" +#include "..\Minecraft.World\AABB.h" +#include "..\Minecraft.World\net.minecraft.stats.h" +#include "..\Minecraft.World\net.minecraft.world.inventory.h" +#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" +#include "..\Minecraft.World\net.minecraft.world.effect.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.World\net.minecraft.world.entity.item.h" + + + + +MultiplayerLocalPlayer::MultiplayerLocalPlayer(Minecraft *minecraft, Level *level, User *user, ClientConnection *connection) : LocalPlayer(minecraft, level, user, level->dimension->id) +{ + // 4J - added initialisers + flashOnSetHealth = false; + xLast = yLast1 = yLast2 = zLast = 0; + yRotLast = xRotLast = 0; + lastOnGround = false; + lastSneaked = false; + lastIdle = false; + lastSprinting = false; + positionReminder = 0; + + this->connection = connection; +} + +bool MultiplayerLocalPlayer::hurt(DamageSource *source, int dmg) +{ + return false; +} + +void MultiplayerLocalPlayer::heal(int heal) +{ +} + +void MultiplayerLocalPlayer::tick() +{ + // 4J Added + // 4J-PB - changing this to a game host option ot hide gamertags + //bool bIsisPrimaryHost=g_NetworkManager.IsHost() && (ProfileManager.GetPrimaryPad()==m_iPad); + + /*if((app.GetGameSettings(m_iPad,eGameSetting_PlayerVisibleInMap)!=0) != m_bShownOnMaps) + { + m_bShownOnMaps = (app.GetGameSettings(m_iPad,eGameSetting_PlayerVisibleInMap)!=0); + if (m_bShownOnMaps) connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::SHOW_ON_MAPS) ) ); + else connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::HIDE_ON_MAPS) ) ); + }*/ + + if (!level->hasChunkAt(Mth::floor(x), 0, Mth::floor(z))) return; + + double tempX = x, tempY = y, tempZ = z; + LocalPlayer::tick(); + + //if( !minecraft->localgameModes[m_iPad]->isTutorial() || minecraft->localgameModes[m_iPad]->getTutorial()->canMoveToPosition(tempX, tempY, tempZ, x, y, z) ) + if(minecraft->localgameModes[m_iPad]->getTutorial()->canMoveToPosition(tempX, tempY, tempZ, x, y, z)) + { + sendPosition(); + } + else + { + //app.Debugprintf("Cannot move to position (%f, %f, %f), falling back to (%f, %f, %f)\n", x, y, z, tempX, y, tempZ); + this->setPos(tempX, y, tempZ); + } +} + +void MultiplayerLocalPlayer::sendPosition() +{ + bool sprinting = isSprinting(); + if (sprinting != lastSprinting) + { + if (sprinting) connection->send(shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::START_SPRINTING))); + else connection->send(shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::STOP_SPRINTING))); + + lastSprinting = sprinting; + } + + bool sneaking = isSneaking(); + if (sneaking != lastSneaked) + { + if (sneaking) connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::START_SNEAKING) ) ); + else connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::STOP_SNEAKING) ) ); + + lastSneaked = sneaking; + } + + bool idle = isIdle(); + if (idle != lastIdle) + { + if (idle) connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::START_IDLEANIM) ) ); + else connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::STOP_IDLEANIM) ) ); + + lastIdle = idle; + } + + double xdd = x - xLast; + double ydd1 = bb->y0 - yLast1; + double zdd = z - zLast; + + double rydd = yRot - yRotLast; + double rxdd = xRot - xRotLast; + + bool move = (xdd * xdd + ydd1 * ydd1 + zdd * zdd) > 0.03 * 0.03 || positionReminder >= POSITION_REMINDER_INTERVAL; + bool rot = rydd != 0 || rxdd != 0; + if (riding != NULL) + { + connection->send( shared_ptr( new MovePlayerPacket::PosRot(xd, -999, -999, zd, yRot, xRot, onGround, abilities.flying) ) ); + move = false; + } + else + { + if (move && rot) + { + connection->send( shared_ptr( new MovePlayerPacket::PosRot(x, bb->y0, y, z, yRot, xRot, onGround, abilities.flying) ) ); + } + else if (move) + { + connection->send( shared_ptr( new MovePlayerPacket::Pos(x, bb->y0, y, z, onGround, abilities.flying) ) ); + } + else if (rot) + { + connection->send( shared_ptr( new MovePlayerPacket::Rot(yRot, xRot, onGround, abilities.flying) ) ); + } + else + { + connection->send( shared_ptr( new MovePlayerPacket(onGround, abilities.flying) ) ); + } + } + + positionReminder++; + lastOnGround = onGround; + + if (move) + { + xLast = x; + yLast1 = bb->y0; + yLast2 = y; + zLast = z; + positionReminder = 0; + } + if (rot) + { + yRotLast = yRot; + xRotLast = xRot; + } + +} + +shared_ptr MultiplayerLocalPlayer::drop() +{ + connection->send( shared_ptr( new PlayerActionPacket(PlayerActionPacket::DROP_ITEM, 0, 0, 0, 0) ) ); + return nullptr; +} + +void MultiplayerLocalPlayer::reallyDrop(shared_ptr itemEntity) +{ +} + +void MultiplayerLocalPlayer::chat(const wstring& message) +{ + connection->send( shared_ptr( new ChatPacket(message) ) ); +} + +void MultiplayerLocalPlayer::swing() +{ + LocalPlayer::swing(); + connection->send( shared_ptr( new AnimatePacket(shared_from_this(), AnimatePacket::SWING) ) ); + +} + +void MultiplayerLocalPlayer::respawn() +{ + connection->send( shared_ptr( new ClientCommandPacket(ClientCommandPacket::PERFORM_RESPAWN))); +} + + +void MultiplayerLocalPlayer::actuallyHurt(DamageSource *source, int dmg) +{ + setHealth(getHealth() - dmg); +} + +// 4J Added override to capture event for tutorial messages +void MultiplayerLocalPlayer::completeUsingItem() +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(useItem != NULL && pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + Tutorial *tutorial = gameMode->getTutorial(); + tutorial->completeUsingItem(useItem); + } + Player::completeUsingItem(); +} + +void MultiplayerLocalPlayer::onEffectAdded(MobEffectInstance *effect) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + Tutorial *tutorial = gameMode->getTutorial(); + tutorial->onEffectChanged(MobEffect::effects[effect->getId()]); + } + Player::onEffectAdded(effect); +} + + +void MultiplayerLocalPlayer::onEffectUpdated(MobEffectInstance *effect) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + Tutorial *tutorial = gameMode->getTutorial(); + tutorial->onEffectChanged(MobEffect::effects[effect->getId()]); + } + Player::onEffectUpdated(effect); +} + + +void MultiplayerLocalPlayer::onEffectRemoved(MobEffectInstance *effect) +{ + Minecraft *pMinecraft = Minecraft::GetInstance(); + if(pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + Tutorial *tutorial = gameMode->getTutorial(); + tutorial->onEffectChanged(MobEffect::effects[effect->getId()],true); + } + Player::onEffectRemoved(effect); +} + +void MultiplayerLocalPlayer::closeContainer() +{ + connection->send( shared_ptr( new ContainerClosePacket(containerMenu->containerId) ) ); + inventory->setCarried(nullptr); + LocalPlayer::closeContainer(); +} + +void MultiplayerLocalPlayer::hurtTo(int newHealth, ETelemetryChallenges damageSource) +{ + if (flashOnSetHealth) + { + LocalPlayer::hurtTo(newHealth, damageSource); + } + else + { + setHealth(newHealth); + flashOnSetHealth = true; + } +} + +void MultiplayerLocalPlayer::awardStat(Stat *stat, byteArray param) +{ + if (stat == NULL) + { + delete [] param.data; + return; + } + + if (stat->awardLocallyOnly) + { + LocalPlayer::awardStat(stat, param); + } + else + { + delete [] param.data; + return; + } +} + +void MultiplayerLocalPlayer::awardStatFromServer(Stat *stat, byteArray param) +{ + if ( stat != NULL && !stat->awardLocallyOnly ) + { + LocalPlayer::awardStat(stat, param); + } + else delete [] param.data; +} + +void MultiplayerLocalPlayer::onUpdateAbilities() +{ + connection->send(shared_ptr(new PlayerAbilitiesPacket(&abilities))); +} + +bool MultiplayerLocalPlayer::isLocalPlayer() +{ + return true; +} + +void MultiplayerLocalPlayer::ride(shared_ptr e) +{ + bool wasRiding = riding != NULL; + LocalPlayer::ride(e); + bool isRiding = riding != NULL; + + if( isRiding ) + { + ETelemetryChallenges eventType = eTelemetryChallenges_Unknown; + if( this->riding != NULL ) + { + switch(riding->GetType()) + { + case eTYPE_BOAT: + eventType = eTelemetryInGame_Ride_Boat; + break; + case eTYPE_MINECART: + eventType = eTelemetryInGame_Ride_Minecart; + break; + case eTYPE_PIG: + eventType = eTelemetryInGame_Ride_Pig; + break; + }; + } + TelemetryManager->RecordEnemyKilledOrOvercome(GetXboxPad(), 0, y, 0, 0, 0, 0, eventType); + } + + updateRichPresence(); + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->localgameModes[m_iPad] != NULL ) + { + TutorialMode *gameMode = (TutorialMode *)pMinecraft->localgameModes[m_iPad]; + if(wasRiding && !isRiding) + { + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Gameplay); + } + else if (!wasRiding && isRiding) + { + if(dynamic_pointer_cast(e) != NULL) + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Riding_Minecart); + else if(dynamic_pointer_cast(e) != NULL) + gameMode->getTutorial()->changeTutorialState(e_Tutorial_State_Riding_Boat); + } + } +} + +void MultiplayerLocalPlayer::StopSleeping() +{ + connection->send( shared_ptr( new PlayerCommandPacket(shared_from_this(), PlayerCommandPacket::STOP_SLEEPING) ) ); +} + +// 4J Added +void MultiplayerLocalPlayer::setAndBroadcastCustomSkin(DWORD skinId) +{ + DWORD oldSkinIndex = getCustomSkin(); + LocalPlayer::setCustomSkin(skinId); +#ifndef _CONTENT_PACKAGE + wprintf(L"Skin for local player %ls has changed to %ls (%d)\n", name.c_str(), customTextureUrl.c_str(), getPlayerDefaultSkin() ); +#endif + if(getCustomSkin() != oldSkinIndex) connection->send( shared_ptr( new TextureAndGeometryChangePacket( shared_from_this(), app.GetPlayerSkinName(GetXboxPad()) ) ) ); +} + +void MultiplayerLocalPlayer::setAndBroadcastCustomCape(DWORD capeId) +{ + DWORD oldCapeIndex = getCustomCape(); + LocalPlayer::setCustomCape(capeId); +#ifndef _CONTENT_PACKAGE + wprintf(L"Cape for local player %ls has changed to %ls\n", name.c_str(), customTextureUrl2.c_str()); +#endif + if(getCustomCape() != oldCapeIndex) connection->send( shared_ptr( new TextureChangePacket( shared_from_this(), TextureChangePacket::e_TextureChange_Cape, app.GetPlayerCapeName(GetXboxPad()) ) ) ); +} diff --git a/Minecraft.Client/MultiPlayerLocalPlayer.h b/Minecraft.Client/MultiPlayerLocalPlayer.h new file mode 100644 index 0000000..8687b36 --- /dev/null +++ b/Minecraft.Client/MultiPlayerLocalPlayer.h @@ -0,0 +1,73 @@ +#pragma once +#include "LocalPlayer.h" +#include "..\Minecraft.World\SharedConstants.h" + +class ClientConnection; +class Minecraft; +class Level; + +class MultiplayerLocalPlayer : public LocalPlayer +{ +private: + static const int POSITION_REMINDER_INTERVAL = SharedConstants::TICKS_PER_SECOND; +public: + ClientConnection *connection; +private: + bool flashOnSetHealth; +public: + MultiplayerLocalPlayer(Minecraft *minecraft, Level *level, User *user, ClientConnection *connection); +private: + double xLast, yLast1, yLast2, zLast; + float yRotLast, xRotLast; +public: + virtual bool hurt(DamageSource *source, int dmg); + virtual void heal(int heal); + virtual void tick(); +private: + bool lastOnGround; + bool lastSneaked; + bool lastIdle; + bool lastSprinting; + int positionReminder; +public: + void sendPosition(); + + using Player::drop; + virtual shared_ptr drop(); +protected: + virtual void reallyDrop(shared_ptr itemEntity); +public: + virtual void chat(const wstring& message); + virtual void swing(); + virtual void respawn(); +protected: + virtual void actuallyHurt(DamageSource *source, int dmg); + + // 4J Added override to capture event for tutorial messages + virtual void completeUsingItem(); + + // 4J Added overrides to capture events for tutorial + virtual void onEffectAdded(MobEffectInstance *effect); + virtual void onEffectUpdated(MobEffectInstance *effect); + virtual void onEffectRemoved(MobEffectInstance *effect); +public: + virtual void closeContainer(); + virtual void hurtTo(int newHealth, ETelemetryChallenges damageSource); + virtual void awardStat(Stat *stat, byteArray param); + void awardStatFromServer(Stat *stat, byteArray param); + void onUpdateAbilities(); + bool isLocalPlayer(); + + // 4J - send the custom skin texture data if there is one + //void CustomSkin(PBYTE pbData, DWORD dwBytes); + + // 4J Overriding this so we can flag an event for the tutorial + virtual void ride(shared_ptr e); + + // 4J - added for the Stop Sleeping + virtual void StopSleeping(); + + // 4J Added + virtual void setAndBroadcastCustomSkin(DWORD skinId); + virtual void setAndBroadcastCustomCape(DWORD capeId); +}; diff --git a/Minecraft.Client/MushroomCowRenderer.cpp b/Minecraft.Client/MushroomCowRenderer.cpp new file mode 100644 index 0000000..688fab5 --- /dev/null +++ b/Minecraft.Client/MushroomCowRenderer.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include "..\Minecraft.World\net.minecraft.world.entity.animal.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "QuadrupedModel.h" +#include "ModelPart.h" +#include "MushroomCowRenderer.h" + +MushroomCowRenderer::MushroomCowRenderer(Model *model, float shadow) : MobRenderer(model, shadow) +{ +} + +void MushroomCowRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) +{ + // 4J - original version used generics and thus had an input parameter of type MushroomCow rather than shared_ptr we have here - + // do some casting around instead + //shared_ptr mob = dynamic_pointer_cast(_mob); + + // 4J Stu - No need to do the cast, just pass through as-is + MobRenderer::render(_mob, x, y, z, rot, a); +} + +void MushroomCowRenderer::additionalRendering(shared_ptr _mob, float a) +{ + // 4J - original version used generics and thus had an input parameter of type MushroomCow rather than shared_ptr we have here - + // do some casting around instead + shared_ptr mob = dynamic_pointer_cast(_mob); + MobRenderer::additionalRendering(mob, a); + if (mob->isBaby()) return; + bindTexture(TN_TERRAIN); // 4J was "/terrain.png" + glEnable(GL_CULL_FACE); + glPushMatrix(); + glScalef(1, -1, 1); + glTranslatef(0.2f, 0.4f, 0.5f); + glRotatef(42, 0, 1, 0); + tileRenderer->renderTile(Tile::mushroom2, 0, 1); + glTranslatef(0.1f, 0, -0.6f); + glRotatef(42, 0, 1, 0); + tileRenderer->renderTile(Tile::mushroom2, 0, 1); + glPopMatrix(); + + glPushMatrix(); + ((QuadrupedModel *) model)->head->translateTo(1 / 16.0f); + glScalef(1, -1, 1); + glTranslatef(0, 0.75f, -0.2f); + glRotatef(12, 0, 1, 0); + tileRenderer->renderTile(Tile::mushroom2, 0, 1); + glPopMatrix(); + + glDisable(GL_CULL_FACE); +} \ No newline at end of file diff --git a/Minecraft.Client/MushroomCowRenderer.h b/Minecraft.Client/MushroomCowRenderer.h new file mode 100644 index 0000000..c5f6f3c --- /dev/null +++ b/Minecraft.Client/MushroomCowRenderer.h @@ -0,0 +1,14 @@ +#pragma once + +#include "MobRenderer.h" + +class MushroomCowRenderer : public MobRenderer +{ +public: + MushroomCowRenderer(Model *model, float shadow); + + virtual void render(shared_ptr _mob, double x, double y, double z, float rot, float a); + +protected: + virtual void additionalRendering(shared_ptr _mob, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/NameEntryScreen.cpp b/Minecraft.Client/NameEntryScreen.cpp new file mode 100644 index 0000000..c9df702 --- /dev/null +++ b/Minecraft.Client/NameEntryScreen.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "NameEntryScreen.h" +#include "Button.h" +#include "..\Minecraft.World\StringHelpers.h" + +const wstring NameEntryScreen::allowedChars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,.:-_'*!\"#%/()=+?[]{}<>"; + +NameEntryScreen::NameEntryScreen(Screen *lastScreen, const wstring& oldName, int slot) +{ + frame = 0; // 4J added + + this->lastScreen = lastScreen; + this->slot = slot; + this->name = oldName; + if (name==L"-") name = L""; +} + +void NameEntryScreen::init() +{ + buttons.clear(); + Keyboard::enableRepeatEvents(true); + buttons.push_back(new Button(0, width / 2 - 100, height / 4 + 24 * 5, L"Save")); + buttons.push_back(new Button(1, width / 2 - 100, height / 4 + 24 * 6, L"Cancel")); + buttons[0]->active = trimString(name).length() > 1; +} + +void NameEntryScreen::removed() +{ + Keyboard::enableRepeatEvents(false); +} + +void NameEntryScreen::tick() +{ + frame++; +} + +void NameEntryScreen::buttonClicked(Button button) +{ + if (!button.active) return; + + if (button.id == 0 && trimString(name).length() > 1) + { + minecraft->saveSlot(slot, trimString(name)); + minecraft->setScreen(NULL); +// minecraft->grabMouse(); // 4J - removed + } + if (button.id == 1) + { + minecraft->setScreen(lastScreen); + } +} + +void NameEntryScreen::keyPressed(wchar_t ch, int eventKey) +{ + if (eventKey == Keyboard::KEY_BACK && name.length() > 0) name = name.substr(0, name.length() - 1); + if (allowedChars.find(ch) != wstring::npos && name.length()<64) + { + name += ch; + } + buttons[0]->active = trimString(name).length() > 1; +} + +void NameEntryScreen::render(int xm, int ym, float a) +{ + renderBackground(); + + drawCenteredString(font, title, width / 2, 40, 0xffffff); + + int bx = width / 2 - 100; + int by = height / 2 - 10; + int bw = 200; + int bh = 20; + fill(bx - 1, by - 1, bx + bw + 1, by + bh + 1, 0xffa0a0a0); + fill(bx, by, bx + bw, by + bh, 0xff000000); + drawString(font, name + (frame / 6 % 2 == 0 ? L"_" : L""), bx + 4, by + (bh - 8) / 2, 0xe0e0e0); + + Screen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/NameEntryScreen.h b/Minecraft.Client/NameEntryScreen.h new file mode 100644 index 0000000..7507748 --- /dev/null +++ b/Minecraft.Client/NameEntryScreen.h @@ -0,0 +1,27 @@ +#pragma once +#include "Screen.h" + +class NameEntryScreen : public Screen +{ +private: + Screen *lastScreen; +protected: + wstring title; +private: + int slot; + wstring name; + int frame; +public: + NameEntryScreen(Screen *lastScreen, const wstring& oldName, int slot); + virtual void init(); + virtual void removed(); + virtual void tick(); +protected: + virtual void buttonClicked(Button button); +private: + static const wstring allowedChars; +protected: + virtual void keyPressed(wchar_t ch, int eventKey); +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/NetherPortalParticle.cpp b/Minecraft.Client/NetherPortalParticle.cpp new file mode 100644 index 0000000..4b70d38 --- /dev/null +++ b/Minecraft.Client/NetherPortalParticle.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "NetherPortalParticle.h" +#include "..\Minecraft.World\JavaMath.h" +#include "..\Minecraft.World\Random.h" +#include "Minecraft.h" + +// 4J Stu - This class was originally "PortalParticle" but I have split the two uses of the particle +// Only the nether portal uses this particle + +NetherPortalParticle::NetherPortalParticle(Level *level, double x, double y, double z, double xd, double yd, double zd) : Particle(level, x, y, z, xd, yd, zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + this->xStart = this->x = x; + this->yStart = this->y = y; + this->zStart = this->z = z; + + float br = random->nextFloat()*0.6f+0.4f; + oSize = size = random->nextFloat()*0.2f+0.5f; + //rCol = gCol = bCol = 1.0f*br; + //gCol *= 0.3f; + //rCol *= 0.9f; + + // Default colour (0.9f, 0.3f, 1.0f) + // 0xE64DFF + + unsigned int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_NetherPortal ); + int r = (colour>>16)&0xFF; + int g = (colour>>8)&0xFF; + int b = colour&0xFF; + rCol = (r/255.0f)*br; + gCol = (g/255.0f)*br; + bCol = (b/255.0f)*br; + + lifetime = (int) (Math::random()*10) + 40; + noPhysics = true; + setMiscTex((int)(Math::random()*8)); +} + +void NetherPortalParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float s = (age + a) / (float) lifetime; + s = 1-s; + s = s*s; + s = 1-s; + size = oSize * (s); + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +// 4J - brought forward from 1.8.2 +int NetherPortalParticle::getLightColor(float a) +{ + int br = Particle::getLightColor(a); + + float pos = age/(float)lifetime; + pos = pos*pos; + pos = pos*pos; + + int br1 = (br) & 0xff; + int br2 = (br >> 16) & 0xff; + br2 += (int) (pos * 15 * 16); + if (br2 > 15 * 16) br2 = 15 * 16; + return br1 | br2 << 16; +} + +float NetherPortalParticle::getBrightness(float a) +{ + float br = Particle::getBrightness(a); + float pos = age/(float)lifetime; + pos = pos*pos; + pos = pos*pos; + return br*(1-pos)+pos; +} + +void NetherPortalParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + float pos = age/(float)lifetime; + float a = pos; + pos = -pos+pos*pos*2; +// pos = pos*pos; +// pos = pos*pos; + pos = 1-pos; + + x = xStart+xd*pos; + y = yStart+yd*pos+(1-a); + z = zStart+zd*pos; + + +// spd+=0.002/lifetime*age; + + if (age++ >= lifetime) remove(); + +// move(xd*spd, yd*spd, zd*spd); +} diff --git a/Minecraft.Client/NetherPortalParticle.h b/Minecraft.Client/NetherPortalParticle.h new file mode 100644 index 0000000..85aa8c6 --- /dev/null +++ b/Minecraft.Client/NetherPortalParticle.h @@ -0,0 +1,21 @@ +#pragma once +#include "Particle.h" + +// 4J Stu - This class was originally "PortalParticle" but I have split the two uses of the particle +// Only the nether portal uses this particle + +class NetherPortalParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_NETHERPORTALPARTICLE; } +private: + float oSize; + double xStart, yStart, zStart; + +public: + NetherPortalParticle(Level *level, double x, double y, double z, double xd, double yd, double zd); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual int getLightColor(float a); // 4J - brought forward from 1.8.2 + virtual float getBrightness(float a); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Network Implementation Notes.txt b/Minecraft.Client/Network Implementation Notes.txt new file mode 100644 index 0000000..e482952 --- /dev/null +++ b/Minecraft.Client/Network Implementation Notes.txt @@ -0,0 +1,45 @@ +NETWORK CODE IMPLEMENTATION NOTES +--------------------------------- + +The networking classes are organised as follows: + + Game \ + ^ | + | | + +-----------------------------+-----------------------------+ | + | | | + v v | +Game Network Manager <--------------------------------> Network Player Interface |- platform independent layers + ^ ^ | + | | | + v | | +Platform Network Manager Interface | | + ^ | / + | | + v v \ +Platform Network Manager Implementation(1) <------> Network Player Implementation (3) | + ^ ^ |_ platform specific layers + | | | + v v | +Platform specific network code(2) Platform specific player code (4) / + + +In general the game should only communicate with the GameNetworkManager and NetworkPlayerInterface APIs, which provide a platform independent +interface for networking functionality. The GameNetworkManager may in general have code which is aware of the game itself, but it shouldn't have +any platform-specific networking code. It communicates with a platform specific implementation of a PlatformNetworkManagerInterface to achieve this. + +The platform specific layers shouldn't contain any general game code, as this is much better placed in the platform independent layers to avoid +duplicating effort. + +Platform specific files for each platform for the numbered classes in the previous diagram are currently: + + + Xbox 360 Sony Other + +(1) PlatformNetworkManagerXbox PlatformNetworkManagerSony PlatformNetworkManagerStub +(2) Provided by QNET SQRNetworkManager Qnet stub* +(3) NetworkPlayerXbox NetworkPlayerSony NetworkPlayerXbox +(4) Provided by QNET SQRNetworkPlayer Qnet stub* + + *temporarily provided by extra64.h + diff --git a/Minecraft.Client/NoteParticle.cpp b/Minecraft.Client/NoteParticle.cpp new file mode 100644 index 0000000..2e2320c --- /dev/null +++ b/Minecraft.Client/NoteParticle.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "..\Minecraft.World\Mth.h" +#include "NoteParticle.h" + +void NoteParticle::init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) +{ + xd *= 0.01f; + yd *= 0.01f; + zd *= 0.01f; + yd += 0.2; + + /* + unsigned int cMin = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_NoteMin ); + unsigned int cMax = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_NoteMax ); + double rMin = ( (cMin>>16)&0xFF )/255.0f, gMin = ( (cMin>>8)&0xFF )/255.0, bMin = ( cMin&0xFF )/255.0; + double rMax = ( (cMax>>16)&0xFF )/255.0f, gMax = ( (cMax>>8)&0xFF )/255.0, bMax = ( cMax&0xFF )/255.0; + + rCol = Mth::sin(((float) xa + 0.0f / 3) * PI * 2) * (rMax - rMin) + rMin; + gCol = Mth::sin(((float) xa + 1.0f / 3) * PI * 2) * (gMax - gMin) + gMin; + bCol = Mth::sin(((float) xa + 2.0f / 3) * PI * 2) * (bMax - bMin) + bMin; + */ + + // 4J-JEV: Added, + // There are 24 valid colours for this particle input through the 'xa' field (0.0-1.0). + int note = (int) floor(0.5 + (xa*24.0)) + (int) eMinecraftColour_Particle_Note_00; + unsigned int col = Minecraft::GetInstance()->getColourTable()->getColor( (eMinecraftColour) note ); + + rCol = ( (col>>16)&0xFF )/255.0; + gCol = ( (col>>8)&0xFF )/255.0; + bCol = ( col&0xFF )/255.0; + + size *= 0.75f; + size *= scale; + oSize = size; + + lifetime = 6; + noPhysics = false; + + + setMiscTex(16 * 4); +} + +NoteParticle::NoteParticle(Level *level, double x, double y, double z, double xa, double ya, double za) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level, x, y, z, xa, ya, za, 2); +} + +NoteParticle::NoteParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale) : Particle(level, x, y, z, 0, 0, 0) +{ + init(level, x, y, z, xa, ya, za, scale); +} + +void NoteParticle::render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2) +{ + float l = ((age + a) / lifetime) * 32; + if (l < 0) l = 0; + if (l > 1) l = 1; + + size = oSize * l; + Particle::render(t, a, xa, ya, za, xa2, za2); +} + +void NoteParticle::tick() +{ + xo = x; + yo = y; + zo = z; + + if (age++ >= lifetime) remove(); + + move(xd, yd, zd); + if (y == yo) + { + xd *= 1.1; + zd *= 1.1; + } + xd *= 0.66f; + yd *= 0.66f; + zd *= 0.66f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + } + +} diff --git a/Minecraft.Client/NoteParticle.h b/Minecraft.Client/NoteParticle.h new file mode 100644 index 0000000..b53910b --- /dev/null +++ b/Minecraft.Client/NoteParticle.h @@ -0,0 +1,16 @@ +#pragma once +#include "Particle.h" + +class NoteParticle : public Particle +{ +public: + virtual eINSTANCEOF GetType() { return eType_NOTEPARTICLE; } +private: + void init(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); // 4J - added +public: + NoteParticle(Level *level, double x, double y, double z, double xa, double ya, double za); + float oSize; + NoteParticle(Level *level, double x, double y, double z, double xa, double ya, double za, float scale); + virtual void render(Tesselator *t, float a, float xa, float ya, float za, float xa2, float za2); + virtual void tick(); +}; diff --git a/Minecraft.Client/OffsettedRenderList.cpp b/Minecraft.Client/OffsettedRenderList.cpp new file mode 100644 index 0000000..729d26f --- /dev/null +++ b/Minecraft.Client/OffsettedRenderList.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "..\Minecraft.World\IntBuffer.h" +#include "OffsettedRenderList.h" + +// 4J added +OffsettedRenderList::OffsettedRenderList() +{ + x = y = z = 0; + xOff = yOff = zOff = 0; + lists = MemoryTracker::createIntBuffer(1024 * 64); + inited = false; + rendered = false; +} + +void OffsettedRenderList::init(int x, int y, int z, double xOff, double yOff, double zOff) +{ + inited = true; + lists->clear(); + this->x = x; + this->y = y; + this->z = z; + + this->xOff = (float) xOff; + this->yOff = (float) yOff; + this->zOff = (float) zOff; +} + +bool OffsettedRenderList::isAt(int x, int y, int z) +{ + if (!inited) return false; + return x == this->x && y == this->y && z == this->z; +} + +void OffsettedRenderList::add(int list) +{ + // 4J - added - chunkList::getList returns -1 when chunks aren't visible, we really don't want to end up sending that to glCallLists + if( list >= 0 ) + { + lists->put(list); + } + if (lists->remaining() == 0) render(); +} + +void OffsettedRenderList::render() +{ + if (!inited) return; + if (!rendered) + { + lists->flip(); + rendered = true; + } + if (lists->remaining() > 0) + { + glPushMatrix(); + glTranslatef(x - xOff, y - yOff, z - zOff); + glCallLists(lists); + glPopMatrix(); + } +} + +void OffsettedRenderList::clear() +{ + inited = false; + rendered = false; +} \ No newline at end of file diff --git a/Minecraft.Client/OffsettedRenderList.h b/Minecraft.Client/OffsettedRenderList.h new file mode 100644 index 0000000..c989da3 --- /dev/null +++ b/Minecraft.Client/OffsettedRenderList.h @@ -0,0 +1,21 @@ +#pragma once + +class IntBuffer; + +class OffsettedRenderList +{ +private: + int x, y, z; + float xOff, yOff, zOff; + IntBuffer *lists; + bool inited; + bool rendered ; + +public: + OffsettedRenderList(); // 4J added + void init(int x, int y, int z, double xOff, double yOff, double zOff); + bool isAt(int x, int y, int z); + void add(int list); + void render(); + void clear(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Options.cpp b/Minecraft.Client/Options.cpp new file mode 100644 index 0000000..9952fc8 --- /dev/null +++ b/Minecraft.Client/Options.cpp @@ -0,0 +1,525 @@ +#include "stdafx.h" +#include "Options.h" +#include "KeyMapping.h" +#include "LevelRenderer.h" +#include "Textures.h" +#include "..\Minecraft.World\net.minecraft.locale.h" +#include "..\Minecraft.World\Language.h" +#include "..\Minecraft.World\File.h" +#include "..\Minecraft.World\BufferedReader.h" +#include "..\Minecraft.World\DataInputStream.h" +#include "..\Minecraft.World\InputStreamReader.h" +#include "..\Minecraft.World\FileInputStream.h" +#include "..\Minecraft.World\FileOutputStream.h" +#include "..\Minecraft.World\DataOutputStream.h" +#include "..\Minecraft.World\StringHelpers.h" + +// 4J - the Option sub-class used to be an java enumerated type, trying to emulate that functionality here +const Options::Option Options::Option::options[17] = +{ + Options::Option(L"options.music", true, false), + Options::Option(L"options.sound", true, false), + Options::Option(L"options.invertMouse", false, true), + Options::Option(L"options.sensitivity", true, false), + Options::Option(L"options.renderDistance", false, false), + Options::Option(L"options.viewBobbing", false, true), + Options::Option(L"options.anaglyph", false, true), + Options::Option(L"options.advancedOpengl", false, true), + Options::Option(L"options.framerateLimit", false, false), + Options::Option(L"options.difficulty", false, false), + Options::Option(L"options.graphics", false, false), + Options::Option(L"options.ao", false, true), + Options::Option(L"options.guiScale", false, false), + Options::Option(L"options.fov", true, false), + Options::Option(L"options.gamma", true, false), + Options::Option(L"options.renderClouds",false, true), + Options::Option(L"options.particles", false, false), +}; + +const Options::Option *Options::Option::MUSIC = &Options::Option::options[0]; +const Options::Option *Options::Option::SOUND = &Options::Option::options[1]; +const Options::Option *Options::Option::INVERT_MOUSE = &Options::Option::options[2]; +const Options::Option *Options::Option::SENSITIVITY = &Options::Option::options[3]; +const Options::Option *Options::Option::RENDER_DISTANCE = &Options::Option::options[4]; +const Options::Option *Options::Option::VIEW_BOBBING = &Options::Option::options[5]; +const Options::Option *Options::Option::ANAGLYPH = &Options::Option::options[6]; +const Options::Option *Options::Option::ADVANCED_OPENGL = &Options::Option::options[7]; +const Options::Option *Options::Option::FRAMERATE_LIMIT = &Options::Option::options[8]; +const Options::Option *Options::Option::DIFFICULTY = &Options::Option::options[9]; +const Options::Option *Options::Option::GRAPHICS = &Options::Option::options[10]; +const Options::Option *Options::Option::AMBIENT_OCCLUSION = &Options::Option::options[11]; +const Options::Option *Options::Option::GUI_SCALE = &Options::Option::options[12]; +const Options::Option *Options::Option::FOV = &Options::Option::options[13]; +const Options::Option *Options::Option::GAMMA = &Options::Option::options[14]; +const Options::Option *Options::Option::RENDER_CLOUDS = &Options::Option::options[15]; +const Options::Option *Options::Option::PARTICLES = &Options::Option::options[16]; + + +const Options::Option *Options::Option::getItem(int id) +{ + return &options[id]; +} + +Options::Option::Option(const wstring& captionId, bool hasProgress, bool isBoolean) : _isProgress(hasProgress), _isBoolean(isBoolean), captionId(captionId) +{ +} + +bool Options::Option::isProgress() const +{ + return _isProgress; +} + +bool Options::Option::isBoolean() const +{ + return _isBoolean; +} + +int Options::Option::getId() const +{ + return (int)(this-options); +} + +wstring Options::Option::getCaptionId() const +{ + return captionId; +} + +const wstring Options::RENDER_DISTANCE_NAMES[] = +{ + L"options.renderDistance.far", L"options.renderDistance.normal", L"options.renderDistance.short", L"options.renderDistance.tiny" +}; +const wstring Options::DIFFICULTY_NAMES[] = +{ + L"options.difficulty.peaceful", L"options.difficulty.easy", L"options.difficulty.normal", L"options.difficulty.hard" +}; +const wstring Options::GUI_SCALE[] = +{ + L"options.guiScale.auto", L"options.guiScale.small", L"options.guiScale.normal", L"options.guiScale.large" +}; +const wstring Options::FRAMERATE_LIMITS[] = +{ + L"performance.max", L"performance.balanced", L"performance.powersaver" +}; + +const wstring Options::PARTICLES[] = { + L"options.particles.all", L"options.particles.decreased", L"options.particles.minimal" +}; + +// 4J added +void Options::init() +{ + music = 1; + sound = 1; + sensitivity = 0.5f; + invertYMouse = false; + viewDistance = 0; + bobView = true; + anaglyph3d = false; + advancedOpengl = false; + framerateLimit = 2; + fancyGraphics = true; + ambientOcclusion = true; + renderClouds = true; + skin = L"Default"; + + keyUp = new KeyMapping(L"key.forward", Keyboard::KEY_W); + keyLeft = new KeyMapping(L"key.left", Keyboard::KEY_A); + keyDown = new KeyMapping(L"key.back", Keyboard::KEY_S); + keyRight = new KeyMapping(L"key.right", Keyboard::KEY_D); + keyJump = new KeyMapping(L"key.jump", Keyboard::KEY_SPACE); + keyBuild = new KeyMapping(L"key.inventory", Keyboard::KEY_E); + keyDrop = new KeyMapping(L"key.drop", Keyboard::KEY_Q); + keyChat = new KeyMapping(L"key.chat", Keyboard::KEY_T); + keySneak = new KeyMapping(L"key.sneak", Keyboard::KEY_LSHIFT); + keyAttack = new KeyMapping(L"key.attack", -100 + 0); + keyUse = new KeyMapping(L"key.use", -100 + 1); + keyPlayerList = new KeyMapping(L"key.playerlist", Keyboard::KEY_TAB); + keyPickItem = new KeyMapping(L"key.pickItem", -100 + 2); + keyToggleFog = new KeyMapping(L"key.fog", Keyboard::KEY_F); + + keyMappings[0] = keyAttack; + keyMappings[1] = keyUse; + keyMappings[2] = keyUp; + keyMappings[3] = keyLeft; + keyMappings[4] = keyDown; + keyMappings[5] = keyRight; + keyMappings[6] = keyJump; + keyMappings[7] = keySneak; + keyMappings[8] = keyDrop; + keyMappings[9] = keyBuild; + keyMappings[10] = keyChat; + keyMappings[11] = keyPlayerList; + keyMappings[12] = keyPickItem; + keyMappings[13] = keyToggleFog; + + minecraft = NULL; + //optionsFile = NULL; + + difficulty = 2; + hideGui = false; + thirdPersonView = false; + renderDebug = false; + lastMpIp = L""; + + isFlying = false; + smoothCamera = false; + fixedCamera = false; + flySpeed = 1; + cameraSpeed = 1; + guiScale = 0; + particles = 0; + fov = 0; + gamma = 0; +} + +Options::Options(Minecraft *minecraft, File workingDirectory) +{ + init(); + this->minecraft = minecraft; + optionsFile = File(workingDirectory, L"options.txt"); +} + +Options::Options() +{ + init(); +} + +wstring Options::getKeyDescription(int i) +{ + Language *language = Language::getInstance(); + return language->getElement(keyMappings[i]->name); +} + +wstring Options::getKeyMessage(int i) +{ + int key = keyMappings[i]->key; + if (key < 0) { + return I18n::get(L"key.mouseButton", key + 101); + } else { + return Keyboard::getKeyName(keyMappings[i]->key); + } +} + +void Options::setKey(int i, int key) +{ + keyMappings[i]->key = key; + save(); +} + +void Options::set(const Options::Option *item, float fVal) +{ + if (item == Option::MUSIC) + { + music = fVal; +#ifdef _XBOX + minecraft->soundEngine->updateMusicVolume(fVal*2.0f); +#else + minecraft->soundEngine->updateMusicVolume(fVal); +#endif + } + if (item == Option::SOUND) + { + sound = fVal; +#ifdef _XBOX + minecraft->soundEngine->updateSoundEffectVolume(fVal*2.0f); +#else + minecraft->soundEngine->updateSoundEffectVolume(fVal); +#endif + } + if (item == Option::SENSITIVITY) + { + sensitivity = fVal; + } + if (item == Option::FOV) + { + fov = fVal; + } + if (item == Option::GAMMA) + { + gamma = fVal; + } +} + +void Options::toggle(const Options::Option *option, int dir) +{ + if (option == Option::INVERT_MOUSE) invertYMouse = !invertYMouse; + if (option == Option::RENDER_DISTANCE) viewDistance = (viewDistance + dir) & 3; + if (option == Option::GUI_SCALE) guiScale = (guiScale + dir) & 3; + if (option == Option::PARTICLES) particles = (particles + dir) % 3; + + // 4J-PB - changing + //if (option == Option::VIEW_BOBBING) bobView = !bobView; + if (option == Option::VIEW_BOBBING) ((dir==0)?bobView=false: bobView=true); + if (option == Option::RENDER_CLOUDS) renderClouds = !renderClouds; + if (option == Option::ADVANCED_OPENGL) + { + advancedOpengl = !advancedOpengl; + minecraft->levelRenderer->allChanged(); + } + if (option == Option::ANAGLYPH) + { + anaglyph3d = !anaglyph3d; + minecraft->textures->reloadAll(); + } + if (option == Option::FRAMERATE_LIMIT) framerateLimit = (framerateLimit + dir + 3) % 3; + + // 4J-PB - Change for Xbox + //if (option == Option::DIFFICULTY) difficulty = (difficulty + dir) & 3; + if (option == Option::DIFFICULTY) difficulty = (dir) & 3; + + app.DebugPrintf("Option::DIFFICULTY = %d",difficulty); + + if (option == Option::GRAPHICS) + { + fancyGraphics = !fancyGraphics; + minecraft->levelRenderer->allChanged(); + } + if (option == Option::AMBIENT_OCCLUSION) + { + ambientOcclusion = !ambientOcclusion; + minecraft->levelRenderer->allChanged(); + } + + // 4J-PB - don't do the file save on the xbox + // save(); + +} + +float Options::getProgressValue(const Options::Option *item) +{ + if (item == Option::FOV) return fov; + if (item == Option::GAMMA) return gamma; + if (item == Option::MUSIC) return music; + if (item == Option::SOUND) return sound; + if (item == Option::SENSITIVITY) return sensitivity; + return 0; +} + +bool Options::getBooleanValue(const Options::Option *item) +{ + // 4J - was a switch statement which we can't do with our Option:: pointer types + if( item == Option::INVERT_MOUSE) return invertYMouse; + if( item == Option::VIEW_BOBBING) return bobView; + if( item == Option::ANAGLYPH) return anaglyph3d; + if( item == Option::ADVANCED_OPENGL) return advancedOpengl; + if( item == Option::AMBIENT_OCCLUSION) return ambientOcclusion; + if( item == Option::RENDER_CLOUDS) return renderClouds; + return false; +} + +wstring Options::getMessage(const Options::Option *item) +{ + // 4J TODO, should these wstrings append rather than add? + + Language *language = Language::getInstance(); + wstring caption = language->getElement(item->getCaptionId()) + L": "; + + if (item->isProgress()) + { + float progressValue = getProgressValue(item); + + if (item == Option::SENSITIVITY) + { + if (progressValue == 0) + { + return caption + language->getElement(L"options.sensitivity.min"); + } + if (progressValue == 1) + { + return caption + language->getElement(L"options.sensitivity.max"); + } + return caption + _toString((int) (progressValue * 200)) + L"%"; + } else if (item == Option::FOV) + { + if (progressValue == 0) + { + return caption + language->getElement(L"options.fov.min"); + } + if (progressValue == 1) + { + return caption + language->getElement(L"options.fov.max"); + } + return caption + _toString((int) (70 + progressValue * 40)); + } else if (item == Option::GAMMA) + { + if (progressValue == 0) + { + return caption + language->getElement(L"options.gamma.min"); + } + if (progressValue == 1) + { + return caption + language->getElement(L"options.gamma.max"); + } + return caption + L"+" + _toString((int) (progressValue * 100)) + L"%"; + } + else + { + if (progressValue == 0) + { + return caption + language->getElement(L"options.off"); + } + return caption + _toString((int) (progressValue * 100)) + L"%"; + } + } else if (item->isBoolean()) + { + + bool booleanValue = getBooleanValue(item); + if (booleanValue) + { + return caption + language->getElement(L"options.on"); + } + return caption + language->getElement(L"options.off"); + } + else if (item == Option::RENDER_DISTANCE) + { + return caption + language->getElement(RENDER_DISTANCE_NAMES[viewDistance]); + } + else if (item == Option::DIFFICULTY) + { + return caption + language->getElement(DIFFICULTY_NAMES[difficulty]); + } + else if (item == Option::GUI_SCALE) + { + return caption + language->getElement(GUI_SCALE[guiScale]); + } + else if (item == Option::PARTICLES) + { + return caption + language->getElement(PARTICLES[particles]); + } + else if (item == Option::FRAMERATE_LIMIT) + { + return caption + I18n::get(FRAMERATE_LIMITS[framerateLimit]); + } + else if (item == Option::GRAPHICS) + { + if (fancyGraphics) + { + return caption + language->getElement(L"options.graphics.fancy"); + } + return caption + language->getElement(L"options.graphics.fast"); + } + + return caption; + +} + +void Options::load() +{ + // 4J - removed try/catch +// try { + if (!optionsFile.exists()) return; + // 4J - was new BufferedReader(new FileReader(optionsFile)); + BufferedReader *br = new BufferedReader(new InputStreamReader( new FileInputStream( optionsFile ) ) ); + + wstring line = L""; + while ((line = br->readLine()) != L"") // 4J - was check against NULL - do we need to distinguish between empty lines and a fail here? + { + // 4J - removed try/catch +// try { + wstring cmds[2]; + int splitpos = (int)line.find(L":"); + if( splitpos == wstring::npos ) + { + cmds[0] = line; + cmds[1] = L""; + } + else + { + cmds[0] = line.substr(0,splitpos); + cmds[1] = line.substr(splitpos,line.length()-splitpos); + } + + if (cmds[0] == L"music") music = readFloat(cmds[1]); + if (cmds[0] == L"sound") sound = readFloat(cmds[1]); + if (cmds[0] == L"mouseSensitivity") sensitivity = readFloat(cmds[1]); + if (cmds[0] == L"fov") fov = readFloat(cmds[1]); + if (cmds[0] == L"gamma") gamma = readFloat(cmds[1]); + if (cmds[0] == L"invertYMouse") invertYMouse = cmds[1]==L"true"; + if (cmds[0] == L"viewDistance") viewDistance = _fromString(cmds[1]); + if (cmds[0] == L"guiScale") guiScale =_fromString(cmds[1]); + if (cmds[0] == L"particles") particles = _fromString(cmds[1]); + if (cmds[0] == L"bobView") bobView = cmds[1]==L"true"; + if (cmds[0] == L"anaglyph3d") anaglyph3d = cmds[1]==L"true"; + if (cmds[0] == L"advancedOpengl") advancedOpengl = cmds[1]==L"true"; + if (cmds[0] == L"fpsLimit") framerateLimit = _fromString(cmds[1]); + if (cmds[0] == L"difficulty") difficulty = _fromString(cmds[1]); + if (cmds[0] == L"fancyGraphics") fancyGraphics = cmds[1]==L"true"; + if (cmds[0] == L"ao") ambientOcclusion = cmds[1]==L"true"; + if (cmds[0] == L"clouds") renderClouds = cmds[1]==L"true"; + if (cmds[0] == L"skin") skin = cmds[1]; + if (cmds[0] == L"lastServer") lastMpIp = cmds[1]; + + for (int i = 0; i < keyMappings_length; i++) + { + if (cmds[0] == (L"key_" + keyMappings[i]->name)) + { + keyMappings[i]->key = _fromString(cmds[1]); + } + } +// } catch (Exception e) { +// System.out.println("Skipping bad option: " + line); +// } + } + //KeyMapping.resetMapping(); // 4J Not implemented + br->close(); +// } catch (Exception e) { +// System.out.println("Failed to load options"); +// e.printStackTrace(); +// } + +} + +float Options::readFloat(wstring string) +{ + if (string == L"true") return 1; + if (string == L"false") return 0; + return _fromString(string); +} + +void Options::save() +{ + // 4J - try/catch removed +// try { + + // 4J - original used a PrintWriter & FileWriter, but seems a bit much implementing these just to do this + FileOutputStream fos = FileOutputStream(optionsFile); + DataOutputStream dos = DataOutputStream(&fos); +// PrintWriter pw = new PrintWriter(new FileWriter(optionsFile)); + + dos.writeChars(L"music:" + _toString(music) + L"\n"); + dos.writeChars(L"sound:" + _toString(sound) + L"\n"); + dos.writeChars(L"invertYMouse:" + wstring(invertYMouse ? L"true" : L"false") + L"\n"); + dos.writeChars(L"mouseSensitivity:" + _toString(sensitivity)); + dos.writeChars(L"fov:" + _toString(fov)); + dos.writeChars(L"gamma:" + _toString(gamma)); + dos.writeChars(L"viewDistance:" + _toString(viewDistance)); + dos.writeChars(L"guiScale:" + _toString(guiScale)); + dos.writeChars(L"particles:" + _toString(particles)); + dos.writeChars(L"bobView:" + wstring(bobView ? L"true" : L"false")); + dos.writeChars(L"anaglyph3d:" + wstring(anaglyph3d ? L"true" : L"false")); + dos.writeChars(L"advancedOpengl:" + wstring(advancedOpengl ? L"true" : L"false")); + dos.writeChars(L"fpsLimit:" + _toString(framerateLimit)); + dos.writeChars(L"difficulty:" + _toString(difficulty)); + dos.writeChars(L"fancyGraphics:" + wstring(fancyGraphics ? L"true" : L"false")); + dos.writeChars(L"ao:" + wstring(ambientOcclusion ? L"true" : L"false")); + dos.writeChars(L"clouds:" + _toString(renderClouds)); + dos.writeChars(L"skin:" + skin); + dos.writeChars(L"lastServer:" + lastMpIp); + + for (int i = 0; i < keyMappings_length; i++) + { + dos.writeChars(L"key_" + keyMappings[i]->name + L":" + _toString(keyMappings[i]->key)); + } + + dos.close(); +// } catch (Exception e) { +// System.out.println("Failed to save options"); +// e.printStackTrace(); +// } + +} + +bool Options::isCloudsOn() +{ + return viewDistance < 2 && renderClouds; +} \ No newline at end of file diff --git a/Minecraft.Client/Options.h b/Minecraft.Client/Options.h new file mode 100644 index 0000000..8be61ac --- /dev/null +++ b/Minecraft.Client/Options.h @@ -0,0 +1,132 @@ +#pragma once +using namespace std; +class Minecraft; +class KeyMapping; +#include "..\Minecraft.World\File.h" + +class Options +{ +public: + static const int AO_OFF = 0; + static const int AO_MIN = 1; + static const int AO_MAX = 2; + + // 4J - this used to be an enum + class Option + { + public: + static const Option options[17]; + static const Option *MUSIC; + static const Option *SOUND; + static const Option *INVERT_MOUSE; + static const Option *SENSITIVITY; + static const Option *RENDER_DISTANCE; + static const Option *VIEW_BOBBING; + static const Option *ANAGLYPH; + static const Option *ADVANCED_OPENGL; + static const Option *FRAMERATE_LIMIT; + static const Option *DIFFICULTY; + static const Option *GRAPHICS; + static const Option *AMBIENT_OCCLUSION; + static const Option *GUI_SCALE; + static const Option *FOV; + static const Option *GAMMA; + static const Option *RENDER_CLOUDS; + static const Option *PARTICLES; + + private: + const bool _isProgress; + const bool _isBoolean; + const wstring captionId; + + public: + static const Option *getItem(int id); + + Option(const wstring& captionId, bool hasProgress, bool isBoolean); + bool isProgress() const; + bool isBoolean() const; + int getId() const; + wstring getCaptionId() const; + }; + +private: + static const wstring RENDER_DISTANCE_NAMES[]; + static const wstring DIFFICULTY_NAMES[]; + static const wstring GUI_SCALE[]; + static const wstring FRAMERATE_LIMITS[]; + static const wstring PARTICLES[]; + +public: + float music; + float sound; + float sensitivity; + bool invertYMouse; + int viewDistance; + bool bobView; + bool anaglyph3d; + bool advancedOpengl; + int framerateLimit; + bool fancyGraphics; + bool ambientOcclusion; + bool renderClouds; + wstring skin; + + KeyMapping *keyUp; + KeyMapping *keyLeft; + KeyMapping *keyDown; + KeyMapping *keyRight; + KeyMapping *keyJump; + KeyMapping *keyBuild; + KeyMapping *keyDrop; + KeyMapping *keyChat; + KeyMapping *keySneak; + KeyMapping *keyAttack; + KeyMapping *keyUse; + KeyMapping *keyPlayerList; + KeyMapping *keyPickItem; + KeyMapping *keyToggleFog; + + static const int keyMappings_length = 14; + KeyMapping *keyMappings[keyMappings_length]; + +protected: + Minecraft *minecraft; +private: + File optionsFile; + +public: + int difficulty; + bool hideGui; + bool thirdPersonView; + bool renderDebug; + wstring lastMpIp; + + bool isFlying; + bool smoothCamera; + bool fixedCamera; + float flySpeed; + float cameraSpeed; + int guiScale; + int particles; // 0 is all, 1 is decreased and 2 is minimal + float fov; + float gamma; + + void init(); // 4J added + Options(Minecraft *minecraft, File workingDirectory); + Options(); + wstring getKeyDescription(int i); + wstring getKeyMessage(int i); + void setKey(int i, int key); + void set(const Options::Option *item, float value); + void toggle(const Options::Option *option, int dir); + float getProgressValue(const Options::Option *item); + bool getBooleanValue(const Options::Option *item); + wstring getMessage(const Options::Option *item); + void load(); +private: + float readFloat(wstring string); +public: + void save(); + + bool isCloudsOn(); +}; diff --git a/Minecraft.Client/OptionsScreen.cpp b/Minecraft.Client/OptionsScreen.cpp new file mode 100644 index 0000000..b5c2f5e --- /dev/null +++ b/Minecraft.Client/OptionsScreen.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "OptionsScreen.h" +#include "SmallButton.h" +#include "SlideButton.h" +#include "Options.h" +#include "ControlsScreen.h" +#include "VideoSettingsScreen.h" +#include "..\Minecraft.World\net.minecraft.locale.h" + +OptionsScreen::OptionsScreen(Screen *lastScreen, Options *options) +{ + title = L"Options"; // 4J added + + this->lastScreen = lastScreen; + this->options = options; +} + +void OptionsScreen::init() +{ + Language *language = Language::getInstance(); + this->title = language->getElement(L"options.title"); + + int position = 0; + + // 4J - this was as static array but moving it into the function to remove any issues with static initialisation order + const Options::Option *items[5] = {Options::Option::MUSIC, Options::Option::SOUND, Options::Option::INVERT_MOUSE, Options::Option::SENSITIVITY, Options::Option::DIFFICULTY}; + for (int i = 0; i < 5; i++) + { + const Options::Option *item = items[i]; + if (!item->isProgress()) + { + buttons.push_back(new SmallButton(item->getId(), width / 2 - 155 + position % 2 * 160, height / 6 + 24 * (position >> 1), item, options->getMessage(item))); + } + else + { + buttons.push_back(new SlideButton(item->getId(), width / 2 - 155 + position % 2 * 160, height / 6 + 24 * (position >> 1), item, options->getMessage(item), options->getProgressValue(item))); + } + position++; + } + + buttons.push_back(new Button(VIDEO_BUTTON_ID, width / 2 - 100, height / 6 + 24 * 4 + 12, language->getElement(L"options.video"))); + buttons.push_back(new Button(CONTROLS_BUTTON_ID, width / 2 - 100, height / 6 + 24 * 5 + 12, language->getElement(L"options.controls"))); + buttons.push_back(new Button(200, width / 2 - 100, height / 6 + 24 * 7, language->getElement(L"gui.done"))); + +} + +void OptionsScreen::buttonClicked(Button *button) +{ + if (!button->active) return; + if (button->id < 100 && (dynamic_cast(button) != NULL)) + { + options->toggle(((SmallButton *) button)->getOption(), 1); + button->msg = options->getMessage(Options::Option::getItem(button->id)); + } + if (button->id == VIDEO_BUTTON_ID) + { + minecraft->options->save(); + minecraft->setScreen(new VideoSettingsScreen(this, options)); + } + if (button->id == CONTROLS_BUTTON_ID) + { + minecraft->options->save(); + minecraft->setScreen(new ControlsScreen(this, options)); + } + if (button->id == 200) + { + minecraft->options->save(); + minecraft->setScreen(lastScreen); + } +} + +void OptionsScreen::render(int xm, int ym, float a) +{ + renderBackground(); + drawCenteredString(font, title, width / 2, 20, 0xffffff); + Screen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/OptionsScreen.h b/Minecraft.Client/OptionsScreen.h new file mode 100644 index 0000000..69a2ffa --- /dev/null +++ b/Minecraft.Client/OptionsScreen.h @@ -0,0 +1,23 @@ +#pragma once +#include "Screen.h" +class Options; +using namespace std; + +class OptionsScreen : public Screen +{ +private: + static const int CONTROLS_BUTTON_ID = 100; + static const int VIDEO_BUTTON_ID = 101; + Screen *lastScreen; +protected: + wstring title; +private: + Options *options; +public: + OptionsScreen(Screen *lastScreen, Options *options); + virtual void init(); +protected: + virtual void buttonClicked(Button *button); +public: + virtual void render(int xm, int ym, float a); +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Assert/assert.h b/Minecraft.Client/Orbis/Assert/assert.h new file mode 100644 index 0000000..d8ec51c --- /dev/null +++ b/Minecraft.Client/Orbis/Assert/assert.h @@ -0,0 +1,20 @@ + +#pragma once +#include +#include + +#ifdef _CONTENT_PACKAGE +#define ORBIS_ASSERT(val) +#elif defined(_RELEASE_FOR_ART) +#define ORBIS_ASSERT(val) +#else +#define ORBIS_ASSERT(val) if(!(val)) { printf("------------------------------------------ \n"); \ + printf("Func : %s \n", __FUNCTION__); \ + printf("File : %s \n", __FILE__); \ + printf("Line : %d \n",__LINE__ ); \ + printf("assert(%s) failed!!!\n", #val); \ + printf("------------------------------------------ \n"); \ + SCE_BREAK(); } +#endif + +#define assert ORBIS_ASSERT diff --git a/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.cpp b/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.cpp new file mode 100644 index 0000000..b766219 --- /dev/null +++ b/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.cpp @@ -0,0 +1,1081 @@ +#include "stdafx.h" + +#include "OrbisLeaderboardManager.h" + +#include "base64.h" + +#include "..\Orbis_App.h" +#include "..\..\Common\Consoles_App.h" + +#include "Common\Network\Sony\SQRNetworkManager.h" + +#include "..\..\..\Minecraft.World\StringHelpers.h" + +#include + +#include +//#include + +#include "Orbis\OrbisExtras\ShutdownManager.h" + + +LeaderboardManager *LeaderboardManager::m_instance = new OrbisLeaderboardManager(); //Singleton instance of the LeaderboardManager + +OrbisLeaderboardManager::OrbisLeaderboardManager() +{ + m_eStatsState = eStatsState_Idle; + + m_titleContext = -1; + + m_myXUID = INVALID_XUID; + + m_scores = NULL; //m_stats = NULL; + + m_statsType = eStatsType_Kills; + m_difficulty = 0; + + m_requestId = 0; + + m_openSessions = 0; + + InitializeCriticalSection(&m_csViewsLock); + + m_running = false; + m_threadScoreboard = NULL; +} + +OrbisLeaderboardManager::~OrbisLeaderboardManager() +{ + m_running = false; + + // 4J-JEV: Wait for thread to stop and hope it doesn't take too long. + long long startShutdown = System::currentTimeMillis(); + while (m_threadScoreboard->isRunning()) + { + Sleep(1); + assert( (System::currentTimeMillis() - startShutdown) < 16 ); + } + + delete m_threadScoreboard; + + DeleteCriticalSection(&m_csViewsLock); +} + +int OrbisLeaderboardManager::scoreboardThreadEntry(LPVOID lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eLeaderboardThread); + OrbisLeaderboardManager *self = reinterpret_cast(lpParam); + + self->m_running = true; + app.DebugPrintf("[LeaderboardManager] Thread started.\n"); + + bool needsWriting = false; + do + { + if (self->m_openSessions > 0 || needsWriting) + { + self->scoreboardThreadInternal(); + } + + EnterCriticalSection(&self->m_csViewsLock); + needsWriting = self->m_views.size() > 0; + LeaveCriticalSection(&self->m_csViewsLock); + + // 4J Stu - We can't write while we aren't signed in to live + if (!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + needsWriting = false; + } + + if ( (!needsWriting) && (self->m_eStatsState != eStatsState_Getting) ) + { + Sleep(50); // 4J-JEV: When we're not reading or writing. + } + + } while ( (self->m_running || self->m_eStatsState == eStatsState_Getting || needsWriting) + && ShutdownManager::ShouldRun(ShutdownManager::eLeaderboardThread) + ); + + // 4J-JEV, moved this here so setScore can finish up. + sceNpScoreDestroyTitleCtx(self->m_titleContext); + // TODO sceNpScoreTerm(); + app.DebugPrintf("[LeaderboardManager] Thread closed.\n"); + ShutdownManager::HasFinished(ShutdownManager::eLeaderboardThread); + return 0; +} + +void OrbisLeaderboardManager::scoreboardThreadInternal() +{ + // 4J-JEV: Just initialise the context the once now. + if (m_titleContext == -1) + { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedInLive(primaryPad)) return; + + SceNpId npId; + ProfileManager.GetSceNpId(primaryPad,&npId); + + int ret = sceNpScoreCreateNpTitleCtx(primaryPad, &npId); + + if (ret < 0) return; + else m_titleContext = ret; + } + else assert( m_titleContext > 0 ); //Paranoia + + + switch (m_eStatsState) + { + case eStatsState_Getting: + // Player starts using async multiplayer feature + // 4J-PB - Fix for SCEA FQA #4 - TRC R4064 - Incorrect usage of AsyncMultiplay + // Note 1: + // The following NP call should be reserved for asynchronous multiplayer modes that require PS Plus to be accessed. + // + // Note 2: + // The message is not displayed with a user without PlayStationPlus subscription and they are able to access the Leaderboards. + + // NotifyAsyncPlusFeature(); + + switch(m_eFilterMode) + { + case eFM_MyScore: + case eFM_Friends: + getScoreByIds(); + break; + case eFM_TopRank: + getScoreByRange(); + break; + } + break; + + case eStatsState_Canceled: + case eStatsState_Failed: + case eStatsState_Ready: + case eStatsState_Idle: + + // 4J-JEV: Moved this here, I don't want reading and + // writing going on at the same time. + // -- + // 4J-JEV: Writing no longer changes the manager state, + // we'll manage the write queue seperately. + + EnterCriticalSection(&m_csViewsLock); + bool hasWork = !m_views.empty(); + LeaveCriticalSection(&m_csViewsLock); + + if (hasWork) + { + setScore(); + } + + break; + } +} + +bool OrbisLeaderboardManager::getScoreByIds() +{ + if (m_eStatsState == eStatsState_Canceled) return false; + + // ---------------------------- + SceRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + SceNpId *npIds = NULL; + + + int ret; + uint32_t num = 0; + + SceNpScorePlayerRankData *ptr; + SceNpScoreComment *comments; + // ---------------------------- + + // Check for invalid LManager state. + assert( m_eFilterMode == eFM_Friends + || m_eFilterMode == eFM_MyScore); + + SceNpId myNpId; + // 4J-PB - should it be user 0? + if(!ProfileManager.IsSignedInLive(0)) + { + app.DebugPrintf("[LeaderboardManager] OpenSession() fail: User isn't signed in to PSN\n"); + return false; + } + ProfileManager.GetSceNpId(0,&myNpId); + + // Get queried users. + if (m_eFilterMode == eFM_Friends) + { + + sce::Toolkit::NP::Utilities::Future s_friendList; + sce::Toolkit::NP::FriendInfoRequest request; + + memset(&request, 0, sizeof(request)); + request.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + request.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_ALL; + ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&s_friendList, &request, false); + + if(ret != SCE_TOOLKIT_NP_SUCCESS) + { + // Error handling + if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManager] getFriendslist fail\n"); + return false; + } + else if (s_friendList.hasResult()) + { + // 4J-JEV: Friends list doesn't include player, leave space for them. + num = s_friendList.get()->size() + 1; + + npIds = new SceNpId[num]; + + int i = 0; + + sce::Toolkit::NP::FriendsList::const_iterator itr; + for (itr = s_friendList.get()->begin(); itr != s_friendList.get()->end(); itr++) + { + npIds[i] = itr->npid; + i++; + } + + npIds[num-1] = myNpId; // 4J-JEV: Append player to end of query. + } + else + { + // 4J-JEV: Something terrible must have happend, + // 'getFriendslist' was supposed to be a synchronous operation. + __debugbreak(); + + // 4J-JEV: We can at least fall-back to just the players score. + num = 1; + npIds = new SceNpId[1]; + npIds[0] = myNpId; + } + } + else if (m_eFilterMode == eFM_MyScore) + { + num = 1; + npIds = new SceNpId[1]; + npIds[0] = myNpId; + } + + ptr = new SceNpScorePlayerRankData[num]; + comments = new SceNpScoreComment[num]; + + ZeroMemory(ptr, sizeof(SceNpScorePlayerRankData) * num); + ZeroMemory(comments, sizeof(SceNpScoreComment) * num); + + /* app.DebugPrintf("sceNpScoreGetRankingByNpId(\n\t transaction=%i,\n\t boardID=0,\n\t npId=%i,\n\t friendCount*sizeof(SceNpId)=%i*%i=%i,\ + rankData=%i,\n\t friendCount*sizeof(SceNpScorePlayerRankData)=%i,\n\t NULL, 0, NULL, 0,\n\t friendCount=%i,\n...\n", + transaction, npId, friendCount, sizeof(SceNpId), friendCount*sizeof(SceNpId), + rankData, friendCount*sizeof(SceNpScorePlayerRankData), friendCount + ); */ + + int boardId = getBoardId(m_difficulty, m_statsType); + + for (int batch=0; batchrankData.scoreValue + ); + + // Sort scores + std::sort(m_scores, m_scores + m_readCount, SortByRank); + + delete [] ptr; + delete [] comments; + delete [] npIds; + + m_eStatsState = eStatsState_Ready; + return true; + + // Error. +error3: + if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109 + sceNpScoreDestroyTransactionCtx(m_requestId); + m_requestId = 0; + delete [] ptr; + delete [] comments; +error2: + if (npIds != NULL) delete [] npIds; +error1: + if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManger] getScoreByIds() FAILED, ret=0x%X\n", ret); + return false; +} + +bool OrbisLeaderboardManager::getScoreByRange() +{ + SceRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + unsigned int num = m_readCount; + SceNpScoreRankData *ptr; + SceNpScoreComment *comments; + + assert(m_eFilterMode == eFM_TopRank); + + int ret = sceNpScoreCreateTransactionCtx(m_titleContext); + if (m_eStatsState == eStatsState_Canceled) + { + // Cancel operation has been called, abort. + app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - m_eStatsState == eStatsState_Canceled.\n"); + sceNpScoreDestroyTransactionCtx(ret); + return false; + } + else if (ret < 0) + { + // Error occurred creating a transacion, abort. + m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - createTransaction failed, ret=0x%X\n", ret); + return false; + } + else + { + // Transaction created successfully, continue. + m_requestId = ret; + } + + ptr = new SceNpScoreRankData[num]; + comments = new SceNpScoreComment[num]; + + int boardId = getBoardId(m_difficulty, m_statsType); + ret = sceNpScoreGetRankingByRange( + m_requestId, + boardId, // BoardId + + m_startIndex, + + ptr, sizeof(SceNpScoreRankData) * num, //OUT: Rank Data + + comments, sizeof(SceNpScoreComment) * num, //OUT: Comment Data + + NULL, 0, // GameData. + + num, + + &last_sort_date, + &m_maxRank, // 'Total number of players registered in the target scoreboard.' + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) + { + ret = sceNpScoreDestroyTransactionCtx(m_requestId); + app.DebugPrintf("[LeaderboardManager] getScoreByRange(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret); + + delete [] ptr; + delete [] comments; + + return false; + } + else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND) + { + ret = sceNpScoreDestroyTransactionCtx(m_requestId); + app.DebugPrintf("[LeaderboardManager] getScoreByRange(): Game ranking not found."); + + delete [] ptr; + delete [] comments; + + m_scores = NULL; + m_readCount = 0; + + m_eStatsState = eStatsState_Ready; + return false; + } + else if (ret<0) goto error2; + else + { + app.DebugPrintf("[LeaderboardManager] getScoreByRange(), success, 1stScore=%i.\n", ptr->scoreValue); + } + + // Return. + sceNpScoreDestroyTransactionCtx(m_requestId); + m_requestId = 0; + + //m_stats = ptr; //Maybe: addPadding(num,ptr); + + if (m_scores != NULL) delete [] m_scores; + m_readCount = ret; + m_scores = new ReadScore[m_readCount]; + for (int i=0; i 0) + ret = eStatsReturn_Success; + + if (m_readListener != NULL) + { + app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _), m_readCount=%i.\n", ret, m_maxRank, m_readCount); + m_readListener->OnStatsReadComplete(ret, m_maxRank, view); + } + + m_eStatsState = eStatsState_Idle; + + delete [] m_scores; + m_scores = NULL; + } + break; + + case eStatsState_Failed: + { + view.m_numQueries = 0; + view.m_queries = NULL; + + if ( m_readListener != NULL ) + m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view); + + m_eStatsState = eStatsState_Idle; + } + break; + + case eStatsState_Canceled: + { + m_eStatsState = eStatsState_Idle; + } + break; + + default: // Getting or Idle. + break; + } +} + +bool OrbisLeaderboardManager::OpenSession() +{ + if (m_openSessions == 0) + { + if (m_threadScoreboard == NULL) + { + m_threadScoreboard = new C4JThread(&scoreboardThreadEntry, this, "4JScoreboard"); + m_threadScoreboard->SetProcessor(CPU_CORE_LEADERBOARDS); + m_threadScoreboard->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); + m_threadScoreboard->Run(); + } + + app.DebugPrintf("[LeaderboardManager] OpenSession(): Starting sceNpScore utility.\n"); + } + else + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1); + } + + m_openSessions++; + return true; +} + +void OrbisLeaderboardManager::CloseSession() +{ + m_openSessions--; + + if (m_openSessions == 0) app.DebugPrintf("[LeaderboardManager] CloseSession(): Quitting sceNpScore utility.\n"); + else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions); +} + +void OrbisLeaderboardManager::DeleteSession() {} + +bool OrbisLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views) +{ + // Need to cancel read/write operation first. + //if (m_eStatsState != eStatsState_Idle) return false; + + // Write relevant parameters. + //RegisterScore *regScore = reinterpret_cast(views); + + EnterCriticalSection(&m_csViewsLock); + for (int i=0; iutf8Comment; + + for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++) + { + int sByte = (i*5) / 8; + int eByte = (5+(i*5)) / 8; + int dIndex = (i*5) % 8; + + unsigned char fivebits = 0; + + fivebits = *(bytes+sByte) << dIndex; + + if (eByte != sByte) + fivebits = fivebits | *(bytes+eByte) >> (8-dIndex); + + fivebits = (fivebits>>3) & 0x1F; + + if (fivebits < 10) // 0 - 9 + chars[i] = '0' + fivebits; + else if (fivebits < 32) // A - V + chars[i] = 'A' + (fivebits-10); + else + assert(false); + } + + toSymbols(out->utf8Comment); +} + +void OrbisLeaderboardManager::fromBase32(void *out, SceNpScoreComment *in) +{ + PBYTE bytes = (PBYTE) out; + ZeroMemory(bytes, RECORD_SIZE); + + fromSymbols(in->utf8Comment); + + char ch[2] = { 0, 0 }; + for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++) + { + ch[0] = in->utf8Comment[i]; + unsigned char fivebits = strtol(ch, NULL, 32) << 3; + + int sByte = (i*5) / 8; + int eByte = (5+(i*5)) / 8; + int dIndex = (i*5) % 8; + + *(bytes + sByte) = *(bytes+sByte) | (fivebits >> dIndex); + + if (eByte != sByte) + *(bytes + eByte) = fivebits << (8-dIndex); + } +} + +char symbBase32[32] = { + ' ', '!','\"', '#', '$', '%', '&','\'', '(', ')', + '*', '+', '`', '-', '.', '/', ':', ';', '<', '=', + '>', '?', '[','\\', ']', '^', '_', '{', '|', '}', + '~', '@' +}; + +char charBase32[32] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V' +}; + +void OrbisLeaderboardManager::toSymbols(char *str) +{ + for (int i = 0; i < 63; i++) + { + for (int j=0; j < 32; j++) + { + if (str[i]==charBase32[j]) + str[i] =symbBase32[j]; + } + } +} + +void OrbisLeaderboardManager::fromSymbols(char *str) +{ + for (int i = 0; i < 63; i++) + { + for (int j=0; j < 32; j++) + { + if (str[i]==symbBase32[j]) + str[i] =charBase32[j]; + } + } +} + +bool OrbisLeaderboardManager::test_string(string testing) +{ +#ifndef _CONTENT_PACKAGE + static SceNpScoreComment comment; + ZeroMemory(&comment, sizeof(SceNpScoreComment)); + memcpy(&comment, testing.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN); + + int ctx = sceNpScoreCreateTransactionCtx(m_titleContext); + if (ctx<0) return false; + + int ret = sceNpScoreCensorComment(ctx, (const char *) &comment, NULL); + + if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED) + { + app.DebugPrintf("\n[TEST_STRING]: REJECTED "); + } + else if (ret < 0) + { + sceNpScoreDestroyTransactionCtx(ctx); + return false; + } + else + { + app.DebugPrintf("\n[TEST_STRING]: permitted "); + } + + app.DebugPrintf("'%s'\n", comment.utf8Comment); + sceNpScoreDestroyTransactionCtx(ctx); + return true; +#else + return true; +#endif +} + +void OrbisLeaderboardManager::initReadScoreStruct(ReadScore &out, SceNpScoreRankData &rankData) +{ + ZeroMemory(&out, sizeof(ReadScore)); + + // Init rank and onlineID + out.m_uid.setOnlineID( rankData.npId.handle, true ); + out.m_rank = rankData.rank; + + // Convert to wstring and copy name. + wstring wstrName = convStringToWstring( string(rankData.npId.handle.data) ).c_str(); + //memcpy(&out.m_name, wstrName.c_str(), XUSER_NAME_SIZE); + out.m_name=wstrName; +} + +void OrbisLeaderboardManager::fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment) +{ + StatsData statsData; + fromBase32( (void *) &statsData, &comment ); + + switch (statsData.m_statsType) + { + case eStatsType_Farming: + out.m_statsSize = 6; + out.m_statsData[0] = statsData.m_farming.m_eggs; + out.m_statsData[1] = statsData.m_farming.m_wheat; + out.m_statsData[2] = statsData.m_farming.m_mushroom; + out.m_statsData[3] = statsData.m_farming.m_sugarcane; + out.m_statsData[4] = statsData.m_farming.m_milk; + out.m_statsData[5] = statsData.m_farming.m_pumpkin; + break; + case eStatsType_Mining: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_mining.m_dirt; + out.m_statsData[1] = statsData.m_mining.m_cobblestone; + out.m_statsData[2] = statsData.m_mining.m_sand; + out.m_statsData[3] = statsData.m_mining.m_stone; + out.m_statsData[4] = statsData.m_mining.m_gravel; + out.m_statsData[5] = statsData.m_mining.m_clay; + out.m_statsData[6] = statsData.m_mining.m_obsidian; + break; + case eStatsType_Kills: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_kills.m_zombie; + out.m_statsData[1] = statsData.m_kills.m_skeleton; + out.m_statsData[2] = statsData.m_kills.m_creeper; + out.m_statsData[3] = statsData.m_kills.m_spider; + out.m_statsData[4] = statsData.m_kills.m_spiderJockey; + out.m_statsData[5] = statsData.m_kills.m_zombiePigman; + out.m_statsData[6] = statsData.m_kills.m_slime; + break; + case eStatsType_Travelling: + out.m_statsSize = 4; + out.m_statsData[0] = statsData.m_travelling.m_walked; + out.m_statsData[1] = statsData.m_travelling.m_fallen; + out.m_statsData[2] = statsData.m_travelling.m_minecart; + out.m_statsData[3] = statsData.m_travelling.m_boat; + break; + } +} + +bool OrbisLeaderboardManager::SortByRank(const ReadScore &lhs, const ReadScore &rhs) +{ + return lhs.m_rank < rhs.m_rank; +} + +// Notify plus feature use +/*void OrbisLeaderboardManager::NotifyAsyncPlusFeature() +{ + SceNpNotifyPlusFeatureParameter param = SceNpNotifyPlusFeatureParameter(); + param.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + param.size = sizeof(SceNpNotifyPlusFeatureParameter); + param.features = SCE_NP_PLUS_FEATURE_ASYNC_MULTIPLAY; + ZeroMemory(param.padding, sizeof(char) * 4); + ZeroMemory(param.reserved, sizeof(uint8_t) * 32); + + int err = sceNpNotifyPlusFeature(¶m); + if (err != SCE_OK) + { + app.DebugPrintf("OrbisLeaderboardManager::NotifyAsyncPlusFeature: sceNpNotifyPlusFeature failed (0x%x)\n", err); + assert(0); + } +}*/ \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.h b/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.h new file mode 100644 index 0000000..c2243a9 --- /dev/null +++ b/Minecraft.Client/Orbis/Leaderboards/OrbisLeaderboardManager.h @@ -0,0 +1,109 @@ +#pragma once + +#include "Common\Leaderboards\LeaderboardManager.h" + +class OrbisLeaderboardManager : public LeaderboardManager +{ +protected: + enum EStatsState + { + eStatsState_Idle, + eStatsState_Getting, + eStatsState_Failed, + eStatsState_Ready, + eStatsState_Canceled, + //eStatsState_Writing, + eStatsState_Max + }; + +public: + OrbisLeaderboardManager(); + virtual ~OrbisLeaderboardManager(); + +private: + unsigned short m_openSessions; + + C4JThread *m_threadScoreboard; + bool m_running; + + int m_titleContext; + int32_t m_requestId; + + //SceNpId m_myNpId; + + static int scoreboardThreadEntry(LPVOID lpParam); + void scoreboardThreadInternal(); + + bool getScoreByIds(); + bool getScoreByRange(); + + bool setScore(); + queue m_views; + + CRITICAL_SECTION m_csViewsLock; + + EStatsState m_eStatsState; //State of the stats read + // EFilterMode m_eFilterMode; + + ReadScore *m_scores; + unsigned int m_maxRank; + //SceNpScoreRankData *m_stats; + +public: + virtual void Tick();// {} + + //Open a session + virtual bool OpenSession();// { return true; } + + //Close a session + virtual void CloseSession();// {} + + //Delete a session + virtual void DeleteSession();// {} + + //Write the given stats + //This is called synchronously and will not free any memory allocated for views when it is done + + virtual bool WriteStats(unsigned int viewCount, ViewIn views);// { return false; } + + virtual bool ReadStats_Friends(LeaderboardReadListener *callback, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount);// { return false; } + virtual bool ReadStats_MyScore(LeaderboardReadListener *callback, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount);// { return false; } + virtual bool ReadStats_TopRank(LeaderboardReadListener *callback, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount);// { return false; } + + //Perform a flush of the stats + virtual void FlushStats();// {} + + //Cancel the current operation + virtual void CancelOperation();// {} + + //Is the leaderboard manager idle. + virtual bool isIdle();// { return true; } + + +private: + int getBoardId(int difficulty, EStatsType); + + //LeaderboardManager::ReadScore *filterJustScorers(unsigned int &num, LeaderboardManager::ReadScore *friendsData); + + SceNpScorePlayerRankData *addPadding(unsigned int num, SceNpScoreRankData *rankData); + + void convertToOutput(unsigned int &num, ReadScore *out, SceNpScorePlayerRankData *rankData, SceNpScoreComment *comm); + + void toBinary(void *out, SceNpScoreComment *in); + void fromBinary(SceNpScoreComment **out, void *in); + + void toBase32(SceNpScoreComment *out, void *in); + void fromBase32(void *out, SceNpScoreComment *in); + + void toSymbols(char *); + void fromSymbols(char *); + + bool test_string(string); + + void initReadScoreStruct(ReadScore &out, SceNpScoreRankData &); + void fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment); + + static bool SortByRank(const ReadScore &lhs, const ReadScore &rhs); + + //void NotifyAsyncPlusFeature(); +}; diff --git a/Minecraft.Client/Orbis/Leaderboards/base64.cpp b/Minecraft.Client/Orbis/Leaderboards/base64.cpp new file mode 100644 index 0000000..19106cc --- /dev/null +++ b/Minecraft.Client/Orbis/Leaderboards/base64.cpp @@ -0,0 +1,131 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 Ren Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "stdafx.h" + +#include "base64.h" +#include + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +// 4J ADDED, +std::string base64_encode(std::string str) +{ + return base64_encode( reinterpret_cast(str.c_str()), str.length() ); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(int ii = 0; (ii <4) ; ii++) + ret += base64_chars[char_array_4[ii]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Leaderboards/base64.h b/Minecraft.Client/Orbis/Leaderboards/base64.h new file mode 100644 index 0000000..7f6a1e4 --- /dev/null +++ b/Minecraft.Client/Orbis/Leaderboards/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +std::string base64_encode(std::string str); +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); \ No newline at end of file diff --git a/Minecraft.Client/Orbis/MinecraftPronunciation/MinecraftPronunciation.xml b/Minecraft.Client/Orbis/MinecraftPronunciation/MinecraftPronunciation.xml new file mode 100644 index 0000000..daaacf8 --- /dev/null +++ b/Minecraft.Client/Orbis/MinecraftPronunciation/MinecraftPronunciation.xml @@ -0,0 +1,46 @@ + + + + Minecraft + Japanese + + + m i n k r a f t + true + false + + + m i n e k r a f t + true + false + + + + + Minecraft + EnglishUS + + + m ay n k r ae f t + false + false + + + m ay n k r ax f t + false + false + + + + + Minecraft + EnglishUK + + + m ih n k r aa f t + false + false + + + + \ No newline at end of file diff --git a/Minecraft.Client/Orbis/MinecraftPronunciation/pronunciation.xml b/Minecraft.Client/Orbis/MinecraftPronunciation/pronunciation.xml new file mode 100644 index 0000000..2127b80 --- /dev/null +++ b/Minecraft.Client/Orbis/MinecraftPronunciation/pronunciation.xml @@ -0,0 +1,13 @@ + + + + + Minecraft + m i n k r a f t + + + Minecraft + m i n e k r a f t + + + \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Minecraft_Macros.h b/Minecraft.Client/Orbis/Minecraft_Macros.h new file mode 100644 index 0000000..c292cb5 --- /dev/null +++ b/Minecraft.Client/Orbis/Minecraft_Macros.h @@ -0,0 +1,41 @@ + +#pragma once + +// 3 bit user index +// 5 bits alpha +// 1 bit decoration +// 11 bits unused // was aux val but needed 15 bits for potions so moved to item bitmask +// 6 bits count +// 6 bits scale + +// uiCount is up to 64, but can't ever be 0, so to make it 6 bits, subtract one from the packing, and add one on the unpacking +#define MAKE_SLOTDISPLAY_DATA_BITMASK(uiUserIndex,uiAlpha,bDecorations,uiCount,uiScale,uiPopTime) ((((uiUserIndex&0x7)<<29) | (uiAlpha&0x1F)<<24) | (bDecorations?0x800000:0) | ((uiPopTime&0x7)<<20) | ((uiCount-1)<<6) | (uiScale&0x3F)) + +#define GET_SLOTDISPLAY_USERINDEX_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>29)&0x7) +#define GET_SLOTDISPLAY_ALPHA_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>24)&0x1F) +#define GET_SLOTDISPLAY_DECORATIONS_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x800000)?true:false) +//#define GET_SLOTDISPLAY_AUXVAL_FROM_DATA_BITMASK(uiBitmask) ((((unsigned long)uiBitmask)>>12)&0x7FF) +#define GET_SLOTDISPLAY_COUNT_FROM_DATA_BITMASK(uiBitmask) (((((unsigned int)uiBitmask)>>6)&0x3F)+1) +#define GET_SLOTDISPLAY_SCALE_FROM_DATA_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0x3F) +#define GET_SLOTDISPLAY_POPTIME_FROM_DATA_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>20)&0x7) + +// 16 bits for id (either item id or xzp icon id) +// 15 bits for aux value +// 1 bit for foil +#define MAKE_SLOTDISPLAY_ITEM_BITMASK(uiId,uiAuxValue,bFoil) ( (uiId & 0xFFFF) | ((uiAuxValue & 0x7FFF) << 16) | (bFoil?0x80000000:0) ) + +#define GET_SLOTDISPLAY_ID_FROM_ITEM_BITMASK(uiBitmask) (((unsigned int)uiBitmask)&0xFFFF) +#define GET_SLOTDISPLAY_AUXVAL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)>>16) & 0x7FFF) +#define GET_SLOTDISPLAY_FOIL_FROM_ITEM_BITMASK(uiBitmask) ((((unsigned int)uiBitmask)&0x80000000)?true:false) + + +// For encoding the players skin selection in their profile +// bDlcSkin = false is a players skin, bDlcSkin = true is a DLC skin +#define MAKE_SKIN_BITMASK(bDlcSkin, dwSkinId) ( (bDlcSkin?0x80000000:0) | (dwSkinId & 0x7FFFFFFF) ) +#define IS_SKIN_ID_IN_RANGE(dwSkinId) (dwSkinId <= 0x7FFFFFFF) + +#define GET_DLC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFFF) +#define GET_UGC_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x7FFFFFE0) +#define GET_DEFAULT_SKIN_ID_FROM_BITMASK(uiBitmask) (((DWORD)uiBitmask)&0x0000001F) +#define GET_IS_DLC_SKIN_FROM_BITMASK(uiBitmask) ((((DWORD)uiBitmask)&0x80000000)?true:false) + diff --git a/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp new file mode 100644 index 0000000..d1c9cf1 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.cpp @@ -0,0 +1,490 @@ +#include "stdafx.h" + +#include "Orbis_NPToolkit.h" +#include "Orbis\ps4__np_conf.h" +#include "Orbis/Network/SonyCommerce_Orbis.h" + +// #define NP_TITLE_ID "CUSA00265_00" +// #define NP_TITLE_SECRET_HEX "c37e30fa1f7fd29e3534834d62781143ae29aa7b51d02320e7aa0b45116ad600e4d309e8431bc37977d98b8db480e721876e7d736e11fd906778c0033bbb6370903477b1dc1e65106afc62007a5feee3158844d721b88c3f4bff2e56417b6910cedfdec78b130d2e0dd35a35a9e2ae31d5889f9398c1d62b52a3630bb03faa5b" +// #define CLIENT_ID_FOR_SAMPLE "c8c483e7-f0b4-420b-877b-307fcb4c3cdc" + +//#define _USE_STANDARD_ALLOC + +// Singleton +OrbisNPToolkit NPToolkit; +sce::Toolkit::NP::Utilities::Future< sce::Toolkit::NP::NpSessionInformation > OrbisNPToolkit::sm_createJoinFuture; +sce::Toolkit::NP::NpSessionInformation OrbisNPToolkit::m_currentSessionInfo; +sce::Toolkit::NP::Utilities::Future OrbisNPToolkit::m_messageData; + + +void OrbisNPToolkit::presenceCallback( const sce::Toolkit::NP::Event& event ) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::presenceSet: + app.DebugPrintf("presenceSet Successfully\n"); + break; + case sce::Toolkit::NP::Event::presenceSetFailed: + app.DebugPrintf("presenceSetFailed event received = 0x%x\n", event.returnCode); + SQRNetworkManager_Orbis::SetPresenceFailedCallback(); + break; + default: + break; + } +} + +void OrbisNPToolkit::coreCallback( const sce::Toolkit::NP::Event& event ) +{ + switch (event.event) + { + case sce::Toolkit::NP::Event::enetDown: + app.DebugPrintf("Online: Received core callback: Network down \n"); + ProfileManager.SetNetworkStatus(false); + break; + case sce::Toolkit::NP::Event::enetUp: + app.DebugPrintf("Online: Received core callback: Network up \n"); + ProfileManager.SetNetworkStatus(true); + break; + case sce::Toolkit::NP::Event::loggedIn: + app.DebugPrintf("Online: Received core callback: PSN sign in \n"); + assert(event.userInformation.state == SCE_NP_STATE_SIGNED_IN); + ProfileManager.SignedInPSNStateCallback(event.userInformation.userId, event.userInformation.state, &event.userInformation.npId); + break; + case sce::Toolkit::NP::Event::loggedOut: + app.DebugPrintf("Online: Received core callback: PSN sign out \n"); + assert(event.userInformation.state == SCE_NP_STATE_SIGNED_OUT); + ProfileManager.SignedInPSNStateCallback(event.userInformation.userId, event.userInformation.state, &event.userInformation.npId); + break; + default: + app.DebugPrintf("Online: Received core callback: event Num: %d \n", event.event); + break; + } +} + +void OrbisNPToolkit::sceNpToolkitCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.service) + { + case sce::Toolkit::NP::ServiceType::core: + coreCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::netInfo: +// Menu::NetInfo::sceNpToolkitCallback(event); +// break; + case sce::Toolkit::NP::ServiceType::sessions: + sessionsCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::tss: +// Menu::Tss::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::ranking: +// Menu::Ranking::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::tus: +// Menu::Tus::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::profile: +// Menu::Profile::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::friends: +// Menu::Friends::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::auth: +// Menu::Auth::sceNpToolkitCallback(event); +// break; + case sce::Toolkit::NP::ServiceType::trophy: + ProfileManager.trophySystemCallback(event); + break; + case sce::Toolkit::NP::ServiceType::messaging: + messagingCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::inGameMessage: +// Menu::Messaging::sceNpToolkitCallback(event); +// break; + + case sce::Toolkit::NP::ServiceType::commerce: + SonyCommerce_Orbis::commerce2Handler(event); + break; + case sce::Toolkit::NP::ServiceType::presence: + presenceCallback(event); + break; +// case sce::Toolkit::NP::ServiceType::wordFilter: +// Menu::WordFilter::sceNpToolkitCallback(event); +// break; +// case sce::Toolkit::NP::ServiceType::sns: +// Menu::Sns::sceNpToolkitCallback(event); +// break; + + case sce::Toolkit::NP::ServiceType::gameCustomData: + gameCustomDataCallback(event); + default: + break; + } +} + + + +void OrbisNPToolkit::sessionsCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::npSessionCreateResult: ///< An event generated when the %Np session creation process has been completed. + app.DebugPrintf("npSessionCreateResult"); + if(sm_createJoinFuture.hasResult()) + { + app.DebugPrintf("Session Created Successfully\n"); + m_currentSessionInfo = *sm_createJoinFuture.get(); + } + else + { + app.DebugPrintf("Session Creation Failed 0x%x\n",sm_createJoinFuture.getError()); + } + sm_createJoinFuture.reset(); + break; + case sce::Toolkit::NP::Event::npSessionJoinResult: ///< An event generated when the join %Np session process has been completed. + app.DebugPrintf("npSessionJoinResult"); + if(sm_createJoinFuture.hasResult()) + { + app.DebugPrintf("Session joined successfully\n"); + m_currentSessionInfo = *sm_createJoinFuture.get(); + } + else + { + app.DebugPrintf("Session join Failed 0x%x\n",sm_createJoinFuture.getError()); + } + sm_createJoinFuture.reset(); + break; + case sce::Toolkit::NP::Event::npSessionError: ///< An event generated when there was error performing the current %Np session process. + app.DebugPrintf("npSessionError"); + break; + case sce::Toolkit::NP::Event::npSessionLeaveResult: ///< An event generated when the user has left the current %Np session. + app.DebugPrintf("npSessionLeaveResult"); + break; + case sce::Toolkit::NP::Event::npSessionModified: ///< An event generated when the %Np session has been modified. + app.DebugPrintf("npSessionModified"); + break; + case sce::Toolkit::NP::Event::npSessionUpdateResult: ///< An event generated when the %Np session has been updated. + app.DebugPrintf("npSessionUpdateResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetInfoResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionGetInfoResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetInfoListResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionGetInfoListResult"); + break; + case sce::Toolkit::NP::Event::npSessionGetSessionDataResult: ///< An event generated when the %Np session data has been retrieved. + app.DebugPrintf("npSessionGetSessionDataResult"); + break; + case sce::Toolkit::NP::Event::npSessionSearchResult: ///< An event generated when the %Np session search request has been completed. + app.DebugPrintf("npSessionSearchResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteNotification: ///< An event generated when the %Np session push notification is received. + app.DebugPrintf("npSessionInviteNotification"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetInfoResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionInviteGetInfoResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetInfoListResult: ///< An event generated when the %Np session info has been retrieved. + app.DebugPrintf("npSessionInviteGetInfoListResult"); + break; + case sce::Toolkit::NP::Event::npSessionInviteGetDataResult: ///< An event generated when the %Np session data has been retrieved. + app.DebugPrintf("npSessionInviteGetDataResult"); + break; + default: + assert(0); + break; + } + +} + +void OrbisNPToolkit::gameCustomDataCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + + case sce::Toolkit::NP::Event::gameCustomDataItemListResult: + app.DebugPrintf("gameCustomDataItemListResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataGameDataResult: + app.DebugPrintf("gameCustomDataGameDataResult"); + if(m_messageData.hasResult()) + { + SQRNetworkManager_Orbis::GetInviteDataAndProcess(m_messageData.get()); + } + else + { + app.DebugPrintf("gameCustomDataMessageResult error 0x%08x\n", m_messageData.getError()); + + UINT uiIDA[1] = { IDS_OK }; + + switch(m_messageData.getError()) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + app.ShowPatchAvailableError(); + break; + case SCE_NP_ERROR_AGE_RESTRICTION: + default: + ui.RequestMessageBox(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1); + break; + } + } + break; + case sce::Toolkit::NP::Event::gameCustomDataMessageResult: + app.DebugPrintf("gameCustomDataMessageResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataSetUseFlagResult: + app.DebugPrintf("gameCustomDataSetUseFlagResult"); + break; + case sce::Toolkit::NP::Event::gameCustomDataGameThumbnailResult: + app.DebugPrintf("gameCustomDataGameThumbnailResult"); + break; + case sce::Toolkit::NP::Event::messageError: + app.DebugPrintf("messageError : 0x%08x\n", event.returnCode); + assert(0); + break; + default: + assert(0); + break; + } +} + +void OrbisNPToolkit::messagingCallback( const sce::Toolkit::NP::Event& event) +{ + switch(event.event) + { + case sce::Toolkit::NP::Event::messageSent: ///< An event generated when a message has been sent. + app.DebugPrintf("sce::Toolkit::NP::Event::messageSent\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + case sce::Toolkit::NP::Event::messageError: ///< An event generated when a message failed to be received or sent. + app.DebugPrintf("sce::Toolkit::NP::Event::messageError\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + case sce::Toolkit::NP::Event::messageDialogTerminated: ///< An event generated when a message dialog box is terminated. + app.DebugPrintf("sce::Toolkit::NP::Event::messageDialogTerminated\n"); + SQRNetworkManager_Orbis::s_bInviteDialogRunning = false; + break; + //case sce::Toolkit::NP::Event::messageRetrieved: ///< An event generated when a message attachment has been retrieved. + //case sce::Toolkit::NP::Event::messageInGameDataReceived: ///< An event generated when in-game data is received. + //case sce::Toolkit::NP::Event::messageInGameDataRetrievalDone: ///< An event generated when in-game data retrieval is complete. + + // case sce::Toolkit::NP::Event::messageAttachmentReceived: ///< An event generated when a message with a data attachment has been received. + // case sce::Toolkit::NP::Event::messageAttachmentOpened: ///< An event generated when a message with a data attachment has been opened (and the sysutil GUI is closed). + // case sce::Toolkit::NP::Event::messageInviteReceived: ///< An event generated when a message with an invite has been received. + // case sce::Toolkit::NP::Event::messageInviteAccepted: ///< An event generated when a message with an invite has been accepted via ToolkitNp (and the sysutil GUI is closed). + } +} + +static uint8_t hexCharToUint(char ch) +{ + uint8_t val = 0; + + if ( isdigit(ch) ){ + val = (ch - '0'); + } + else if ( isupper(ch) ){ + val = (ch - 'A' + 10); + } + else{ + val = (ch - 'a' + 10); + } + + return val; +} + +void hexStrToBin( + const char *pHexStr, + uint8_t *pBinBuf, + size_t binBufSize + ) +{ + uint8_t val = 0; + int hexStrLen = strlen(pHexStr); + + int binOffset = 0; + for (int i = 0; i < hexStrLen; i++) { + val |= hexCharToUint(*(pHexStr + i)); + if (i % 2 == 0) { + val <<= 4; + } + else { + if (pBinBuf != NULL && binOffset < binBufSize) { + memcpy(pBinBuf + binOffset, &val, 1); + val = 0; + } + binOffset++; + } + } + + if (val != 0 && pBinBuf != NULL && binOffset < binBufSize) { + memcpy(pBinBuf + binOffset, &val, 1); + } + + return; +} + +void OrbisNPToolkit::init() +{ +// MenuApp menuApp; + + sce::Toolkit::NP::NpTitleId nptTitleId; + nptTitleId.setTitleSecret(*SQRNetworkManager_Orbis::GetSceNpTitleId(), *SQRNetworkManager_Orbis::GetSceNpTitleSecret()); + sce::Toolkit::NP::CommunicationId commsIds(s_npCommunicationId, s_npCommunicationPassphrase, s_npCommunicationSignature); + sce::Toolkit::NP::Parameters params(sceNpToolkitCallback,nptTitleId); + + + int ret = sce::Toolkit::NP::Interface::init(params); + if (ret != SCE_OK) + { + app.DebugPrintf("Failed to initialize NP Toolkit Library : 0x%x\n", ret); + assert(0); + } + + + ret = sce::Toolkit::NP::Interface::registerNpCommsId(commsIds, sce::Toolkit::NP::matching); + if (ret < 0) + { + app.DebugPrintf("Failed to register TSS Comms ID : 0x%x\n", ret); + assert(0); + } + + // Get network status and inform library + sce::Toolkit::NP::Utilities::Future netStateFuture = sce::Toolkit::NP::Utilities::Future(); + sce::Toolkit::NP::NetInfo::Interface::getNetInfo(&netStateFuture); + + // Wait for the net state (< 5ms) + while (netStateFuture.isBusy()) + { + Sleep(1); + } + + if (netStateFuture.hasResult()) + { + sce::Toolkit::NP::NetStateBasic *netState = netStateFuture.get(); + ProfileManager.SetNetworkStatus(netState->connectionStatus == SCE_NET_CTL_STATE_IPOBTAINED); + } + else + { + // Error message means we're disconnected + ProfileManager.SetNetworkStatus(false); + } + +// // Register Client ID for Auth +// ret = sce::Toolkit::NP::Interface::registerClientId(CLIENT_ID_FOR_SAMPLE); +// if (ret < 0) +// { +// app.DebugPrintf("Failed to register Auth Client ID : 0x%x\n", ret); +// assert(0); +// } + +} + + + +void OrbisNPToolkit::createNPSession() +{ +#define CURRENT_SESSION_ATTR_NUMS 5 +#define SESSION_IMAGE_PATH "/app0/orbis/session_image.png" +#define SESSION_STATUS "Minecraft online game (this text needs defined and localised)" +#define SESSION_NAME "Minecraft(this text needs defined and localised)" + + static const int maxSlots = 8; + + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x ...\n",ret); + } + + sce::Toolkit::NP::CreateNpSessionRequest createSessionRequest; + memset(&createSessionRequest,0,sizeof(createSessionRequest)); + strncpy(createSessionRequest.sessionName,SESSION_NAME,strlen(SESSION_NAME)); + createSessionRequest.sessionTypeFlag = SCE_TOOLKIT_NP_CREATE_SESSION_TYPE_PUBLIC; + createSessionRequest.maxSlots = maxSlots; + strncpy(createSessionRequest.sessionImgPath,SESSION_IMAGE_PATH,strlen(SESSION_IMAGE_PATH)); + strncpy(createSessionRequest.sessionStatus,SESSION_STATUS,strlen(SESSION_STATUS)); + createSessionRequest.userInfo.userId = userId; + char test[3] = {'R','K','B'}; + createSessionRequest.sessionData= test; + createSessionRequest.sessionDataSize = 3; + ret = sce::Toolkit::NP::Sessions::Interface::create(&createSessionRequest,&sm_createJoinFuture); +} + + +void OrbisNPToolkit::joinNPSession() +{ + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x ...\n",ret); + } + + sce::Toolkit::NP::JoinNpSessionRequest joinSessionRequest; + memset(&joinSessionRequest,0,sizeof(joinSessionRequest)); + // still to sort this out + ORBIS_STUBBED; +} + +void OrbisNPToolkit::leaveNPSession() +{ + +} + + + +void OrbisNPToolkit::getMessageData(SceNpGameCustomDataEventParam* paramData) +{ + + sce::Toolkit::NP::GameCustomDataGameDataRequest req; + SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; + int ret = sceUserServiceGetInitialUser(&userId); + if( ret < 0 ) { + //Error handling + } + + req.itemId = paramData->itemId; + req.userInfo.userId = userId; + ret = sce::Toolkit::NP::GameCustomData::Interface::getGameData(&req, &m_messageData); + if( ret < 0 ) { + //Error handling + } else { + //Error handling + } + + +// +// +// sce::Toolkit::NP::GameDataRequest req; +// SceUserServiceUserId userId = SCE_USER_SERVICE_USER_ID_INVALID; +// int ret = sceUserServiceGetInitialUser(&userId); +// if( ret < 0 ) { +// //Error handling +// } +// +// req.itemId = ItemID ; +// req.userInfo.userId = userId; +// sce::Toolkit::NP::Utilities::Future actualMessage; +// ret = sce::Toolkit::NP::GameCustomData::Interface::getGameData(&req,&actualMessage,false); +// if( ret < 0 ) { +// //Error handling +// } else { +// //Error handling +// } +// +// +// +// +// +// +// +// +// app.DebugPrintf("Session Invitation \n"); +// sce::Toolkit::NP::ReceiveMessageRequest request; +// request.eventParamData = paramData; +// request.msgType = SCE_TOOLKIT_NP_MESSAGE_TYPE_CUSTOM_DATA; +// sceUserServiceGetInitialUser(&request.userInfo.userId); +// sce::Toolkit::NP::Messaging::Interface::retrieveMessageAttachmentFromEvent(&request,&m_messageData); +} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h new file mode 100644 index 0000000..1ea72c4 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/Orbis_NPToolkit.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + + +class OrbisNPToolkit +{ +public: + void init(); + static void sceNpToolkitCallback( const sce::Toolkit::NP::Event& event); + static void coreCallback( const sce::Toolkit::NP::Event& event); + static void presenceCallback( const sce::Toolkit::NP::Event& event); + static void sessionsCallback( const sce::Toolkit::NP::Event& event); + static void gameCustomDataCallback( const sce::Toolkit::NP::Event& event); + static void messagingCallback( const sce::Toolkit::NP::Event& event); + + static void createNPSession(); + static void destroyNPSession(); + static void joinNPSession(); + static void leaveNPSession(); + static SceNpSessionId* getNPSessionID() { return &m_currentSessionInfo.npSessionId; } + + static void getMessageData(SceNpGameCustomDataEventParam* paramData); +private: + static sce::Toolkit::NP::Utilities::Future sm_createJoinFuture; + static sce::Toolkit::NP::NpSessionInformation m_currentSessionInfo; + static sce::Toolkit::NP::Utilities::Future m_messageData; + +}; + +// Singleton +extern OrbisNPToolkit NPToolkit; + diff --git a/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp new file mode 100644 index 0000000..0af1658 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "PsPlusUpsellWrapper_Orbis.h" + +PsPlusUpsellWrapper::PsPlusUpsellWrapper(int userIndex) + : m_userIndex(userIndex) +{ + m_bHasResponse = false; +} + +bool PsPlusUpsellWrapper::displayUpsell() +{ + app.DebugPrintf(" Bringing up system PsPlus upsell for Pad_%i.\n", m_userIndex); + + sceNpCommerceDialogInitialize(); + + SceNpCommerceDialogParam param; + sceNpCommerceDialogParamInitialize(¶m); + param.mode = SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + param.userId = ProfileManager.getUserID(m_userIndex); + + sceNpCommerceDialogOpen(¶m); + + return true; +} + +bool PsPlusUpsellWrapper::hasResponse() +{ + if (m_bHasResponse) return true; + + if (sceNpCommerceDialogUpdateStatus() == SCE_COMMON_DIALOG_STATUS_FINISHED) + { + app.DebugPrintf( + " Pad_%i %s an PsPlus upsell.\n", + m_userIndex, (m_result.authorized?"accepted":"rejected") + ); + + m_bHasResponse = true; + + sceNpCommerceDialogGetResult(&m_result); + + sceNpCommerceDialogTerminate(); + +#ifndef _CONTENT_PACKAGE + // 4J-JEV: If HasPlayStationPlus is miraculously true now, + // we didn't give it enough time to update before bringing up the upsell + assert( ProfileManager.HasPlayStationPlus(m_userIndex) == false ); +#endif + + ProfileManager.PsPlusUpdate(m_userIndex, &m_result); + } + + return false; +} diff --git a/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h new file mode 100644 index 0000000..64ad9a9 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/PsPlusUpsellWrapper_Orbis.h @@ -0,0 +1,20 @@ +#pragma once + + +// 4J-JEV: To help handle PsPlus upsells. +class PsPlusUpsellWrapper +{ +private: + bool m_bHasResponse; + + SceNpCommerceDialogResult m_result; + +public: + const int m_userIndex; + + PsPlusUpsellWrapper(int userIndex); + + bool displayUpsell(); + + bool hasResponse(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp new file mode 100644 index 0000000..5c02cb0 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.cpp @@ -0,0 +1,4155 @@ +#include "stdafx.h" +#include "SQRNetworkManager_Orbis.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../ps4__np_conf.h" +#include "Orbis_NPToolkit.h" +#include "SonyVoiceChat_Orbis.h" +#include "Common\Network\Sony\SonyHttp.h" +#include "..\..\..\Minecraft.World\C4JThread.h" +// #include "..\PS3Extras\PS3Strings.h" + + +int (* SQRNetworkManager_Orbis::s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad) = NULL; +void * SQRNetworkManager_Orbis::s_SignInCompleteParam = NULL; +sce::Toolkit::NP::PresenceDetails SQRNetworkManager_Orbis::s_lastPresenceInfo; + +__int64 SQRNetworkManager_Orbis::s_lastPresenceTime = 0; +__int64 SQRNetworkManager_Orbis::s_resendPresenceTime = 0; + +bool SQRNetworkManager_Orbis::s_presenceStatusDirty = false; +bool SQRNetworkManager_Orbis::s_presenceDataDirty = false; +bool SQRNetworkManager_Orbis::s_signInCompleteCallbackIfFailed = false; +bool SQRNetworkManager_Orbis::s_signInCompleteCallbackFAIL = false; +int SQRNetworkManager_Orbis::s_SignInCompleteCallbackPad = -1; +bool SQRNetworkManager_Orbis::s_SignInCompleteCallbackPending = false; +long long SQRNetworkManager_Orbis::s_errorDialogClosed = -1; +long long SQRNetworkManager_Orbis::s_systemDialogClosed = -1; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::s_lastPresenceSyncInfo = { 0 }; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::c_presenceSyncInfoNULL = { 0 }; +//SceNpBasicAttachmentDataId SQRNetworkManager_Orbis::s_lastInviteIdToRetry = SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID; +long long SQRNetworkManager_Orbis::s_roomStartTime = 0; +bool SQRNetworkManager_Orbis::b_inviteRecvGUIRunning = false; +SQRNetworkManager_Orbis::PresenceSyncInfo* SQRNetworkManager_Orbis::m_gameBootInvite; +SQRNetworkManager_Orbis::PresenceSyncInfo SQRNetworkManager_Orbis::m_gameBootInvite_data; +bool SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=false; +bool SQRNetworkManager_Orbis::s_errorDialogRunning=false; +bool SQRNetworkManager_Orbis::s_bInviteDialogRunning=false; + +static const int sc_UserEventHandle = 0; + +static const int sc_verbose = false; + +int g_numRUDPContextsBound = 0; +//unsigned int SQRNetworkManager_Orbis::RoomSyncData::playerCount = 0; + +// This maps internal to extern states, and needs to match element-by-element the eSQRNetworkManagerInternalState enumerated type +const SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::m_INTtoEXTStateMappings[SQRNetworkManager_Orbis::SNM_INT_STATE_COUNT] = +{ + SNM_STATE_INITIALISING, // SNM_INT_STATE_UNINITIALISED + SNM_STATE_INITIALISING, // SNM_INT_STATE_SIGNING_IN + SNM_STATE_INITIALISING, // SNM_INT_STATE_STARTING_CONTEXT + SNM_STATE_INITIALISE_FAILED, // SNM_INT_STATE_INITIALISE_FAILED + SNM_STATE_IDLE, // SNM_INT_STATE_IDLE + SNM_STATE_IDLE, // SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_FOUND + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_CREATING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT + SNM_STATE_HOSTING, // SNM_INT_STATE_HOSTING_WAITING_TO_PLAY + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_FOUND + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED + SNM_STATE_JOINING, // SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS + SNM_STATE_ENDING, // SNM_INT_STATE_SERVER_DELETING_CONTEXT + SNM_STATE_STARTING, // SNM_INT_STATE_STARTING + SNM_STATE_PLAYING, // SNM_INT_STATE_PLAYING + SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING + SNM_STATE_LEAVING, // SNM_INT_STATE_LEAVING_FAILED + SNM_STATE_ENDING, // SNM_INT_STATE_ENDING +}; + +SQRNetworkManager_Orbis::SQRNetworkManager_Orbis(ISQRNetworkManagerListener *listener) +{ + m_state = SNM_INT_STATE_UNINITIALISED; + m_stateExternal = SNM_STATE_INITIALISING; + m_nextIdleReasonIsFull = false; + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + m_serverContextValid = false; + m_isHosting = false; + m_currentSmallId = 0; + memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); + m_listener = listener; + m_soc = -1; + m_resendExternalRoomDataCountdown = 0; + m_matching2initialised = false; + m_matchingContextValid = false; + m_inviteIndex = 0; + m_doBootInviteCheck = true; + m_isInSession = false; + m_offlineGame = false; + m_offlineSQR = false; + m_aServerId = NULL; + m_gameBootInvite = NULL; + m_onlineStatus = false; + m_bLinkDisconnected = false; + m_bRefreshingRestrictionsForInvite = false; + + InitializeCriticalSection(&m_csRoomSyncData); + InitializeCriticalSection(&m_csPlayerState); + InitializeCriticalSection(&m_csStateChangeQueue); + InitializeCriticalSection(&m_signallingEventListCS); + + int ret = sceKernelCreateEqueue(&m_basicEventQueue, "SQRNetworkManager_Orbis EQ"); + assert(ret == SCE_OK); + ret = sceKernelAddUserEvent(m_basicEventQueue, sc_UserEventHandle); + assert(ret == SCE_OK); + + m_basicEventThread = new C4JThread(&BasicEventThreadProc,this,"Basic Event Handler"); + m_basicEventThread->Run(); +} + +// First stage of initialisation. This initialises a few things that don't require the user to be signed in, and then kicks of the network start dialog utility. +// Initialisation continues in InitialiseAfterOnline once this completes. +void SQRNetworkManager_Orbis::Initialise() +{ + #define NP_IN_GAME_MESSAGE_POOL_SIZE ( 16 * 1024 ) + + int32_t ret = 0; + int32_t libCtxId = 0; + ret = sceNpInGameMessageInitialize(NP_IN_GAME_MESSAGE_POOL_SIZE, NULL); + assert (ret >= 0); + libCtxId = ret; + + assert( m_state == SNM_INT_STATE_UNINITIALISED ); + + + //Initialize libnetctl + ret = sceNetCtlInit(); + if( ( ret < 0 /*&& ret != CELL_NET_CTL_ERROR_NOT_TERMINATED*/ ) || ForceErrorPoint( SNM_FORCE_ERROR_NET_CTL_INIT ) ) + { + app.DebugPrintf("sceNetCtlInit failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + int hid; + ret = sceNetCtlRegisterCallback(&NetCtlCallback,this,&hid); + + // Initialise RUDP + const int RUDP_POOL_SIZE = (500 * 1024); // TODO - find out what we need, this size is copied from library reference + uint8_t *rudp_pool = (uint8_t *)malloc(RUDP_POOL_SIZE); + ret = sceRudpInit(rudp_pool, RUDP_POOL_SIZE); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_RUDP_INIT ) ) + { + app.DebugPrintf("sceRudpInit failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + SetState(SNM_INT_STATE_SIGNING_IN); + + + SonyHttp::init(); + ret = sceNpSetNpTitleId(GetSceNpTitleId(), GetSceNpTitleSecret()); + if (ret < 0) + { + app.DebugPrintf("sceNpSetNpTitleId failed, ret=%x\n", ret); + assert(0); + } + + ret = sceRudpEnableInternalIOThread(RUDP_THREAD_STACK_SIZE, RUDP_THREAD_PRIORITY); + if(ret < 0) + { + app.DebugPrintf("sceRudpEnableInternalIOThread failed with error code 0x%08x\n", ret); + assert(0); + } + // Already online? the callback won't catch this, so carry on initialising now + if(ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + InitialiseAfterOnline(); + } + else + { + // On Orbis, PSN sign in is only handled by the XMB + SetState(SNM_INT_STATE_IDLE); + } + SonyVoiceChat_Orbis::init(); + + +} + +void SQRNetworkManager_Orbis::Terminate() +{ + // If playing, attempt to nicely leave the room before shutting down so that our friends won't still think this game is in progress + if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || + ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || + ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || + ( m_state == SNM_INT_STATE_PLAYING ) ) + { + if( !m_offlineGame ) + { + LeaveRoom(true); + int count = 200; + do + { + Tick(); + Sleep(10); + count--; + } while( ( count > 0 ) && ( m_state != SNM_INT_STATE_IDLE ) ); + app.DebugPrintf(CMinecraftApp::USER_RR,"Attempted to leave room, %dms used\n",count * 10); + } + } + + int ret = sceRudpEnd(); + ret = sceNpMatching2Terminate(); + // Terminate event thread by sending it a non-zero value for data + sceKernelTriggerUserEvent(m_basicEventQueue, sc_UserEventHandle, (void*)1); + + + do + { + Sleep(10); + } while( m_basicEventThread->isRunning() ); +} + +// Second stage of initialisation, that requires NP Manager to be online & the player to be signed in. This kicks of the creation of a context +// for Np Matching 2. Initialisation is finally complete when we get a callback to ContextCallback. The SQRNetworkManager_Orbis is then finally moved +// into SNM_INT_STATE_IDLE at this stage. +void SQRNetworkManager_Orbis::InitialiseAfterOnline() +{ + SceNpId npId; + int option = 0; + + // We should only be doing this if we have come in from an initialisation stage (SQRNetworkManager_Orbis::Initialise) or we've had a network disconnect and are coming in from an offline state. + // Don't do anything otherwise - this mainly to catch a bit of a corner case in the initialisation phase where potentially we could register for the callback that would call this with sceNpManagerRegisterCallback. + // and could then really quickly go online so that the there becomes two paths (via the callback or SQRNetworkManager_Orbis::Initialise) by which this could be called + if( ( m_state != SNM_INT_STATE_SIGNING_IN ) && !(( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ) + { + // If we aren't going to continue on with this sign-in but are expecting a callback, then let the game know that we have completed the bit we are expecting to do. This + // will happen whilst in game, when we want to be able to sign into PSN, but don't expect the full matching stuff to get set up. + if( s_SignInCompleteCallbackFn ) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam,true,s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + s_SignInCompleteCallbackPad = -1; + } + return; + } + + // Initialize matching2 + SceNpMatching2InitializeParameter initParam; + memset(&initParam,0,sizeof(initParam)); + initParam.size = sizeof(initParam); + initParam.cpuAffinityMask = 1 << 4; + initParam.threadPriority = SCE_KERNEL_PRIO_FIFO_DEFAULT; // This seems to be completely ignored and always set to default anyway + initParam.threadStackSize = SCE_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT * 2; + initParam.poolSize = SCE_NP_MATCHING2_POOLSIZE_DEFAULT * 2; + int ret = sceNpMatching2Initialize(&initParam); + + if( ( ret < 0 && ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) || ForceErrorPoint( SNM_FORCE_ERROR_MATCHING2_INIT ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2Initialize failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + app.DebugPrintf("SQRNetworkManager::InitialiseAfterOnline - matching context is now valid\n"); + m_matching2initialised = true; + + // Get NP ID of the signed-in user + SceNpId npID; + int primaryPad = ProfileManager.GetPrimaryPad(); + if(primaryPad >=0 && ProfileManager.IsSignedInLive(primaryPad)) + { + ProfileManager.GetSceNpId(primaryPad, &npID); + } + else + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - Primary pad not signed in to live"); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + SceNpServiceLabel serviceLabel = 0; + SceNpMatching2CreateContextParam param; + memset(¶m, 0, sizeof(param)); + param.npId = &npID; + param.serviceLabel = serviceLabel; + param.size = sizeof(param); + + + ret = sceNpMatching2CreateContext(¶m, &m_matchingContext); + + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2CreateContext failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + m_matchingContextValid = true; + + bool bRet = RegisterCallbacks(); + if( ( !bRet ) || ForceErrorPoint( SNM_FORCE_ERROR_REGISTER_CALLBACKS ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - RegisterCallbacks failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + return; + } + + // State should be starting context until the callback that this has been created happens + SetState(SNM_INT_STATE_STARTING_CONTEXT); + + // Start the context + // Set time-out time to 10 seconds + ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_CONTEXT_START_ASYNC ) ) + { + app.DebugPrintf("SQRNetworkManager_Orbis::InitialiseAfterOnline - sceNpMatching2ContextStart failed with error 0x%08x\n", ret); + SetState(SNM_INT_STATE_INITIALISE_FAILED); + } +} + +void SQRNetworkManager_Orbis::RefreshChatAndContentRestrictionsReturned_HandleInvite(void *pParam) +{ + SQRNetworkManager_Orbis *netMan = (SQRNetworkManager_Orbis *)pParam; + + netMan->m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), netMan->m_gameBootInvite ); + netMan->m_gameBootInvite = NULL; + + netMan->m_bRefreshingRestrictionsForInvite = false; +} + +// General tick function to be called from main game loop - any internal tick functions should be called from here. +void SQRNetworkManager_Orbis::Tick() +{ + OnlineCheck(); + sceNetCtlCheckCallback(); + ServerContextTick(); + RoomCreateTick(); + FriendSearchTick(); + TickRichPresence(); + TickInviteGUI(); + TickNotify(); + tickErrorDialog(); + SignallingEventsTick(); // process the signalling events here now instead of in the matching2 callback + if( ( m_gameBootInvite ) && ( s_safeToRespondToGameBootInvite ) && !m_bRefreshingRestrictionsForInvite ) + { + m_bRefreshingRestrictionsForInvite = true; + ProfileManager.RefreshChatAndContentRestrictions(RefreshChatAndContentRestrictionsReturned_HandleInvite, this); + //m_listener->HandleInviteReceived( ProfileManager.GetPrimaryPad(), m_gameBootInvite ); + //m_gameBootInvite = NULL; + } + + ErrorHandlingTick(); + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + if( m_resendExternalRoomDataCountdown ) + { + if( m_state == SNM_INT_STATE_PLAYING ) + { + m_resendExternalRoomDataCountdown--; + if( m_resendExternalRoomDataCountdown == 0 ) + { + UpdateExternalRoomData(); + } + } + else + { + m_resendExternalRoomDataCountdown = 0; + } + } + +// ProfileManager.SetNetworkStatus(GetOnlineStatus()); + + // Client only - do the final transition to a starting & playing state once we have fully joined the room, And told the game about all the local players so they are also all valid + if( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) + { + if( m_localPlayerJoined == m_localPlayerCount ) + { + // Since we're now fully joined, we can update our presence info so that our friends could find us in this game. This data was set up + // at the point that we joined the game (either from search info, or an invitation). + UpdateRichPresenceCustomData(&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); + SetState( SNM_INT_STATE_STARTING); + SetState( SNM_INT_STATE_PLAYING ); + } + } + + if( m_state == SNM_INT_STATE_SERVER_DELETING_CONTEXT ) + { + // make sure we've removed all the remote players and killed the udp connections before we bail out + if(m_RudpCtxToPlayerMap.size() == 0) + ResetToIdle(); + } + + EnterCriticalSection(&m_csStateChangeQueue); + while(m_stateChangeQueue.size() > 0 ) + { + if( m_listener ) + { + m_listener->HandleStateChange(m_stateChangeQueue.front().m_oldState, m_stateChangeQueue.front().m_newState, m_stateChangeQueue.front().m_idleReasonIsSessionFull); + if( m_stateChangeQueue.front().m_newState == SNM_STATE_IDLE ) + { + m_isInSession = false; + } + } + m_stateExternal = m_stateChangeQueue.front().m_newState; + m_stateChangeQueue.pop(); + } + LeaveCriticalSection(&m_csStateChangeQueue); + + // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it frmo the tick instead + if(m_bCallPSNSignInCallback) + { + // TODO: This may be a sign in attempt for a different pad, need to fix this + app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ s_SignInCompleteCallbackFn false ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + + m_bCallPSNSignInCallback=false; + if( s_signInCompleteCallbackIfFailed && s_signInCompleteCallbackFAIL) + { + s_signInCompleteCallbackIfFailed=false; + s_signInCompleteCallbackFAIL=false; + if(s_SignInCompleteCallbackFn) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + } + s_SignInCompleteCallbackPad = -1; + } + else if(s_SignInCompleteCallbackFn) + { + s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, s_SignInCompleteCallbackPad); + s_SignInCompleteCallbackFn = NULL; + s_SignInCompleteCallbackPad = -1; + } + } + +} + + +void SQRNetworkManager_Orbis::tickErrorDialog() +{ + if(s_errorDialogRunning) + { + SceErrorDialogStatus s = sceErrorDialogUpdateStatus(); + switch (s) + { + case SCE_ERROR_DIALOG_STATUS_NONE: + assert(0); + break; + + case SCE_ERROR_DIALOG_STATUS_INITIALIZED: + case SCE_ERROR_DIALOG_STATUS_RUNNING: +// sceErrorDialogClose(); + break; + + case SCE_ERROR_DIALOG_STATUS_FINISHED: + sceErrorDialogTerminate(); + s_errorDialogRunning = false; + + // Start callback timer + s_SignInCompleteCallbackPending = true; + s_errorDialogClosed = System::currentTimeMillis(); + break; + } + } + + + // Note: I'll revisit this at some point, there must be a better way + // Long story short we need to wait for a bit for the system UI to appear, If it doesn't appear or it + // disappears we wait for a bit then check the sign in results. Not perfect but it is safe! + + // If we have a pending callback, try callback + if (s_SignInCompleteCallbackPending) + { + int sinceErrorClosed = System::currentTimeMillis() - s_errorDialogClosed; + + // If error dialog has been gone for a while, look for system dialogue + if (sinceErrorClosed > SYSTEM_UI_WAIT_TIME) + { + // Check if the system UI is currently displayed + SceSystemServiceStatus status = SceSystemServiceStatus(); + sceSystemServiceGetStatus(&status); + bool systemUiDisplayed = status.isInBackgroundExecution || status.isSystemUiOverlaid; + + if (systemUiDisplayed) + { + // Wait till the system goes away + } + else + { + // Start timer if we haven't already + if (s_systemDialogClosed <= 0) + { + s_systemDialogClosed = System::currentTimeMillis(); + } + else + { + int sinceSystemClosed = System::currentTimeMillis() - s_systemDialogClosed; + + // If the system dialog has been closed a while, we're ready to callback + if (sinceSystemClosed > SYSTEM_UI_WAIT_TIME) + { + // 4J-PB - don't call this here, let the SQRNetworkManager_Orbis::Tick call it + //CallSignInCompleteCallback(); + m_bCallPSNSignInCallback=true; + + // Reset + s_SignInCompleteCallbackPending = false; + s_errorDialogClosed = -1; + s_systemDialogClosed = -1; + } + } + } + } + } +} + +// Detect any states which reflect internal error states, do anything required, and transition away again +void SQRNetworkManager_Orbis::ErrorHandlingTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_INITIALISE_FAILED: + if( s_SignInCompleteCallbackFn ) + { + if( s_signInCompleteCallbackIfFailed ) + { + // 4J-PB - flag the failed sign in complete to be called + s_signInCompleteCallbackFAIL=true; + m_bCallPSNSignInCallback=true; + //s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,s_SignInCompleteCallbackPad); + } + //s_SignInCompleteCallbackFn = NULL; + //s_SignInCompleteCallbackPad = -1; + } + app.DebugPrintf("Network error: SNM_INT_STATE_INITIALISE_FAILED\n"); + if( m_isInSession && m_offlineGame) // m_offlineSQR ) // MGH - changed this to m_offlineGame, as m_offlineSQR can be true when running an online game but the init has failed because the servers are down + { + // This is a fix for an issue where a player attempts (and fails) to sign in, whilst in an offline game. This was setting the state to idle, which in turn + // sets the game to Not be in a session anymore (but the game wasn't generally aware of, and so keeps playing). Howoever, the game's connections use + // their tick to determine whether to empty their queues or not and so no communications (even though they don't actually use this network manager for local connections) + // were happening. + SetState(SNM_INT_STATE_PLAYING); + } + else + { + m_offlineSQR = true; + SetState(SNM_INT_STATE_IDLE); + } + break; + case SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED\n"); + ResetToIdle(); + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED\n"); + app.SetDisconnectReason(DisconnectPacket::eDisconnect_NetworkError); + DeleteServerContext(); + break; + case SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED\n"); + ResetToIdle(); + break; + case SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED\n"); + DeleteServerContext(); + break; + case SNM_INT_STATE_LEAVING_FAILED: + app.DebugPrintf("Network error: SNM_INT_STATE_LEAVING_FAILED\n"); + if( !m_isHosting ) + { + RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); + } + DeleteServerContext(); + break; + } + +} + +// Start hosting a game, by creating a room & joining it. We explicity create a server context here (via GetServerContext) as Sony suggest that +// this means we have greater control of representing when players are actually "online". The creation of the room is carried out in a callback +// after that server context is made (ServerContextValidCallback_CreateRoom). +// hostIndex is the index of the user that is hosting the session, and localPlayerMask has bit 0 - 3 set to indicate the full set of local players joining the game. +// extData and extDataSize define the initial state of room data that is externally visible (eg by players searching for rooms, but not in it) +void SQRNetworkManager_Orbis::CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline) +{ + // hostIndex should always be in the mask + assert( ( ( 1 << hostIndex ) & localPlayerMask ) != 0 ); + + m_isHosting = true; + m_joinExtData = extData; + m_joinExtDataSize = extDataSize; + m_offlineGame = offline; + m_resendExternalRoomDataCountdown = 0; + m_isInSession= true; + + // Default value for room, which we can use for offlinae games + m_room = 0; + + // Initialise room data that will be synchronised. Slot 0 is always reserved for the host. We don't know the + // room member until the room is actually created so this will be set/updated at that point + memset( &m_roomSyncData, 0, sizeof(m_roomSyncData) ); + m_roomSyncData.setPlayerCount(1); + m_roomSyncData.players[0].m_smallId = m_currentSmallId++; + m_roomSyncData.players[0].m_localIdx = hostIndex; + + // Remove the host player that we've already added, then add any other local players specified in the mask + localPlayerMask &= ~( ( 1 << hostIndex ) & localPlayerMask ); + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( localPlayerMask & ( 1 << i ) ) + { + m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_smallId = m_currentSmallId++; + m_roomSyncData.players[m_roomSyncData.getPlayerCount()].m_localIdx = i; + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + } + } + m_localPlayerCount = m_roomSyncData.getPlayerCount(); + + // For offline games, we can jump straight to the state that says we've just created the room (or would have, for an online game) + if( m_offlineGame ) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); + } + else + { + // Kick off the sequence of events required for an online game, starting with getting the server context + m_isInSession = GetServerContext(); + } +} + +// Updates the externally visible data that was associated with the room when it was created with CreateAndJoinRoom. +void SQRNetworkManager_Orbis::UpdateExternalRoomData() +{ + if( m_offlineGame ) return; + if( m_isHosting ) + { + SceNpMatching2SetRoomDataExternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + SceNpMatching2BinAttr roomBinAttr; + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + roomBinAttr.ptr = m_joinExtData; + roomBinAttr.size = m_joinExtDataSize; + reqParam.roomBinAttrExternalNum = 1; + reqParam.roomBinAttrExternal = &roomBinAttr; + + int ret = sceNpMatching2SetRoomDataExternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SetRoomDataExternal returns 0x%x, number of players %d\n",ret,((char *)m_joinExtData)[174]); + if( ( ret < 0 ) || ForceErrorPoint( SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA ) ) + { + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + m_resendExternalRoomDataCountdown = 60; + } + } +} + +// Determine if the friend room manager is busy. If it isn't busy, then other operations (searching for a friend, reading the found friend's room lists) may safely be performed +bool SQRNetworkManager_Orbis::FriendRoomManagerIsBusy() +{ + return (m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE); +} + +// Initiate a search for rooms that the signed in user's friends are in. This is an asynchronous operation, this function returns after it kicks off a search across all game servers +// for any of the player's friends. +bool SQRNetworkManager_Orbis::FriendRoomManagerSearch() +{ + if( m_state != SNM_INT_STATE_IDLE ) return false; + + // Don't start another search if we're already searching... + if( m_friendSearchState != SNM_FRIEND_SEARCH_STATE_IDLE ) + { + return false; + } + + // Free up any external data that we received from the previous search + for( int i = 0; i < m_aFriendSearchResults.size(); i++ ) + { + if(m_aFriendSearchResults[i].m_RoomExtDataReceived) + free(m_aFriendSearchResults[i].m_RoomExtDataReceived); + m_aFriendSearchResults[i].m_RoomExtDataReceived = NULL; + } + + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT; + m_friendCount = 0; + m_aFriendSearchResults.clear(); + + // Get friend list - doing this in another thread as it can lock up for a few seconds + m_getFriendCountThread = new C4JThread(&GetFriendsThreadProc,this,"GetFriendsThreadProc"); + m_getFriendCountThread->Run(); + + return true; +} + +bool SQRNetworkManager_Orbis::FriendRoomManagerSearch2() +{ + SceNpMatching2AttributeId attrId; + SceNpMatching2GetUserInfoListRequest reqParam; + + if( m_friendCount == 0 ) + { + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + return false; + } + + if( m_aFriendSearchResults.size() > 0 ) + { + // If we have some results, then we also want to make sure that we don't have any duplicate rooms here if more than one friend is playing in the same room. + unordered_set uniqueRooms; + for( unsigned int i = 0; i < m_aFriendSearchResults.size(); i++ ) + { + if(m_aFriendSearchResults[i].m_RoomFound) + { + uniqueRooms.insert( m_aFriendSearchResults[i].m_RoomId ); + } + } + + // Tidy the results up further based on this + for( unsigned int i = 0; i < m_aFriendSearchResults.size(); ) + { + if( uniqueRooms.find(m_aFriendSearchResults[i].m_RoomId) == uniqueRooms.end() ) + { + free(m_aFriendSearchResults[i].m_RoomExtDataReceived); + m_aFriendSearchResults[i] = m_aFriendSearchResults.back(); + m_aFriendSearchResults.pop_back(); + } + else + { + uniqueRooms.erase(m_aFriendSearchResults[i].m_RoomId); + i++; + } + } + } + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_IDLE; + return true; +} + +void SQRNetworkManager_Orbis::FriendSearchTick() +{ + // Move onto next state if we're done getting our friend count + if( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_COUNT ) + { + if( !m_getFriendCountThread->isRunning() ) + { + m_friendSearchState = SNM_FRIEND_SEARCH_STATE_GETTING_FRIEND_INFO; + delete m_getFriendCountThread; + m_getFriendCountThread = NULL; + FriendRoomManagerSearch2(); + } + } +} + +// The handler for basic events can't actually get the events themselves, this has to be done on another thread. Instead, we send a sys_event_t to a queue on This thread, +// which has a single data item used which we can use to determine whether to terminate this thread or get a basic event & handle that. +int SQRNetworkManager_Orbis::BasicEventThreadProc( void *lpParameter ) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; + + int ret = SCE_OK; + SceKernelEvent event; + int outEv; + + do + { + ret = sceKernelWaitEqueue(manager->m_basicEventQueue, &event, 1, &outEv, NULL); + + // If the sys_event_t we've sent here from the handler has a non-zero data1 element, this is to signify that we should terminate the thread + if( event.udata == 0 ) + { +// int iEvent; +// SceNpUserInfo from; +// uint8_t buffer[SCE_NP_BASIC_MAX_MESSAGE_SIZE]; +// size_t bufferSize = SCE_NP_BASIC_MAX_MESSAGE_SIZE; +// int ret = sceNpBasicGetEvent(&iEvent, &from, &buffer, &bufferSize); +// if( ret == 0 ) +// { +// if( iEvent == SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION ) +// { +// // 4J Stu - Don't do this here as it can be very disruptive to gameplay. Players can bring this up from LoadOrJoinMenu, PauseMenu and InGameInfoMenu +// //sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); +// } +// if( iEvent == SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT ) +// { +// SceNpBasicExtendedAttachmentData *result = (SceNpBasicExtendedAttachmentData *)buffer; +// if(result->userAction == SCE_NP_BASIC_MESSAGE_ACTION_ACCEPT ) +// { +// manager->GetInviteDataAndProcess(result->data.id); +// } +// } +// app.DebugPrintf("Incoming basic event of type %d\n",iEvent); +// } + } + + } while(event.udata == 0 ); + return 0; +} + +int SQRNetworkManager_Orbis::GetFriendsThreadProc( void* lpParameter ) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)lpParameter; + sce::Toolkit::NP::Utilities::Future friendList; + + int ret = 0; + manager->m_aFriendSearchResults.clear(); + manager->m_friendCount = 0; + if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + app.DebugPrintf("getFriendslist failed, not signed into Live! \n"); + return 0; + } + + sce::Toolkit::NP::FriendInfoRequest requestParam; + memset(&requestParam,0,sizeof(requestParam)); + requestParam.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_IN_CONTEXT; + requestParam.limit = 0; + requestParam.offset = 0; + requestParam.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + bool async = false; + ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&friendList, &requestParam, async); + if(ret != SCE_OK) + { + app.DebugPrintf("getFriendslist failed! 0x%08x\n", ret); + return 0; + } + + if (friendList.hasResult()) + { + sce::Toolkit::NP::FriendsList::const_iterator iter ; + int i = 1 ; + bool noFriends = true; + for (iter = friendList.get()->begin(); iter != friendList.get()->end() ; ++iter,i++) + { + manager->m_friendCount++; + noFriends = false; + app.DebugPrintf("------ Friend: %d -----\n",i); + app.DebugPrintf("Online Name: %s\n",iter->npid.handle.data); + app.DebugPrintf("------------------------\n"); + + + sce::Toolkit::NP::PresenceRequest presenceRequest; + sce::Toolkit::NP::Utilities::Future presenceInfo; + memset(&presenceRequest,0,sizeof(presenceRequest)); + strncpy(presenceRequest.onlineId.data,iter->npid.handle.data, strlen(iter->npid.handle.data)); + presenceRequest.presenceType = SCE_TOOLKIT_NP_PRESENCE_TYPE_INCONTEXT_INFO; + presenceRequest.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());; + + + ret = sce::Toolkit::NP::Presence::Interface::getPresence(&presenceRequest,&presenceInfo,false); + if( ret < 0 ) + { + app.DebugPrintf("getPresence() error. ret = 0x%x\n", ret); + } + else + { + app.DebugPrintf("\nPresence Data Retrieved:\n"); + app.DebugPrintf("Platform Type: %s\n", presenceInfo.get()->platformType.c_str()); + app.DebugPrintf("Online Status: %s\n", (presenceInfo.get()->onlineStatus == SCE_NP_GAME_PRESENCE_STATUS_OFFLINE)?"OFFLINE":"ONLINE"); + app.DebugPrintf("Presence Type (Refer to SCE_TOOLKIT_NP_PRESENCE_TYPE_* flags): %d\n", presenceInfo.get()->presenceType); + app.DebugPrintf("Title ID: %s\n", presenceInfo.get()->gameInfo.npTitleId.c_str()); + app.DebugPrintf("Title Name: %s\n", presenceInfo.get()->gameInfo.npTitleName.c_str()); + app.DebugPrintf("Title Status String: %s\n", presenceInfo.get()->gameInfo.gameStatus.c_str()); + + FriendSearchResult result; + memcpy(&result.m_NpId, &iter->npid, sizeof(SceNpId)); + result.m_RoomFound = false; + + // Only include the friend's game if its the same network id ( this also filters out generally Zeroed PresenceSyncInfo, which we do when we aren't in an active game session) +// if( presenceDetails.size == sizeof(PresenceSyncInfo) ) + { + PresenceSyncInfo *pso = (PresenceSyncInfo *)presenceInfo.get()->gameInfo.gameData; + if( pso->netVersion == MINECRAFT_NET_VERSION ) + { + if( !pso->inviteOnly ) + { + result.m_RoomFound = true; + result.m_RoomId = pso->m_RoomId; + result.m_ServerId = pso->m_ServerId; + + CPlatformNetworkManagerSony::MallocAndSetExtDataFromSQRPresenceInfo(&result.m_RoomExtDataReceived, pso); + manager->m_aFriendSearchResults.push_back(result); + } + } + } + + } + } + } + else if (friendList.hasError()) + { + app.DebugPrintf( "Error occurred while retrieving FriendsList, ret = 0x%x\n", friendList.getError()); + app.DebugPrintf("Check sign-in status\n"); + + } + + return 0; +} + +// Get count of rooms that friends are playing in. Only valid when FriendRoomManagerIsBusy() returns false +int SQRNetworkManager_Orbis::FriendRoomManagerGetCount() +{ + assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); + return m_aFriendSearchResults.size(); +} + +// Get details of a found session that a friend is playing in. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false +void SQRNetworkManager_Orbis::FriendRoomManagerGetRoomInfo(int idx, SQRNetworkManager_Orbis::SessionSearchResult *searchResult) +{ + assert( idx < m_aFriendSearchResults.size() ); + assert( m_friendSearchState == SNM_FRIEND_SEARCH_STATE_IDLE ); + + searchResult->m_NpId = m_aFriendSearchResults[idx].m_NpId; + searchResult->m_sessionId.m_RoomId = m_aFriendSearchResults[idx].m_RoomId; + searchResult->m_sessionId.m_ServerId = m_aFriendSearchResults[idx].m_ServerId; + searchResult->m_extData = m_aFriendSearchResults[idx].m_RoomExtDataReceived; +} + +// Get overall state of the network manager. +SQRNetworkManager_Orbis::eSQRNetworkManagerState SQRNetworkManager_Orbis::GetState() +{ + return m_stateExternal;; +} + +bool SQRNetworkManager_Orbis::IsHost() +{ + return m_isHosting; +} + +bool SQRNetworkManager_Orbis::IsReadyToPlayOrIdle() +{ + return (( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || ( m_state == SNM_INT_STATE_PLAYING ) || ( m_state == SNM_INT_STATE_IDLE ) ); +} + + +// Consider as "in session" from the moment that a game is created or joined, until the point where the game itself has been told via state change that we are now idle. The +// game code requires IsInSession to return true as soon as it has asked to do one of these things (even if the state system hasn't really caught up with this request yet), and +// it also requires that it is informed of the state changes leading up to not being in the session, before this should report false. +bool SQRNetworkManager_Orbis::IsInSession() +{ + return m_isInSession; +} + +// Get count of players currently in the session +int SQRNetworkManager_Orbis::GetPlayerCount() +{ + return m_roomSyncData.getPlayerCount(); +} + +// Get count of players who are in the session, but not local to this machine +int SQRNetworkManager_Orbis::GetOnlinePlayerCount() +{ + int onlineCount = 0; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + onlineCount++; + } + } + return onlineCount; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByIndex(int idx) +{ + if( idx < MAX_ONLINE_PLAYER_COUNT ) + { + return GetPlayerIfReady(m_aRoomSlotPlayers[idx]); + } + else + { + return NULL; + } +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerBySmallId(int idx) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_smallId == idx ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerByXuid(PlayerUID xuid) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_UID == xuid ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetLocalPlayerByUserIndex(int idx) +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && ( m_roomSyncData.players[i].m_localIdx == idx ) ) + { + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[i]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + return NULL; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetHostPlayer() +{ + EnterCriticalSection(&m_csRoomSyncData); + SQRNetworkPlayer *player = GetPlayerIfReady(m_aRoomSlotPlayers[0]); + LeaveCriticalSection(&m_csRoomSyncData); + return player; +} + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerIfReady(SQRNetworkPlayer *player) +{ + if( player == NULL ) return NULL; + + if( player->IsReady() ) return player; + + return NULL; +} + +// Update state internally +void SQRNetworkManager_Orbis::SetState(SQRNetworkManager_Orbis::eSQRNetworkManagerInternalState state) +{ + eSQRNetworkManagerState oldState = m_INTtoEXTStateMappings[m_state]; + eSQRNetworkManagerState newState = m_INTtoEXTStateMappings[state]; + bool setIdleReasonSessionFull = false; + if( ( state == SNM_INT_STATE_IDLE ) && m_nextIdleReasonIsFull ) + { + setIdleReasonSessionFull = true; + m_nextIdleReasonIsFull = false; + } + m_state = state; + // Queue any important (ie externally relevant) state changes - we will do a call back for these in our main tick. Don't do it directly here + // as we could be coming from any thread at this stage, with any stack size etc. and so we don't generally want to expect the game to be able to handle itself in such circumstances. + if( ( newState != oldState ) || setIdleReasonSessionFull ) + { + EnterCriticalSection(&m_csStateChangeQueue); + m_stateChangeQueue.push(StateChangeInfo(oldState,newState,setIdleReasonSessionFull)); + LeaveCriticalSection(&m_csStateChangeQueue); + } +} + +void SQRNetworkManager_Orbis::ResetToIdle() +{ + app.DebugPrintf("------------------ResetToIdle--------------------\n"); + // If we're the client, remove any networked players properly ( this will destory their rupd context etc.) + if( !m_isHosting ) + { + RemoveNetworkPlayers((1 << MAX_LOCAL_PLAYER_COUNT)-1); + } + m_serverContextValid = false; + m_isHosting = false; + m_currentSmallId = 0; + EnterCriticalSection(&m_csRoomSyncData); + for(int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + delete m_aRoomSlotPlayers[i]; + } + memset( m_aRoomSlotPlayers, 0, sizeof(m_aRoomSlotPlayers) ); + memset( &m_roomSyncData,0,sizeof(m_roomSyncData)); + LeaveCriticalSection(&m_csRoomSyncData); + SetState(SNM_INT_STATE_IDLE); + assert(m_RudpCtxToPlayerMap.size() == 0); + SonyVoiceChat_Orbis::checkFinished(); +} + +// Join a room that was found with FriendRoomManagerSearch. 0 < idx < FriendRoomManagerGetCount(). Only valid when FriendRoomManagerIsBusy() returns false +bool SQRNetworkManager_Orbis::JoinRoom(SQRNetworkManager_Orbis::SessionSearchResult *searchResult, int localPlayerMask) +{ + // Set up the presence info we would like to synchronise out when we have fully joined the game + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, searchResult->m_extData, searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId); + return JoinRoom(searchResult->m_sessionId.m_RoomId, searchResult->m_sessionId.m_ServerId, localPlayerMask, NULL); +} + +// Join room with a specified roomId. This is used when joining from an invite, as well as by the previous method +bool SQRNetworkManager_Orbis::JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_Orbis::PresenceSyncInfo *presence) +{ + // The presence info will be directly passed in if we are joining from an invite, otherwise it has already been set up. This is synchronised out when we have fully joined the game. + if( presence ) + { + memcpy( &s_lastPresenceSyncInfo, presence, sizeof(PresenceSyncInfo) ); + } + + m_isInSession = true; + + m_isHosting = false; + m_offlineGame = false; + m_roomToJoin = roomId; + m_localPlayerJoinMask = localPlayerMask; + m_localPlayerCount = 0; + m_localPlayerJoined = 0; + + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( localPlayerMask & ( 1 << i ) ) m_localPlayerCount++; + } + + return GetServerContext( serverId ); +} + +void SQRNetworkManager_Orbis::StartGame() +{ + assert( ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || (( m_state == SNM_INT_STATE_IDLE ) && m_offlineSQR) ); + + SetState( SNM_INT_STATE_STARTING); + SetState( SNM_INT_STATE_PLAYING); +} + +void SQRNetworkManager_Orbis::LeaveRoom(bool bActuallyLeaveRoom) +{ + if( m_offlineGame ) + { + if( m_state != SNM_INT_STATE_PLAYING ) return; + + SetState(SNM_INT_STATE_LEAVING); + SetState(SNM_INT_STATE_ENDING); + ResetToIdle(); + return; + } + + UpdateRichPresenceCustomData(& c_presenceSyncInfoNULL, sizeof(PresenceSyncInfo) ); + +// SonyVoiceChat::shutdown(); + + // Attempt to leave the room if we are in any of the states we could be in if we have successfully created it + if( bActuallyLeaveRoom ) + { + if( ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS ) || + ( m_state == SNM_INT_STATE_HOSTING_WAITING_TO_PLAY ) || + ( m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) || + ( m_state == SNM_INT_STATE_PLAYING ) ) + { + SceNpMatching2LeaveRoomRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + + SetState(SNM_INT_STATE_LEAVING); + int ret = sceNpMatching2LeaveRoom( m_matchingContext, &reqParam, NULL, &m_leaveRoomRequestId ); + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_LEAVE_ROOM) ) + { + SetState(SNM_INT_STATE_LEAVING_FAILED); + } + } + else if ( m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ) + { + // Haven't created the room yet, but will have created the server context so need to recover from that + DeleteServerContext(); + } + else + { + SetState(SNM_INT_STATE_IDLE); + } + } + else + { + // We have created a room but have now had some kind of connection error which means that we've been dropped out of the room and it has been destroyed, so + // no need to leave it again since it doesn't exist anymore. Still need to destroy server context which may be valid + DeleteServerContext(); + } +} + +void SQRNetworkManager_Orbis::EndGame() +{ +} + +bool SQRNetworkManager_Orbis::SessionHasSpace(int spaceRequired) +{ + return( ( m_roomSyncData.getPlayerCount() + spaceRequired ) <= MAX_ONLINE_PLAYER_COUNT ); +} + +bool SQRNetworkManager_Orbis::AddLocalPlayerByUserIndex(int idx) +{ + if( m_isHosting ) + { + if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; + + // Host's players are always at the start of the sync data, so we just need to find the first entry that isn't us to determine what we want to insert before + int insertAtIdx = m_roomSyncData.getPlayerCount(); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + insertAtIdx = i; + break; + } + else + { + // Don't add the same local index twice + if( m_roomSyncData.players[i].m_localIdx == idx ) + { + return false; + } + } + } + + // Make room for a new entry... + for( int i = m_roomSyncData.getPlayerCount(); i > insertAtIdx; i-- ) + { + m_roomSyncData.players[i] = m_roomSyncData.players[i-1]; + } + m_roomSyncData.players[insertAtIdx].m_localIdx = idx; + m_roomSyncData.players[insertAtIdx].m_roomMemberId = m_localMemberId; + m_roomSyncData.players[insertAtIdx].m_smallId = m_currentSmallId++; + + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + + // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. + // This will also create the required new SQRNetworkPlayer and do all the callbacks that requires etc. + MapRoomSlotPlayers(); + + // Sync this back out to our networked clients... + SyncRoomData(); + + UpdateRemotePlay(); + + // no connections being made because we're all on the host, so add this player to the existing connections + SonyVoiceChat_Orbis::connectPlayerToAll(idx); + return true; + } + else + { + // Don't attempt to join if our client's view of the players indicates that there aren't any free slots + if( m_roomSyncData.getPlayerCount() == MAX_ONLINE_PLAYER_COUNT ) return false; + + // Add the requested player to the mask of local players currently in the game, and update this data - this + // will also then resync with the server which can respond appropriately + int mask = 1 << idx; + if( m_localPlayerJoinMask & mask ) return false; + + m_localPlayerJoinMask |= mask; + + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_room; + reqParam.memberId = m_localMemberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); + + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL) ) + { + return false; + } + + UpdateRemotePlay(); + + // Create the client's end of the rudp connections... note that m_roomSyncData.players[0].m_roomMemberId is always be the host's room member id. + bool rudpOk = CreateRudpConnections(m_room, m_roomSyncData.players[0].m_roomMemberId, mask, m_localMemberId ); + + if( rudpOk ) + { + bool ret = CreateVoiceRudpConnections( m_room, m_roomSyncData.players[0].m_roomMemberId, mask); + assert(ret); + return true; + } + else + { + m_localPlayerJoinMask &= (~mask); + return false; + } + } +} + +bool SQRNetworkManager_Orbis::RemoveLocalPlayerByUserIndex(int idx) +{ + if( m_isHosting ) + { + EnterCriticalSection(&m_csRoomSyncData); + + int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( ( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) && + ( m_roomSyncData.players[i].m_localIdx == idx ) ) + { + // Shuffle all remaining entries up... + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); + for( int j = i; j < m_roomSyncData.getPlayerCount(); j++ ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j+1]; + } + + // Zero last element that isn't part of the currently sized array anymore + memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); + + // And do any adjusting necessary to the mappings from this room data, to the SQRNetworkPlayers. + // This will also delete the SQRNetworkPlayer and do all the callbacks that requires etc. + MapRoomSlotPlayers(roomSlotPlayerCount); + m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; + + // Sync this back out to our networked clients... + SyncRoomData(); + + SonyVoiceChat_Orbis::disconnectLocalPlayer(idx); + + LeaveCriticalSection(&m_csRoomSyncData); + return true; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + + UpdateRemotePlay(); + + return false; + } + else + { + // Remove the requested player from the mask of local players currently in the game, and update this data - this + // will also then resync with the server which can respond appropriately + int mask = 1 << idx; + if( ( m_localPlayerJoinMask & mask ) == 0 ) return false; + + m_localPlayerJoinMask &= ~mask; + + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_room; + reqParam.memberId = m_localMemberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_setRoomMemberInternalDataRequestId ); + + if( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2) ) + { + return false; + } + + RemoveNetworkPlayers( mask ); + + UpdateRemotePlay(); + + return true; + } +} + +// Update remote play on multiplayer status +void SQRNetworkManager_Orbis::UpdateRemotePlay() +{ + int localPlayerCount = 0; + for(int i = 0; i < XUSER_MAX_COUNT; i++) + { + if(GetLocalPlayerByUserIndex(i) != NULL) localPlayerCount++; + } + InputManager.SetLocalMultiplayer(localPlayerCount > 1); +} + +extern uint8_t *mallocAndCreateUTF8ArrayFromString(int iID); + +// Bring up a Gui to send an invite so a player that the user can select. This invite will contain the room Id so that +void SQRNetworkManager_Orbis::SendInviteGUI() +{ + if(s_bInviteDialogRunning) + { + app.DebugPrintf("SendInviteGUI - Invite dialog is already running so ignoring request\n"); + return; + } + + s_bInviteDialogRunning = true; + + //Set invitation information - this is now exactly the same as the presence information that we synchronise out. + + // If we joined a game, we'll have already set s_lastPresenceSyncInfo up (whether we came in from an invite, or joining a game we discovered). If we were hosting, + // then we'll need to set this up now from the external dasta. + if( m_isHosting ) + { + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData(&s_lastPresenceSyncInfo, m_joinExtData, m_room, m_serverId); + } + + sce::Toolkit::NP::MessageData messData; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + +//#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.png" +#define SESSION_IMAGE_PATH "/app0/Orbis/session_image.jpg" + + char *subject = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_SUBJECT_MAX_18_CHARS); + char *body = (char*)mallocAndCreateUTF8ArrayFromString(IDS_INVITATION_BODY); + + messData.body.assign(body); + messData.dialogFlag = SCE_TOOLKIT_NP_DIALOG_TYPE_USER_EDITABLE; + messData.npIdsCount = 2; // TODO: Set this to the number of available slots + messData.npIds = NULL; + messData.userInfo.userId = userId; + + // Set expire to maximum + messData.expireMinutes = 0; + + // Now setting session ID + messData.npSessionId = *OrbisNPToolkit::getNPSessionID(); + + // Attachment + sce::Toolkit::NP::MessageAttachment attachment = sce::Toolkit::NP::MessageAttachment(); + attachment.setAttachmentData((SceChar8*)&s_lastPresenceSyncInfo, sizeof(PresenceSyncInfo)); // This step is important, allocates space on heap for the data + messData.attachment = attachment.getAttachmentData(); + messData.attachmentSize = attachment.getAttachmentSize(); + + messData.dataDescription.assign(body); + messData.dataName.assign(subject); + messData.iconPath.assign(SESSION_IMAGE_PATH); + + + int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_CUSTOM_DATA); +// int ret = sce::Toolkit::NP::Messaging::Interface::sendMessage(&messData, SCE_TOOLKIT_NP_MESSAGE_TYPE_INVITE); + + free(subject); + free(body); + + if(ret < SCE_TOOLKIT_NP_SUCCESS ) + { + s_bInviteDialogRunning = false; + app.DebugPrintf("Send Message failed 0x%x ...\n",ret); + assert(0); + return; + } +} + + +void SQRNetworkManager_Orbis::RecvInviteGUI() +{ + int ret = sceGameCustomDataDialogInitialize(); + if(ret != SCE_OK) + { + app.DebugPrintf("sceGameCustomDataDialogInitialize() failed. ret = 0x%x\n", ret); + } + else + { + + SceGameCustomDataDialogParam dialogParam; + SceGameCustomDataDialogDataParam dataParam; + + sceGameCustomDataDialogParamInit( &dialogParam ); + memset( &dataParam, 0x00, sizeof( SceGameCustomDataDialogDataParam ) ); + dialogParam.mode = SCE_GAME_CUSTOM_DATA_DIALOG_MODE_RECV; + dialogParam.dataParam = &dataParam; + dialogParam.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + ret = sceGameCustomDataDialogOpen( &dialogParam ); + + if( SCE_OK != ret ) + { + app.DebugPrintf("sceGameCustomDataDialogOpen() failed. ret = 0x%x\n", ret); + } + else + { + b_inviteRecvGUIRunning = true; + } + } +} + + +void SQRNetworkManager_Orbis::TickInviteGUI() +{ + + if(b_inviteRecvGUIRunning) + { + SceCommonDialogStatus status = sceGameCustomDataDialogUpdateStatus(); + + if( SCE_COMMON_DIALOG_STATUS_FINISHED == status ) + { + SceGameCustomDataDialogOnlineIdList sentOnlineIdList; + memset( &sentOnlineIdList, 0x0, sizeof(SceGameCustomDataDialogOnlineIdList)); + SceGameCustomDataDialogResult dialogResult; + memset( &dialogResult, 0x0, sizeof(SceGameCustomDataDialogResult) ); + dialogResult.sentOnlineIds = &sentOnlineIdList; + + int32_t ret = sceGameCustomDataDialogGetResult( &dialogResult ); + + if( SCE_OK != ret ) + { + app.DebugPrintf( "***** sceGameCustomDataDialogGetResult error:0x%x\n", ret); + } + sceGameCustomDataDialogClose(); + sceGameCustomDataDialogTerminate(); + b_inviteRecvGUIRunning = false; + } + } +} + +// Get the data for an invite into a statically allocated array of invites, and pass a pointer of this back up to the game. Elements in the array are used in a circular fashion, to save any issues with handling freeing of this invite data as the +// qnet equivalent of this seems to just assume that the data persists forever. +void SQRNetworkManager_Orbis::GetInviteDataAndProcess(sce::Toolkit::NP::MessageAttachment* pInvite) +{ + + app.DebugPrintf("GameCustomData attachment size : %d\n", pInvite->getAttachmentSize()); + if(pInvite->getAttachmentSize() == sizeof(m_gameBootInvite_data)) + { + memcpy(&m_gameBootInvite_data, pInvite->getAttachmentData(), sizeof(m_gameBootInvite_data)); + m_gameBootInvite = &m_gameBootInvite_data; + } +// InvitationInfoRequest requestInfo; +// sce::Toolkit::NP::Utilities::Future< NpSessionInvitationInfo > inviteInfo; +// +// requestInfo.invitationId = pInvite->invitationId; +// requestInfo.userInfo.npId = pInvite->onlineId; +// int err = sce::Toolkit::NP::Sessions::Interface::getInvitationInfo(&requestInfo, &inviteInfo, false); +// if(err == SCE_OK) +// { +// if(inviteInfo.hasResult()) +// { +// inviteInfo.get()-> +// } +// else if(inviteInfo.hasError()) +// { +// app.DebugPrintf("inviteInfo.getError() 0x%08x", inviteInfo.getError()); +// } +// } +// else +// { +// app.DebugPrintf("getInvitationInfo error 0x%08x", err); +// } +// +// +// +// INVITE_INFO *invite = &m_inviteReceived[m_inviteIndex]; +// m_inviteIndex = ( m_inviteIndex + 1 ) % MAX_SIMULTANEOUS_INVITES; +// size_t dataSize = sizeof(INVITE_INFO); +// int ret = sceNpBasicRecvMessageAttachmentLoad(id, invite, &dataSize); +// +// // If we fail ( which we might do if we aren't online at this point) then zero the invite information and we'll try and get it later after (possibly) signing in +// if( ret != 0 ) +// { +// memset(invite, 0, sizeof( INVITE_INFO ) ); +// s_lastInviteIdToRetry = id; +// } +// +// m_gameBootInvite = invite; +} + +// bool SQRNetworkManager_Orbis::UpdateInviteData(SQRNetworkManager_Orbis::PresenceSyncInfo *invite) +// { +// // size_t dataSize = sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo); +// // int ret = sceNpBasicRecvMessageAttachmentLoad(s_lastInviteIdToRetry, invite, &dataSize); +// // return (ret == 0); +// } + +// This method is a helper used in MapRoomSlotPlayers - tries to find a player that matches: +// (1) the playerType +// (2) if playerType is remote, memberId +// (3) localPlayerIdx +// The reason we don't care about memberid when the player isn't remote is that it doesn't matter (since we know the player is either on this machine, or it is the host and there's only one of those), +// and there's a period when starting up the host game where it doesn't accurately know the memberId for its own local players +void SQRNetworkManager_Orbis::FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId) +{ + for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) + { + if( ((*it)->m_type == playerType ) && ( (*it)->m_localPlayerIdx == localPlayerIdx ) ) + { + if( ( playerType != SQRNetworkPlayer::SNP_TYPE_REMOTE ) || ( (*it)->m_roomMemberId == memberId ) ) + { + SQRNetworkPlayer *player = *it; + m_vecTempPlayers.erase(it); + m_aRoomSlotPlayers[ slot ] = player; + return; + } + } + } + // Create the player - non-network players can be considered complete as soon as we create them as we aren't waiting on their network connections becoming complete, so can flag them as such and notify via callback + PlayerUID *pUID = NULL; + PlayerUID localUID; + if( ( playerType == SQRNetworkPlayer::SNP_TYPE_LOCAL ) || + (m_isHosting && ( playerType == SQRNetworkPlayer::SNP_TYPE_HOST )) ) + { + // Local players can establish their UID at this point + ProfileManager.GetXUID(localPlayerIdx,&localUID,true); + pUID = &localUID; + } + SQRNetworkPlayer *player = new SQRNetworkPlayer(this, (SQRNetworkPlayer::eSQRNetworkPlayerType)playerType, m_isHosting, memberId, localPlayerIdx, 0, pUID ); + // For offline games, set name directly from gamertag as the PlayerUID will be full of zeroes. + if( m_offlineGame ) + { + player->SetName(ProfileManager.GetGamertag(localPlayerIdx)); + } + NonNetworkPlayerComplete( player, smallId); + m_aRoomSlotPlayers[ slot ] = player; + HandlePlayerJoined( player ); +} + +// For data sending on the local machine, used to send between host and localplayers on the host +void SQRNetworkManager_Orbis::LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize) +{ + assert(m_isHosting); + if(m_listener) + { + m_listener->HandleDataReceived( playerFrom, playerTo, (unsigned char *)data, dataSize ); + } +} + +int SQRNetworkManager_Orbis::GetSessionIndex(SQRNetworkPlayer *player) +{ + int roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + for( int i = 0; i < roomSlotPlayerCount; i++ ) + { + if( m_aRoomSlotPlayers[i] == player ) return i; + } + return 0; +} + +// Updates m_aRoomSlotPlayers, based on what is in m_roomSyncData. This needs to be updated when room members join & leave, and when any SQRNetworkPlayer is created externally that this should be mapping to +void SQRNetworkManager_Orbis::MapRoomSlotPlayers(int roomSlotPlayerCount/*=-1*/) +{ + EnterCriticalSection(&m_csRoomSyncData); + + // If we pass an explicit roomSlotPlayerCount, it is because we are removing a player, and this is the count of slots that there were *before* the removal. + bool zeroLastSlot = false; + if( roomSlotPlayerCount == -1 ) + { + roomSlotPlayerCount = m_roomSyncData.getPlayerCount(); + } + else + { + zeroLastSlot = true; + } + + if( m_isHosting ) + { + for( int i = 0; i < roomSlotPlayerCount; i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + // On host, remote players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. + // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer + if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_REMOTE ) + { + m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); + m_aRoomSlotPlayers[i] = NULL; + } + } + } + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData + } + else + { + if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) + { + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_LOCAL, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); // On host, UIDs flow from player data -> m_roomSyncData + } + else + { + m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); + // If we're the host, then we allocated the small id so can flag now if we've got a player to flag... + if( m_aRoomSlotPlayers[i] ) + { + NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); + } + } + } + } + + if( zeroLastSlot ) + { + if( roomSlotPlayerCount ) + { + m_aRoomSlotPlayers[ roomSlotPlayerCount - 1 ] = 0; + } + } + + // Also update the externally visible room data for the current slots + if (m_listener ) + { + m_listener->HandleResyncPlayerRequest(m_aRoomSlotPlayers); + } + } + else + { + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + // On clients, local players are created and destroyed by the Rudp connections being established and removed, so don't go deleting them here. Other types are managed by this mapping. + // Note that m_vecTempPlayers is used as a pool of players to consider by FindOrCreateNonNetworkPlayer + if( m_aRoomSlotPlayers[i]->m_type != SQRNetworkPlayer::SNP_TYPE_LOCAL ) + { + m_vecTempPlayers.push_back(m_aRoomSlotPlayers[i]); + m_aRoomSlotPlayers[i] = NULL; + } + } + } + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_HOST, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data + } + else + { + if( m_roomSyncData.players[i].m_roomMemberId == m_localMemberId ) + { + // This player is local to this machine - don't bother setting UID from sync data, as it will already have been set accurately when we (locally) made this player + m_aRoomSlotPlayers[i] = GetPlayerFromRoomMemberAndLocalIdx( m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx ); + // If we've got the room sync data back from the server, then we've got our smallId. Set flag for this. + if( m_aRoomSlotPlayers[i] ) + { + NetworkPlayerSmallIdAllocated(m_aRoomSlotPlayers[i], m_roomSyncData.players[i].m_smallId); + } + } + else + { + FindOrCreateNonNetworkPlayer( i, SQRNetworkPlayer::SNP_TYPE_REMOTE, m_roomSyncData.players[i].m_roomMemberId, m_roomSyncData.players[i].m_localIdx, m_roomSyncData.players[i].m_smallId); + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); // On client, UIDs flow from m_roomSyncData->player data + } + } + } + } + // Clear up any non-network players that are no longer required - this would be a good point to notify of players leaving when we support that + // FindOrCreateNonNetworkPlayer will have pulled any players that we Do need out of m_vecTempPlayers, so the ones that are remaining are no longer in the game + for(AUTO_VAR(it, m_vecTempPlayers.begin()); it != m_vecTempPlayers.end(); it++ ) + { + if( m_listener ) + { + m_listener->HandlePlayerLeaving(*it); + } + delete (*it); + } + m_vecTempPlayers.clear(); + + LeaveCriticalSection(&m_csRoomSyncData); +} + +// On host, update the room sync data with UIDs that are in the players +void SQRNetworkManager_Orbis::UpdateRoomSyncUIDsFromPlayers() +{ + EnterCriticalSection(&m_csRoomSyncData); + if( m_isHosting ) + { + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + m_roomSyncData.players[i].m_UID = m_aRoomSlotPlayers[i]->GetUID(); + } + } + } + + LeaveCriticalSection(&m_csRoomSyncData); +} + +// On the client, move UIDs from the room sync data out to the players. +void SQRNetworkManager_Orbis::UpdatePlayersFromRoomSyncUIDs() +{ + EnterCriticalSection(&m_csRoomSyncData); + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + if( i == 0 ) + { + // Special case - slot 0 is always the host + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); + } + else + { + // Don't sync local players as we already set those up with their UID in the first place... + if( m_roomSyncData.players[i].m_roomMemberId != m_localMemberId ) + { + m_aRoomSlotPlayers[i]->SetUID(m_roomSyncData.players[i].m_UID); + } + } + } + } + LeaveCriticalSection(&m_csRoomSyncData); +} + +// Host only - add remote players to our internal storage of player slots, and synchronise this with other room members. +bool SQRNetworkManager_Orbis::AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull/*==NULL*/ ) +{ + assert( m_isHosting ); + + EnterCriticalSection(&m_csRoomSyncData); + + // Establish whether we have enough room to add the players + int addCount = 0; + for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + addCount++; + } + } + + if( ( m_roomSyncData.getPlayerCount() + addCount ) > MAX_ONLINE_PLAYER_COUNT ) + { + if( isFull ) + { + *isFull = true; + } + LeaveCriticalSection(&m_csRoomSyncData); + return false; + } + + // We want to keep all players from a particular machine together, so search through the room sync data to see if we can find + // any pre-existing players from this machine. + int firstIdx = -1; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId == memberId ) + { + firstIdx = i; + break; + } + } + + // We'll just be inserting at the end unless we've got a pre-existing player to insert after. Even then there might be no following + // players. + int insertIdx = m_roomSyncData.getPlayerCount(); + if( firstIdx > -1 ) + { + for( int i = firstIdx; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId != memberId ) + { + insertIdx = i; + break; + } + } + } + + // Add all remote players determined from the player mask to our own slots of active players + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playerMask & ( 1 << i ) ) + { + // Shift any following players along... + for( int j = m_roomSyncData.getPlayerCount(); j > insertIdx; j-- ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j-1]; + } + PlayerSyncData *player = &m_roomSyncData.players[ insertIdx ]; + player->m_smallId = m_currentSmallId++; + player->m_roomMemberId = memberId; + player->m_localIdx = i; + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()+1); + insertIdx++; + } + } + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + // And then synchronise this out to all other machines + SyncRoomData(); + + LeaveCriticalSection(&m_csRoomSyncData); + + return true; +} + +// Host only - remove all remote players belonging to the supplied memberId, and in the supplied mask, and synchronise this with other room members +void SQRNetworkManager_Orbis::RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask ) +{ + assert( m_isHosting ); + EnterCriticalSection(&m_csRoomSyncData); + + // Remove any applicable players, keeping remaining players in order + for( int i = 0; i < m_roomSyncData.getPlayerCount(); ) + { + if( ( m_roomSyncData.players[ i ].m_roomMemberId == memberId ) && ( ( 1 << m_roomSyncData.players[ i ].m_localIdx ) & mask ) ) + { + SQRNetworkPlayer *player = GetPlayerFromRoomMemberAndLocalIdx( memberId, m_roomSyncData.players[ i ].m_localIdx ); + if( player ) + { + // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) + int ctx = player->m_rudpCtx; + int err = sceRudpTerminate( ctx ); + assert(err == SCE_OK); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + g_numRUDPContextsBound--; + if( m_listener ) + { + m_listener->HandlePlayerLeaving(player); + } + // Delete the player itself and the mapping from context to player map as this context is no longer valid + delete player; + m_RudpCtxToPlayerMap.erase(ctx); + + removePlayerFromVoiceChat(player); + } + m_roomSyncData.setPlayerCount(m_roomSyncData.getPlayerCount()-1); + // Shuffled entries up into the space that we have just created + for( int j = i ; j < m_roomSyncData.getPlayerCount(); j++ ) + { + m_roomSyncData.players[j] = m_roomSyncData.players[j + 1]; + m_aRoomSlotPlayers[j] = m_aRoomSlotPlayers[j + 1]; + } + // Zero last element, that isn't part of the currently sized array anymore + memset(&m_roomSyncData.players[m_roomSyncData.getPlayerCount()],0,sizeof(PlayerSyncData)); + m_aRoomSlotPlayers[m_roomSyncData.getPlayerCount()] = NULL; + } + else + { + i++; + } + } + LeaveCriticalSection(&m_csRoomSyncData); + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + + // And then synchronise this out to all other machines + SyncRoomData(); + +// if(GetOnlinePlayerCount() == 0) +// SonyVoiceChat::shutdown(); +} + +// Client only - remove all network players matching the supplied mask +void SQRNetworkManager_Orbis::RemoveNetworkPlayers( int mask ) +{ + assert( !m_isHosting ); + + for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); ) + { + SQRNetworkPlayer *player = it->second; + if( (player->m_roomMemberId == m_localMemberId ) && ( ( 1 << player->m_localPlayerIdx ) & mask ) ) + { + // Get Rudp context for this player, close that context down ( which will in turn close the socket if required) + int ctx = it->first; + int err = sceRudpTerminate( ctx ); + g_numRUDPContextsBound--; + assert(err == SCE_OK); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + if( m_listener ) + { + m_listener->HandlePlayerLeaving(player); + } + // Delete any reference to this player from the player mappings + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_aRoomSlotPlayers[i] == player ) + { + m_aRoomSlotPlayers[i] = NULL; + } + } + // And delete the reference from the ctx->player map + it = m_RudpCtxToPlayerMap.erase(it); + + removePlayerFromVoiceChat(player); + + // Delete the player itself and the mapping from context to player map as this context is no longer valid + delete player; + } + else + { + it++; + } + } + +} + +// Host only - update the memberId of the local players, and synchronise with other room members +void SQRNetworkManager_Orbis::SetLocalPlayersAndSync() +{ + assert( m_isHosting ); + for( int i = 0; i < m_localPlayerCount; i++ ) + { + m_roomSyncData.players[i].m_roomMemberId = m_localMemberId; + } + + // Update mapping from the room slot players to SQRNetworkPlayer instances + MapRoomSlotPlayers(); + + // And then synchronise this out to all other machines + SyncRoomData(); + +} + +// Host only - sync the room data with other machines +void SQRNetworkManager_Orbis::SyncRoomData() +{ + if( m_offlineGame ) return; + + UpdateRoomSyncUIDsFromPlayers(); + + SceNpMatching2SetRoomDataInternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam) ); + reqParam.roomId = m_room; + SceNpMatching2BinAttr roomBinAttr; + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; + roomBinAttr.ptr = &m_roomSyncData; + roomBinAttr.size = sizeof( m_roomSyncData ); + reqParam.roomBinAttrInternalNum = 1; + reqParam.roomBinAttrInternal = &roomBinAttr; + sceNpMatching2SetRoomDataInternal ( m_matchingContext, &reqParam, NULL, &m_setRoomDataRequestId ); +} + +// Check if the matching context is valid, and if not attempt to create one. If to do this requires starting an asynchronous process, then sets the internal state to the state passed in +// before doing this. +// Returns true on success. +bool SQRNetworkManager_Orbis::GetMatchingContext(eSQRNetworkManagerInternalState asyncState) +{ + if( m_matchingContextValid ) return true; + + int ret = 0; + if( !m_matching2initialised) + { + + SceNpMatching2InitializeParameter param; + memset(¶m, 0, sizeof(param)); + param.size = sizeof(param); + ret = sceNpMatching2Initialize(¶m); + } + if( (ret < 0) && (ret != SCE_NP_MATCHING2_ERROR_ALREADY_INITIALIZED) ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2Init2 failed with code 0x%08x\n", ret); + return false; + } + m_matching2initialised = true; + + // Get NP ID of the signed-in user + SceNpId npId; + /*ret = */ProfileManager.GetSceNpId(ProfileManager.GetPrimaryPad(), &npId); + + + // Create context + SceNpServiceLabel serviceLabel = 0; + SceNpMatching2CreateContextParam param; + memset(¶m, 0, sizeof(param)); + param.npId = &npId; + param.serviceLabel = serviceLabel; + param.size = sizeof(param); + ret = sceNpMatching2CreateContext(¶m, &m_matchingContext); + if( ret < 0 ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2CreateContext failed with code 0x%08x\n", ret); + return false; + } + if( ret < 0 ) return false; + + if( !RegisterCallbacks() ) + { + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - RegisterCallbacks failed\n"); + return false; + } + + // Set internal state & kick off async process that will actually start the context. + SetState(asyncState); + ret = sceNpMatching2ContextStart(m_matchingContext, (10*1000*1000)); + if( ret < 0 ) + { + // Put state back so that the caller isn't expecting a callback from sceNpMatching2ContextStartAsync completing to happen + SetState(SNM_INT_STATE_IDLE); + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - sceNpMatching2ContextStartAsync failed with code 0x%08x\n", ret); + return false; + } + + app.DebugPrintf("SQRNetworkManager::GetMatchingContext - matching context is now valid\n"); + m_matchingContextValid = true; + return true; +} + +// Starts the process of obtaining a server context. This is an asynchronous operation, at the end of which (if successful), we'll be creating +// a room. General procedure followed here is as suggested by Sony - we get a list of servers, then pick a random one, and see if it is available. +// If not we just cycle round trying other random ones until we either find an available one or fail. +bool SQRNetworkManager_Orbis::GetServerContext() +{ + assert(m_state == SNM_INT_STATE_IDLE); + assert(m_serverContextValid == false); + + // Check that the matching context is valid & recreate if necessary + if( !GetMatchingContext(SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT) ) return false; + // If this caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes + // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) + if( m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) return true; + + return GetServerContext2(); +} + +// Code split out from previous method, so we can also call from creating matching context if required +bool SQRNetworkManager_Orbis::GetServerContext2() +{ + + m_aServerId = (SceNpMatching2ServerId *)realloc( m_aServerId, sizeof(SceNpMatching2ServerId) * 1 ); + int err = sceNpMatching2GetServerId(m_matchingContext, m_aServerId); + if(err != SCE_OK) + { + m_serverCount = 0; + assert(0); + } + m_serverCount = 1; + + SetState(SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER); + return SelectRandomServer(); +} + +// Overloaded method for (as before) obtaining a server context. This version is so that can also get a server context for a specific server rather than a random one, +// using mainly the same code by making a single element list. This is used when joining an existing room. +bool SQRNetworkManager_Orbis::GetServerContext(SceNpMatching2ServerId serverId) +{ + assert(m_state == SNM_INT_STATE_IDLE); + assert(m_serverContextValid == false); + + // Check that the matching context is valid & recreate if necessary + if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) + { + app.DebugPrintf("SQRNetworkManager::GetServerContext - Failed due to no matching context\n"); + return false; + } + + // 4J Stu - If this state is set, then we have successfully created a new context but it won't have started yet + // Therefore the sceNpMatching2GetServerIdListLocal call will fail. If we just skip this check everything should be good. +// if( m_state != SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) +// { +// // Get list of server IDs of servers allocated to the application. We don't actually need to do this, but it is as good a way as any to try a matching2 service and check that +// // the context *really* is valid. +// int serverCount = sceNpMatching2GetServerIdListLocal( m_matchingContext, NULL, 0 ); +// // If an error is returned here, we need to destroy and recerate our server - if this goes ok we should come back through this path again +// if( ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_UNAVAILABLE ) || // This error has been seen (occasionally) in a normal working environment +// ( serverCount == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) ) // Also checking for this as a means of simulating the previous error +// { +// sceNpMatching2DestroyContext(m_matchingContext); +// m_matchingContextValid = false; +// if( !GetMatchingContext(SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) ) return false; +// } +// } + m_serverCount = 1; + m_totalServerCount = m_serverCount; + m_aServerId = (SceNpMatching2ServerId *)realloc(m_aServerId, sizeof(SceNpMatching2ServerId) * m_serverCount ); + m_aServerId[0] = serverId; + + // If one of the previous GetMatchingContext calls caused an async thing to be started up, then we've done as much as we can here - the rest of the code will happen when the async matching 2 context starting completes + // ( event SCE_NP_MATCHING2_CONTEXT_EVENT_Start is received ) + if( m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) return true; + + SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + return SelectRandomServer(); +} + +// Tick to update the search for a server which is available, for the creation of a server context. +void SQRNetworkManager_Orbis::ServerContextTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER: + case SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER: + break; + case SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR: + case SNM_INT_STATE_JOINING_SERVER_SEARCH_SERVER_ERROR: + // Attempt to keep searching if a single server failed + SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER:SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + if(!SelectRandomServer()) + { + SetState((m_state==SNM_INT_STATE_HOSTING_SERVER_SEARCH_SERVER_ERROR)?SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED:SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); + } + break; + case SNM_INT_STATE_HOSTING_SERVER_FOUND: + m_serverContextValid = true; + ServerContextValid_CreateRoom(); + break; + + case SNM_INT_STATE_JOINING_SERVER_FOUND: + m_serverContextValid = true; + ServerContextValid_JoinRoom(); + break; + default: + break; + } +} + +// Tick the process of creating a room. +void SQRNetworkManager_Orbis::RoomCreateTick() +{ + switch( m_state ) + { + case SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD: + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND: + { + SceNpMatching2CreateJoinRoomRequest reqParam; + SceNpMatching2SignalingOptParam optSignalingParam; + SceNpMatching2BinAttr roomBinAttrExt; + SceNpMatching2BinAttr roomBinAttr; + memset(&reqParam, 0, sizeof(reqParam)); + memset(&optSignalingParam, 0, sizeof( optSignalingParam) ); + memset(&roomBinAttr, 0, sizeof(roomBinAttr)); + memset(&roomBinAttrExt, 0, sizeof(roomBinAttrExt)); + + reqParam.worldId = m_worldId; + reqParam.flagAttr = SCE_NP_MATCHING2_ROOM_FLAG_ATTR_NAT_TYPE_RESTRICTION; + reqParam.sigOptParam = &optSignalingParam; + reqParam.maxSlot = MAX_ONLINE_PLAYER_COUNT; + reqParam.roomBinAttrInternalNum = 1; + reqParam.roomBinAttrInternal = &roomBinAttr; + reqParam.roomBinAttrExternalNum = 1; + reqParam.roomBinAttrExternal = &roomBinAttrExt; + + roomBinAttr.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID; + roomBinAttr.ptr = &m_roomSyncData; + roomBinAttr.size = sizeof( m_roomSyncData ); + + roomBinAttrExt.id = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + roomBinAttrExt.ptr = m_joinExtData; + roomBinAttrExt.size = m_joinExtDataSize; + + optSignalingParam.type = SCE_NP_MATCHING2_SIGNALING_TYPE_MESH; + optSignalingParam.hubMemberId = 0; // Room owner is the hub of the star + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM); + app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room start\n"); + s_roomStartTime = System::currentTimeMillis(); + int ret = sceNpMatching2CreateJoinRoom( m_matchingContext, &reqParam, NULL, &m_createRoomRequestId ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_JOIN_ROOM) ) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + } + } + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM: + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS: + SetState(SNM_INT_STATE_HOSTING_WAITING_TO_PLAY); + + // Now we know the local member id we can update our local players + SetLocalPlayersAndSync(); + break; + case SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED: + break; + default: + break; + } +} + +// For a player using the network to communicate, flag as having its connection complete. This wraps the player's own functionality, so that we can determine if this +// call is transitioning us from not ready to ready, and call a registered callback. +void SQRNetworkManager_Orbis::NetworkPlayerConnectionComplete(SQRNetworkPlayer *player) +{ + EnterCriticalSection(&m_csPlayerState); + bool wasReady = player->IsReady(); + bool wasClientReady = player->HasConnectionAndSmallId(); + player->ConnectionComplete(); + bool isReady = player->IsReady(); + bool isClientReady = player->HasConnectionAndSmallId(); + if( !m_isHosting ) + { + // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end + if( ( !wasClientReady ) && ( isClientReady ) ) + { + player->ConfirmReady(); + isReady = true; + } + } + LeaveCriticalSection(&m_csPlayerState); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// For a player using the network to communicate, set its small id, thereby flagging it as having one allocated +void SQRNetworkManager_Orbis::NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId) +{ + EnterCriticalSection(&m_csPlayerState); + bool wasReady = player->IsReady(); + bool wasClientReady = player->HasConnectionAndSmallId(); + player->SmallIdAllocated(smallId); + bool isReady = player->IsReady(); + bool isClientReady = player->HasConnectionAndSmallId(); + + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Small ID allocated\n"); + if( !m_isHosting ) + { + // For clients, if we are ready (up the the point of having received our small id) then confirm to the host that this is the case, which makes us now fully ready at this end + if( ( !wasClientReady ) && ( isClientReady ) ) + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Confirm ready\n"); + player->ConfirmReady(); + isReady = true; + } + } + LeaveCriticalSection(&m_csPlayerState); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// On host, for a player using the network to communicate, confirm that its small id has now been received back +void SQRNetworkManager_Orbis::NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player, void *data) +{ + EnterCriticalSection(&m_csPlayerState); + SQRNetworkPlayer::InitSendData *ISD = (SQRNetworkPlayer::InitSendData *)data; + bool wasReady = player->IsReady(); + player->InitialDataReceived(ISD); + bool isReady = player->IsReady(); + LeaveCriticalSection(&m_csPlayerState); + // Sync room data back out as we've updated a player's UID here + SyncRoomData(); + + if( ( !wasReady ) && ( isReady ) ) + { + HandlePlayerJoined( player ); + } +} + +// For non-network players, flag that it is complete/ready, and assign its small id. We don't want to call any callbacks for these, as that can be explicitly done when local players are added. +// Also, we dynamically destroy & recreate local players quite a lot when remapping player slots which would create a lot of messages we don't want. +void SQRNetworkManager_Orbis::NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId) +{ + player->ConnectionComplete(); + player->SmallIdAllocated(smallId); +} + +void SQRNetworkManager_Orbis::HandlePlayerJoined(SQRNetworkPlayer *player) +{ + if( m_listener ) + { + m_listener->HandlePlayerJoined( player ); + } + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> HandlePlayerJoined\n"); + + // On client, keep a count of how many local players we have told the game about. We can only transition to telling the game that we are playing once the room is set up And all the local players are valid to use. + if( !m_isHosting ) + { + if( player->IsLocal() ) + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LocalPlayerJoined++\n"); + + m_localPlayerJoined++; + } + } +} + +// Selects a random server from the current list, removes that server so it won't be searched for again, and then kick off an attempt to find out if that particular server is available. +bool SQRNetworkManager_Orbis::SelectRandomServer() +{ + assert( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ); + + if( m_serverCount == 0 ) + { + SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_SEARCH_FAILED : SNM_INT_STATE_JOINING_SERVER_SEARCH_FAILED); + app.DebugPrintf("SQRNetworkManager::SelectRandomServer - Server count is 0\n"); + return false; + } + + // not really selecting a random server, as we've already been allocated one, but calling this to match PS3 + int serverIdx; + serverIdx = 0; + m_serverCount--; + m_aServerId[serverIdx] = m_aServerId[m_serverCount]; + + // This server is available + SetState((m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) ? SNM_INT_STATE_HOSTING_SERVER_FOUND : SNM_INT_STATE_JOINING_SERVER_FOUND); + m_serverId = m_aServerId[serverIdx]; + + return true; +} + +// Delete the current server context. Should be called when finished with the current host or client game session. +void SQRNetworkManager_Orbis::DeleteServerContext() +{ + // No server context on PS4, so we just set the state, and then we'll check all the UDP connections have shutdown before setting to idle + if( m_serverContextValid ) + { + m_serverContextValid = false; + SetState(SNM_INT_STATE_SERVER_DELETING_CONTEXT); + } +} + +// Creates a set of Rudp connections by the "active open" method. This requires that both ends of the connection call cellRudpInitiate to fully create a connection. We +// create one connection per local play on any remote machine. +// +// peerMemberId is the room member Id of the remote end of the connection +// playersMemberId is the room member Id that the players belong to +// ie for the host (when matching incoming connections), these will be the same thing... and for the client, peerMemberId will be the host, whereas playersMemberId will be itself + + +std::string getIPAddressString(SceNetInAddr add) +{ + char str[32]; + unsigned char *vals = (unsigned char*)&add.s_addr; + sprintf(str, "%d.%d.%d.%d", (int)vals[0], (int)vals[1], (int)vals[2], (int)vals[3]); + return std::string(str); +} + +bool SQRNetworkManager_Orbis::CreateSocket() +{ + // First get details of the UDPP2P connection that has been established + int connStatus; + SceNetSockaddrIn sinp2pLocal, sinp2pPeer; + SceNpMatching2SignalingNetInfo netInfo; + + // Local end first... + memset(&sinp2pLocal, 0, sizeof(sinp2pLocal)); + memset(&netInfo, 0 , sizeof(netInfo)); + netInfo.size = sizeof(netInfo); + int ret = sceNpMatching2SignalingGetLocalNetInfo(&netInfo); + if( ret < 0 ) return false; + sinp2pLocal.sin_len = sizeof(sinp2pLocal); + sinp2pLocal.sin_family = AF_INET; + sinp2pLocal.sin_port = sceNetHtons(SCE_NP_PORT); + sinp2pLocal.sin_addr = netInfo.localAddr; + + + // Set vport for both ends of connection + sinp2pLocal.sin_vport = sceNetHtons(1); + + // Create socket & bind + ret = sceNetSocket("rupdSocket", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM_P2P, 0); + assert(ret >= 0); + m_soc = ret; + int optval = 1; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USECRYPTO, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_0) ) return false; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_USESIGNATURE, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_1) ) return false; + ret = sceNetSetsockopt(m_soc, SCE_NET_SOL_SOCKET, SCE_NET_SO_NBIO, &optval, sizeof(optval)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SETSOCKOPT_2) ) return false; + + ret = sceNetBind(m_soc, (SceNetSockaddr *)&sinp2pLocal, sizeof(sinp2pLocal)); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_SOCK_BIND) ) return false; + return true; + +} + + +bool SQRNetworkManager_Orbis::CreateVoiceRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask) +{ + SceNetSockaddrIn sinp2pPeer; + SceNpMatching2SignalingNetInfo netInfo; + int connStatus; + + memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); + sinp2pPeer.sin_len = sizeof(sinp2pPeer); + sinp2pPeer.sin_family = AF_INET; + int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); + sinp2pPeer.sin_vport = sceNetHtons(1); + + + ret = 0; + // Create socket & bind, if we don't already have one + if( m_soc == -1 ) + { + if(CreateSocket() == false) + return false; + } + + // create this connection if we don't have it already + SQRVoiceConnection* pConnection = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); + if(pConnection == NULL) + { + + // Create an Rudp context for the voice connection, this will happen regardless of whether the peer is client or host + int rudpCtx; + ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); + if(ret < 0){ app.DebugPrintf("sceRudpCreateContext failed : 0x%08x\n", ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); + + // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. + ret = sceRudpBind( rudpCtx, m_soc , 5, SCE_RUDP_MUXMODE_P2P ); + if(ret < 0){ app.DebugPrintf("sceRudpBind %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; + g_numRUDPContextsBound++; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); + + ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); + if(ret < 0){ app.DebugPrintf("sceRudpInitiate %s failed : 0x%08x\n", getIPAddressString(sinp2pPeer.sin_addr).c_str(), ret); assert(0); } + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); + + app.DebugPrintf("-----------------------------\n"); + app.DebugPrintf("Voice rudp context created %d connected to %s\n", rudpCtx, getIPAddressString(sinp2pPeer.sin_addr).c_str()); + app.DebugPrintf("-----------------------------\n"); + + pConnection = SonyVoiceChat_Orbis::addRemoteConnection(rudpCtx, peerMemberId); + } + + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + bool bMaskVal = ( playerMask & ( 1 << i ) ); + + if(bMaskVal || GetLocalPlayerByUserIndex(i)) + SonyVoiceChat_Orbis::connectPlayer(pConnection, i); + } + return true; +} + + + +bool SQRNetworkManager_Orbis::CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersMemberId) +{ + // First get details of the UDPP2P connection that has been established + int connStatus; + SceNetSockaddrIn sinp2pPeer; + + // get the peer + memset(&sinp2pPeer, 0, sizeof(sinp2pPeer)); + sinp2pPeer.sin_len = sizeof(sinp2pPeer); + sinp2pPeer.sin_family = AF_INET; + + int ret = sceNpMatching2SignalingGetConnectionStatus(m_matchingContext, roomId, peerMemberId, &connStatus, &sinp2pPeer.sin_addr, &sinp2pPeer.sin_port); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2SignalingGetConnectionStatus returned 0x%x, connStatus %d peer add:%s peer port:0x%x\n",ret, connStatus,getIPAddressString(sinp2pPeer.sin_addr).c_str(),sinp2pPeer.sin_port); + + // Set vport + sinp2pPeer.sin_vport = sceNetHtons(1); + + // Create socket & bind, if we don't already have one + if( m_soc == -1 ) + { + if(CreateSocket() == false) + return false; + } + + // Create an Rudp context for each local player that is required. These can be used as individual virtual connections between room members (ie consoles), which are multiplexed + // over the socket we have just made + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( ( playerMask & ( 1 << i ) ) == 0 ) continue; + + int rudpCtx; + + // Socket for the local network node created, now can create an Rupd context. + ret = sceRudpCreateContext( RudpContextCallback, this, &rudpCtx ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpCreateContext\n" ); + + if( m_isHosting ) + { + m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_REMOTE, true, playersMemberId, i, rudpCtx, NULL ); + } + else + { + // Local players can establish their UID at this point + PlayerUID localUID; + ProfileManager.GetXUID(i,&localUID,true); + + m_RudpCtxToPlayerMap[ rudpCtx ] = new SQRNetworkPlayer( this, SQRNetworkPlayer::SNP_TYPE_LOCAL, false, m_localMemberId, i, rudpCtx, &localUID ); + } + + // If we've created a player, then we want to try and patch up any connections that we should have to it + MapRoomSlotPlayers(); + + // TODO - set any non-default options for the context. By default, the context is set to have delivery critical and order critical both on + + // Bind the context to the socket we've just created, and initiate. The initiation needs to happen on both client & host sides of the connection to complete. + ret = sceRudpBind( rudpCtx, m_soc , 1 + i, SCE_RUDP_MUXMODE_P2P ); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_BIND) ) return false; + g_numRUDPContextsBound++; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpBind\n" ); + + + ret = sceRudpInitiate( rudpCtx, (SceNetSockaddr*)&sinp2pPeer, sizeof(sinp2pPeer), 0); + if ( ( ret < 0 ) || ForceErrorPoint(SNM_FORCE_ERROR_RUDP_INIT2) ) return false; + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpInitiate\n" ); + } + return true; +} + + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRudpCtx(int rudpCtx) +{ + AUTO_VAR(it,m_RudpCtxToPlayerMap.find(rudpCtx)); + if( it != m_RudpCtxToPlayerMap.end() ) + { + return it->second; + } + return NULL; +} + + + +SQRNetworkPlayer *SQRNetworkManager_Orbis::GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx) +{ + for(AUTO_VAR(it, m_RudpCtxToPlayerMap.begin()); it != m_RudpCtxToPlayerMap.end(); it++ ) + { + if( (it->second->m_roomMemberId == roomMember ) && ( it->second->m_localPlayerIdx == localIdx ) ) + { + return it->second; + } + } + return NULL; +} + + +// This is called as part of the general initialisation of the network manager, to register any callbacks that the sony libraries require. +// Returns true if all were registered successfully. +bool SQRNetworkManager_Orbis::RegisterCallbacks() +{ + // Register RUDP event handler + int ret = sceRudpSetEventHandler(RudpEventCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - cellRudpSetEventHandler failed with code 0x%08x\n", ret); + return false; + } + + // Register the context callback function + ret = sceNpMatching2RegisterContextCallback(ContextCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterContextCallback failed with code 0x%08x\n", ret); + return false; + } + + // Register the default request callback & parameters + SceNpMatching2RequestOptParam optParam; + + memset(&optParam, 0, sizeof(optParam)); + optParam.cbFunc = DefaultRequestCallback; + optParam.cbFuncArg = this; + optParam.timeout = (30 * 1000 * 1000); + optParam.appReqId = 0; + + ret = sceNpMatching2SetDefaultRequestOptParam(m_matchingContext, &optParam); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2SetDefaultRequestOptParam failed with code 0x%08x\n", ret); + return false; + } + + // Register signalling callback + ret = sceNpMatching2RegisterSignalingCallback(m_matchingContext, SignallingCallback, this); + if (ret < 0) + { + return false; + } + + // Register room event callback + ret = sceNpMatching2RegisterRoomEventCallback(m_matchingContext, RoomEventCallback, this); + if (ret < 0) + { + app.DebugPrintf("SQRNetworkManager::RegisterCallbacks - sceNpMatching2RegisterRoomEventCallback failed with code 0x%08x\n", ret); + return false; + } + + return true; +} + +extern bool g_bBootedFromInvite; + + +// This is an implementation of SceNpMatching2ContextCallback. Used to determine whether the matching 2 context is valid or not. +void SQRNetworkManager_Orbis::ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg) +{ + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback id:%d, event:%d, eventCause:%d, errorCode:0x%08x\n", id, event, eventCause, errorCode); + + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if (id != manager->m_matchingContext) + { + return; + } + + switch( event ) + { + case SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED: + if(errorCode < 0) + { + if(manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT || + manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT || + manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT) + { + // matching context failed to start (this can happen when you block the IP addresses of the matching servers on your router + // agent-0101.ww.sp-int.matching.playstation.net (198.107.157.191) + // static-resource.sp-int.community.playstation.net (203.105.77.140) + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - Error\n"); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + break; + } + } + + // Some special cases to detect when this event is coming in, in case we had to start the matching context because there wasn't a valid context when we went to get a server context. These two + // responses here complete what should then happen to get the server context in each case (for hosting or joining a game) + if( manager->m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + manager->SetState( SNM_INT_STATE_IDLE ); + manager->GetExtDataForRoom(0, NULL, NULL, NULL); + break; + } + + if( manager->m_state == SNM_INT_STATE_HOSTING_STARTING_MATCHING_CONTEXT ) + { + manager->GetServerContext2(); + break; + } + if( manager->m_state == SNM_INT_STATE_JOINING_STARTING_MATCHING_CONTEXT ) + { + manager->SetState(SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER); + manager->SelectRandomServer(); + break; + } + if ( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) + { + manager->ServerContextValid_CreateRoom(); + break; + } + // Normal handling of context starting, from standard initialisation procedure + assert( manager->m_state == SNM_INT_STATE_STARTING_CONTEXT ); + if (errorCode < 0) + { + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallback - SCE_NP_MATCHING2_CONTEXT_EVENT_STARTED failed with error 0x%08x\n", errorCode); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + } + else + { + manager->m_offlineSQR = false; + manager->SetState(SNM_INT_STATE_IDLE); + + // 4J-PB - SQRNetworkManager_PS3::AttemptPSNSignIn was causing crashes in Iggy by calling LoadMovie from a callback, so call it from the tick instead + m_bCallPSNSignInCallback=true; + app.DebugPrintf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ m_bCallPSNSignInCallback true ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + + // if(s_SignInCompleteCallbackFn) + // { + // s_SignInCompleteCallbackFn(s_SignInCompleteParam, true, 0); + // s_SignInCompleteCallbackFn = NULL; + // } + + + + // Check to see if we were booted from an invite. Only do this once, the first time we have all our networking stuff set up on boot-up + if( manager->m_doBootInviteCheck ) + { +// ORBIS_STUBBED; +// unsigned int type, attributes; +// CellGameContentSize gameSize;` +// char dirName[CELL_GAME_DIRNAME_SIZE]; +// +// if( g_bBootedFromInvite ) +// { +// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); +// manager->m_doBootInviteCheck = false; +// } + } + } + break; + case SCE_NP_MATCHING2_CONTEXT_EVENT_STOPPED: + if( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT ) + { + sceNpMatching2ContextStart(manager->m_matchingContext, (10*1000*1000)); + break; + } + assert(false); + break; + case SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER: + + app.DebugPrintf("SCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER\n"); + app.DebugPrintf("eventCause=%u, errorCode=0x%08x\n", eventCause, errorCode); + + sceNpMatching2DestroyContext(manager->m_matchingContext); + manager->m_matchingContextValid = false; + manager->m_offlineSQR = true; + + if(manager->m_state == SNM_INT_STATE_STARTING_CONTEXT) + { + // MGH - to fix potential issue with a bad state assert on loading a level + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_MATCHING2_CONTEXT_EVENT_START_OVER failed\n"); + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + } + switch(errorCode) + { + case SCE_NP_ERROR_LATEST_PATCH_PKG_EXIST: + case SCE_NP_ERROR_LATEST_PATCH_PKG_DOWNLOADED: + app.DebugPrintf("SQRNetworkManager_Orbis::ContextCallbackSCE_NP_ERROR_LATEST_PATCH_PKG_EXIST failed\n"); + // 4J-PB - need to fail the init, so the external state will be set to idle, and we can continue offline + manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); + break; + } + + break; + } +} + +// This is an implementation of SceNpMatching2RequestCallback. This callback is used by default for any matching 2 request functions. +void SQRNetworkManager_Orbis::DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg) +{ + int ret; + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if( id != manager->m_matchingContext ) return; + + // MGH - added this here for the NAT failure, should only happen for the join request, but without being able to test we can't be sure + if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) + { + app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); + } + + switch( event ) + { + // This is the response to sceNpMatching2GetWorldInfoList, which is called as part of the process to create a room (which needs a world to be created in). We aren't anticipating + // using worlds in a meaningful way so just getting the first world we find on the server here, and then advancing the state so that the tick can get on with the rest of the process. + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST: + if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) + { + // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here + break; + } + assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD ); + if( errorCode == 0 ) + { + if( data != 0 ) + { + // Currently just using first world - this may well be all that we need anyway + SceNpMatching2GetWorldInfoListResponse *pWorldList = (SceNpMatching2GetWorldInfoListResponse *)data; + if( pWorldList->worldNum >= 1 ) + { + manager->m_worldId = pWorldList->world[0].worldId; + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_WORLD_FOUND); + break; + } + } + } + // We get this error when starting a new game after a disconnect occurred in a previous game with at least one remote player. Fix by stopping/starting the matching context. + // We stop the context here, which is picked up in the callback, and started again. Then the start event is picked up and reattempts the sceNpMatching2GetWorldInfoList. + if( errorCode == SCE_NET_ERROR_RESOLVER_ENODNS ) + { + sceNpMatching2ContextStop(manager->m_matchingContext); + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_RESTART_MATCHING_CONTEXT); + break; + } + app.DebugPrintf("SCE_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST failed, errorCode 0x%x, data %d\n", errorCode, data); + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + break; + // This is the response to sceNpMatching2CreateJoinRoom, which if successful means that we are just about ready to move to an online state as host of a game. The final + // transition actually occurs in the create room tick, on detecting that the state has transitioned to SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS here. + case SCE_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM: + if( errorCode == SCE_NP_MATCHING2_ERROR_NP_SIGNED_OUT ) + { + // If we've already signed out, then we should have detected this already elsewhere and so can silently ignore any errors coming in for pending requests here + break; + } + app.DebugPrintf(CMinecraftApp::USER_RR,">> Creating room complete, time taken %d, error 0x%x\n",System::currentTimeMillis()-s_roomStartTime, errorCode); + assert( manager->m_state == SNM_INT_STATE_HOSTING_CREATE_ROOM_CREATING_ROOM ); + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2CreateJoinRoomResponse *roomData = (SceNpMatching2CreateJoinRoomResponse *)data; + manager->m_localMemberId = roomData->memberList.me->memberId; + + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SUCCESS); + manager->m_room = roomData->roomDataInternal->roomId; + break; + } + } + manager->SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + break; + // This is the response to sceNpMatching2JoinRoom, which is called as the final stage of the process started when calling the JoinRoom method. If this is successful, then + // the state can change to SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS. We can transition out of that state once we have told the application that all the local players + // have joined. + case SCE_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM: + assert( manager->m_state == SNM_INT_STATE_JOINING_JOIN_ROOM); + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2JoinRoomResponse *roomData = (SceNpMatching2JoinRoomResponse *)data; + + manager->m_localMemberId = roomData->memberList.me->memberId; + manager->m_room = roomData->roomDataInternal->roomId; +// SonyVoiceChat::init(manager); + // Copy over initial room sync data + for( int i = 0; i < roomData->roomDataInternal->roomBinAttrInternalNum; i++ ) + { + if( roomData->roomDataInternal->roomBinAttrInternal[i].data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) + { + assert( roomData->roomDataInternal->roomBinAttrInternal[i].data.size == sizeof( manager->m_roomSyncData ) ); + memcpy( &manager->m_roomSyncData, roomData->roomDataInternal[i].roomBinAttrInternal[0].data.ptr, sizeof( manager->m_roomSyncData ) ); + +// manager->UpdatePlayersFromRoomSyncUIDs(); + // Update mapping from the room slot players to SQRNetworkPlayer instances + manager->MapRoomSlotPlayers(); + break; + } + } + manager->SetState(SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS); + break; + } + } + manager->SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); + if(errorCode == SCE_NP_MATCHING2_SERVER_ERROR_ROOM_FULL) // MGH - added to fix "host has exited" error when 2 players go after the final slot + { + Minecraft::GetInstance()->connectionDisconnected(ProfileManager.GetPrimaryPad(), DisconnectPacket::eDisconnect_ServerFull); + } + break; + // This is the response to sceNpMatching2GetRoomMemberDataInternal.This only happens on the host, as a response to an incoming connection being established, when we + // kick off the request for room member internal data so that we can determine what local players that remote machine is intending to bring into the game. At this point we can + // activate the host end of each of the Rupd connection that this machine requires. We can also update our player slot data (which gets syncronised back out to other room members) at this point. + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_MEMBER_DATA_INTERNAL: + if( errorCode == 0 ) + { + + if( data != 0 ) + { + SceNpMatching2GetRoomMemberDataInternalResponse *pRoomMemberData = (SceNpMatching2GetRoomMemberDataInternalResponse *)data; + assert( pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternalNum == 1 ); + + if( manager->m_isHosting ) + { + int playerMask = *((int *)(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr)); + + bool isFull = false; + bool success1 = manager->AddRemotePlayersAndSync( pRoomMemberData->roomMemberDataInternal->memberId, playerMask, &isFull ); + bool success2; + if( success1 ) + { + success2 = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, playerMask, pRoomMemberData->roomMemberDataInternal->memberId); + if( success2 ) + { + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, 0); + assert(ret == true); + break; + } + } + // Something has gone wrong adding these players to the room - kick out the player + SceNpMatching2KickoutRoomMemberRequest reqParam; + SceNpMatching2PresenceOptionData optParam; + memset(&reqParam,0,sizeof(reqParam)); + reqParam.roomId = manager->m_room; + reqParam.target = pRoomMemberData->roomMemberDataInternal->memberId; + // Set flag to indicate whether we were kicked for being out of room or not + reqParam.optData.data[0] = isFull ? 1 : 0; + reqParam.optData.len = 1; + int ret = sceNpMatching2KickoutRoomMember(manager->m_matchingContext, &reqParam, NULL, &manager->m_kickRequestId); + app.DebugPrintf(CMinecraftApp::USER_RR,"sceNpMatching2KickoutRoomMember returns error 0x%x\n",ret); + } + else + { + if(pRoomMemberData->roomMemberDataInternal->roomMemberBinAttrInternal->data.ptr == NULL) + { + // the host doesn't send out data, so this must be the host we're connecting to + + // If we are the client, then we locally know what Rupd connections we need (from m_localPlayerJoinMask) and can kick this off. + manager->m_hostMemberId = pRoomMemberData->roomMemberDataInternal->memberId; + bool ret = manager->CreateRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask, manager->m_localMemberId); + if( ret == false ) + { + manager->DeleteServerContext(); + } + else + { + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); + assert(ret == true); + } + } + else + { + // client <-> client + bool ret = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->roomMemberDataInternal->memberId, manager->m_localPlayerJoinMask); + assert(ret == true); + } + } + + } + } + break; + case SCE_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM: + // This is the response to sceNpMatching2LeaveRoom - from the Sony docs, this doesn't ever fail so no need to do error checking here +// SonyVoiceChat::signalDisconnected(); + assert(manager->m_state == SNM_INT_STATE_LEAVING ); + manager->DeleteServerContext(); + break; + // This is the response to SceNpMatching2GetRoomDataExternalListRequest, which happens when we request the full details of a room we are interested in joining + case SCE_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_EXTERNAL_LIST: + if( errorCode == 0 ) + { + if( data != 0 ) + { + SceNpMatching2GetRoomDataExternalListResponse *pExternalData = (SceNpMatching2GetRoomDataExternalListResponse *)data; + SceNpMatching2RoomDataExternal *pRoomExtData = pExternalData->roomDataExternal; + if( pExternalData->roomDataExternalNum == 1 ) + { + if(pRoomExtData->roomBinAttrExternalNum == 1 ) + { + memcpy(manager->m_pExtDataToUpdate, pRoomExtData->roomBinAttrExternal[0].ptr,pRoomExtData->roomBinAttrExternal[0].size); + manager->m_FriendSessionUpdatedFn(true, manager->m_pParamFriendSessionUpdated); + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + } + else + { + manager->m_FriendSessionUpdatedFn(false, manager->m_pParamFriendSessionUpdated); + } + break; + case SCE_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL: + if( ( errorCode != 0 ) || manager->ForceErrorPoint(SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK) ) + { + app.DebugPrintf(CMinecraftApp::USER_RR,"Error updating external data 0x%x (from SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal event in callback)\n",errorCode); + // If we ever fail to send the external room data, we start a countdown so that we attempt to resend. Not sure how likely it is that updating this will fail without the whole network being broken, + // but if in particular we don't update the flag to say that the session is joinable, then nobody is ever going to see this session. + manager->m_resendExternalRoomDataCountdown = 60; + } + break; + }; +} + +void SQRNetworkManager_Orbis::RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, const void *data, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + + bool gotEventData = false; + switch( event ) + { + case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_KICKEDOUT: + { +// SonyVoiceChat::signalRoomKickedOut(); + // We've been kicked out. This server has rejected our attempt to join, most likely because there wasn't enough space in the server to have us. There's a flag set + // so we can determine which thing has happened +// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo ); +// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataUpdateInfo, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomUpdateInfo); +// app.DebugPrintf(CMinecraftApp::USER_RR,"SCE_NP_MATCHING2_ROOM_EVENT_Kickedout, sceNpMatching2GetEventData returning 0x%x\n",ret); + + bool bIsFull = false; + if( ( data ) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) + { + gotEventData = true; + SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)(data); + if( pUpdateInfo->optData.len == 1 ) + { + if( pUpdateInfo->optData.data[0] == 1 ) + { + bIsFull = true; + } + } + } + app.DebugPrintf(CMinecraftApp::USER_RR,"IsFull determined to be %d\n",bIsFull); + if( bIsFull ) + { + manager->m_nextIdleReasonIsFull = true; + } + manager->ResetToIdle(); + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_DESTROYED: +// SonyVoiceChat::signalRoomDestroyed(); + + { + SceNpMatching2RoomUpdateInfo *pUpdateInfo = (SceNpMatching2RoomUpdateInfo *)data; + app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed\n"); + if( pUpdateInfo ) + { + app.DebugPrintf("Further info: Error 0x%x, cause %d\n",pUpdateInfo->errorCode,pUpdateInfo->eventCause); + } + // If we're hosting, then handle this a bit like a disconnect, in that we will shift the game into an offline game - but don't need to actually leave the room + // since that has been destroyed and so isn't there to be left anymore. Don't do this if we are disconnected though, as we've already handled this. + if( ( manager->m_isHosting ) && !manager->m_bLinkDisconnected ) + { + // MGH - we're not receiving an SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD after this so we have to remove all the remote players + while(manager->m_RudpCtxToPlayerMap.size()) + { + SQRNetworkPlayer* pRemotePlayer = manager->m_RudpCtxToPlayerMap.begin()->second; + manager->RemoveRemotePlayersAndSync( pRemotePlayer->m_roomMemberId, 15 ); + } + + if(pUpdateInfo && (pUpdateInfo->eventCause==SCE_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT)) + { + manager->m_listener->HandleDisconnect(true,true); + } + else + { + manager->m_listener->HandleDisconnect(true); + } + } + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_ROOM_OWNER_CHANGED: + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_DATA_INTERNAL: + // We are using the room internal data to synchronise the player data stored in m_roomSyncData from the host to clients. + // The host is the thing creating the internal room data, so it doesn't need to update itself. + if( !manager->m_isHosting ) + { +// assert ( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData( manager->m_matchingContext, eventKey, manager->cRoomDataInternal, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomDataInternalUpdateInfo); + if( ( data) && !manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_DATA) ) + { + gotEventData = true; + SceNpMatching2RoomDataInternalUpdateInfo *pRoomData = (SceNpMatching2RoomDataInternalUpdateInfo *)(data); + for(int i = 0; i < pRoomData->newRoomBinAttrInternalNum; i++) + { + if( pRoomData->newRoomBinAttrInternal[i]->data.id == SCE_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID ) + { + assert( pRoomData->newRoomBinAttrInternal[i]->data.size == sizeof( manager->m_roomSyncData ) ); + memcpy( &manager->m_roomSyncData, pRoomData->newRoomBinAttrInternal[i]->data.ptr, sizeof( manager->m_roomSyncData ) ); + +// manager->UpdatePlayersFromRoomSyncUIDs(); + // Update mapping from the room slot players to SQRNetworkPlayer instances + manager->MapRoomSlotPlayers(); +#if 0 + { + printf("New player sync data arrived\n"); + for(int i = 0; i < manager->m_roomSyncData.getPlayerCount(); i++ ) + { + printf("%d: small %d, machine %d, local %d\n",i, manager->m_roomSyncData.players[i].m_smallId, manager->m_roomSyncData.players[i].m_roomMemberId, manager->m_roomSyncData.players[i].m_localIdx); + } + } +#endif + break; + } + } + break; + } + // TODO - handle error here? What could we do? + } + + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL: + app.DebugPrintf("SCE_NP_MATCHING2_ROOM_EVENT_ROOM_MEMBER_DATA_INTERNAL\n"); + + if( /*( errorCode == 0 ) && */(!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1) ) ) + { + // We'll get this sync'd round all the connected clients, but we only care about it on the host where we can use it to work out if any RUDP connections need to be made or released + if( manager->m_isHosting ) + { +// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); + if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2) ) ) + { + gotEventData = true; + SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); + assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); + + int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); + int oldMask = manager->GetOldMask( pRoomMemberData->newRoomMemberDataInternal->memberId ); + int addedMask = manager->GetAddedMask(playerMask, oldMask ); + int removedMask = manager->GetRemovedMask(playerMask, oldMask ); + + if( addedMask != 0 ) + { + bool success = manager->AddRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask ); + if( success ) + { + success = manager->CreateRudpConnections(manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, addedMask, pRoomMemberData->newRoomMemberDataInternal->memberId); + } + if( ( !success ) || (manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3) ) ) + { + // Failed for some reason - signal back to the client that this is the case, by updating its internal data back again, rather than have + // it wait for a timeout on its rudp connection initialisation. + SceNpMatching2SetRoomMemberDataInternalRequest reqParam; + SceNpMatching2BinAttr binAttr; + + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &oldMask; + binAttr.size = sizeof(oldMask); + + reqParam.roomId = manager->m_room; + reqParam.memberId = pRoomMemberData->newRoomMemberDataInternal->memberId; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2SetRoomMemberDataInternal( manager->m_matchingContext, &reqParam, NULL, &manager->m_setRoomMemberInternalDataRequestId ); + } + else + { + success = manager->CreateVoiceRudpConnections( manager->m_room, pRoomMemberData->newRoomMemberDataInternal->memberId, 0); + assert(success); + } + + } + + if( removedMask != 0 ) + { + manager->RemoveRemotePlayersAndSync( pRoomMemberData->newRoomMemberDataInternal->memberId, removedMask ); + } + + break; + } + } + else + { + // If, as a client, we receive an updated room member data this could be for two reason. + // (1) Another client in the game has updated their own data as someone has joined/left the session + // (2) The server has set someone's data back, due to a failed attempt to join a game + // We're only interested in scenario (2), when the data that has been updated is our own, in which case we know to abandon creating rudp connections etc. for a new player +// assert( dataSize <= SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo ); +// int ret = sceNpMatching2GetEventData(manager->m_matchingContext, eventKey, (void *)(manager->cRoomMemberDataInternalUpdate), SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberDataInternalUpdateInfo); + if( ( data ) && (!manager->ForceErrorPoint(SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4) ) ) + { + gotEventData = true; + SceNpMatching2RoomMemberDataInternalUpdateInfo *pRoomMemberData = (SceNpMatching2RoomMemberDataInternalUpdateInfo *)(data); + assert( pRoomMemberData->newRoomMemberBinAttrInternalNum == 1 ); + if( pRoomMemberData->newRoomMemberDataInternal->memberId == manager->m_localMemberId ) + { + int playerMask = *((int *)(pRoomMemberData->newRoomMemberBinAttrInternal[0]->data.ptr)); + if( playerMask != manager->m_localPlayerJoinMask ) + { + int playersToRemove = manager->m_localPlayerJoinMask & (~playerMask); + manager->RemoveNetworkPlayers( playersToRemove ); + if( manager->m_listener ) + { + for( int i = 0; i < MAX_LOCAL_PLAYER_COUNT; i++ ) + { + if( playersToRemove & ( 1 << i ) ) + { + manager->m_listener->HandleAddLocalPlayerFailed(i); + break; + } + } + } + } + } + } + + } + } + break; + case SCE_NP_MATCHING2_ROOM_EVENT_UPDATED_SIGNALING_OPT_PARAM: + break; + }; + +// // If we didn't get the event data, then we need to clear it, or the system even queue will overflow +// if( !gotEventData ) +// { +// sceNpMatching2ClearEventData(manager->m_matchingContext, eventKey); +// } +} + + + +// This is an implementation of SceNpMatching2SignalingCallback. We configure our too automatically create a star network of connections with the host at the hub, and can respond here to +// the connections being set up to layer sockets and Rudp on top. +void SQRNetworkManager_Orbis::SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code, void *arg) +{ + // MGH - changed this to queue up the signalling events from the callback and process them later on the server thread + + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + EnterCriticalSection(&manager->m_signallingEventListCS); + SignallingEvent ev; + ev.ctxId = ctxId; + ev.roomId = roomId; + ev.peerMemberId = peerMemberId; + ev.event = event; + ev.error_code = error_code; + manager->m_signallingEventList.push_back(ev); + LeaveCriticalSection(&manager->m_signallingEventListCS); +} + + + + +void SQRNetworkManager_Orbis::ProcessSignallingEvent(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code) +{ + switch( event ) + { + case SCE_NP_MATCHING2_SIGNALING_EVENT_DEAD: + { + if( m_isHosting ) + { + // Remove any players associated with this peer + RemoveRemotePlayersAndSync( peerMemberId, 15 ); + } + else if(peerMemberId == m_hostMemberId) + { + // Host has left the game... so its all over for this client too. Finish everything up now, including deleting the server context which belongs to this gaming session + // This also might be a response to a request to leave the game from our end too so don't need to do anything in that case + if( m_state != SNM_INT_STATE_LEAVING ) + { + DeleteServerContext(); + ResetToIdle(); + } + } + else + { + // we've lost connection to another client (voice only) so kill the voice connection + // no players left on the remote machine once we remove this one + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(peerMemberId); + if(pVoice) + SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); + } + } + break; + + case SCE_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED: + { + // MGH - changed this to always get the data now, as we need to know if the connecting peer is the host or not + // If we're the host, then we need to get the data associated with the connecting peer to know what connections we should be trying to match. So + // the actual creation of connections happens when the response for this request is processed. + + SceNpMatching2GetRoomMemberDataInternalRequest reqParam; + memset( &reqParam, 0, sizeof(reqParam)); + reqParam.roomId = roomId; + reqParam.memberId = peerMemberId; + SceNpMatching2AttributeId attrs[1] = {SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID}; + reqParam.attrId = attrs; + reqParam.attrIdNum = 1; + + sceNpMatching2GetRoomMemberDataInternal( m_matchingContext, &reqParam, NULL, &m_roomMemberDataRequestId); + } + break; + } +} + +void SQRNetworkManager_Orbis::SignallingEventsTick() +{ + EnterCriticalSection(&m_signallingEventListCS); + for(int i=0;iSetState(SNM_INT_STATE_INITIALISE_FAILED); +// if( s_SignInCompleteCallbackFn ) +// { +// if( s_signInCompleteCallbackIfFailed ) +// { +// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); +// } +// s_SignInCompleteCallbackFn = NULL; +// } +// return; +// } +// +// if( netstart_result.result != 0 ) +// { +// // Failed, or user may have decided not to sign in - maybe need to differentiate here +// manager->SetState(SNM_INT_STATE_INITIALISE_FAILED); +// if( s_SignInCompleteCallbackFn ) +// { +// if( s_signInCompleteCallbackIfFailed ) +// { +// s_SignInCompleteCallbackFn(s_SignInCompleteParam,false,0); +// } +// s_SignInCompleteCallbackFn = NULL; +// } +// } +// +// break; +// case CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED: +// break; +// case CELL_SYSUTIL_NP_INVITATION_SELECTED: +// manager->GetInviteDataAndProcess(SCE_NP_BASIC_SELECTED_INVITATION_DATA); +// break; +// default: +// break; +// } +} + +// Implementation of CellRudpContextEventHandler. This is associate with an Rudp context every time one is created, and can be used to determine the status of each +// Rudp connection. We create one context/connection per local player on the non-hosting consoles. +void SQRNetworkManager_Orbis::RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + switch(event_id) + { + case SCE_RUDP_CONTEXT_EVENT_CLOSED: + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_CLOSED\n"); + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + if(pVoice) + { + pVoice->m_bConnected = false; + } + else + { + app.DebugPrintf(CMinecraftApp::USER_RR,"RUDP closed - event error 0x%x\n",error_code); + if( !manager->m_isHosting ) + { + if( manager->m_state == SNM_INT_STATE_JOINING_WAITING_FOR_LOCAL_PLAYERS ) + { + manager->LeaveRoom(true); + } + } + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_ESTABLISHED: + { + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ESTABLISHED\n"); + + SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); + if( player ) + { + // Flag connection stage as being completed for this player + manager->NetworkPlayerConnectionComplete(player); + } + else + { + SonyVoiceChat_Orbis::setConnected(ctx_id); + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_ERROR: + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_ERROR\n"); + break; + case SCE_RUDP_CONTEXT_EVENT_WRITABLE: + { + SQRNetworkPlayer *player = manager->GetPlayerFromRudpCtx(ctx_id); + // This event signifies that room has opened up in the write buffer, so attempt to send something + if( player ) + { + player->SendMoreInternal(); + } + else + { + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + assert(pVoice); + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_READABLE: + if( manager->m_listener ) + { + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx(ctx_id); + if(pVoice) + { + pVoice->readRemoteData(); + } + else + { + unsigned int dataSize = sceRudpGetSizeReadable(ctx_id); + // If we're the host, and this player hasn't yet had its small id confirmed, then the first byte sent to us should be this id + if( manager->m_isHosting ) + { + SQRNetworkPlayer *playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); + if( playerFrom && !playerFrom->HasSmallIdConfirmed() ) + { + if( dataSize >= sizeof(SQRNetworkPlayer::InitSendData) ) + { + SQRNetworkPlayer::InitSendData ISD; + int bytesRead = sceRudpRead( ctx_id, &ISD, sizeof(SQRNetworkPlayer::InitSendData), 0, NULL ); + if( bytesRead == sizeof(SQRNetworkPlayer::InitSendData) ) + { + manager->NetworkPlayerInitialDataReceived(playerFrom, &ISD); + dataSize -= sizeof(SQRNetworkPlayer::InitSendData); + } + else + { + assert(false); + } + } + else + { + assert(false); + } + } + } + + if( dataSize > 0 ) + { + unsigned char *data = new unsigned char [ dataSize ]; + int bytesRead = sceRudpRead( ctx_id, data, dataSize, 0, NULL ); + if( bytesRead > 0 ) + { + SQRNetworkPlayer *playerFrom, *playerTo; + if( manager->m_isHosting ) + { + // Data always going from a remote player, to the host + playerFrom = manager->GetPlayerFromRudpCtx( ctx_id ); + playerTo = manager->m_aRoomSlotPlayers[0]; + } + else + { + // Data always going from host player, to a local player + playerFrom = manager->m_aRoomSlotPlayers[0]; + playerTo = manager->GetPlayerFromRudpCtx( ctx_id ); + } + if( ( playerFrom != NULL ) && ( playerTo != NULL ) ) + { + manager->m_listener->HandleDataReceived( playerFrom, playerTo, data, bytesRead ); + } + } + delete [] data; + } + } + } + break; + case SCE_RUDP_CONTEXT_EVENT_FLUSHED: + app.DebugPrintf(sc_verbose, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SCE_RUDP_CONTEXT_EVENT_FLUSHED\n"); + break; + } +} + +// Implementation of CellRudpEventHandler +int SQRNetworkManager_Orbis::RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + if( event_id == SCE_RUDP_EVENT_SOCKET_RELEASED ) + { + assert( soc == manager->m_soc ); + sceNetSocketClose(soc); + manager->m_soc = -1; + } + return 0; +} + +void SQRNetworkManager_Orbis::NetCtlCallback(int eventType, void *arg) +{ + SQRNetworkManager_Orbis *manager = (SQRNetworkManager_Orbis *)arg; + // Oddly, the disconnect event comes in with a new state of "CELL_NET_CTL_STATE_Connecting"... looks like the event is more important than the state to + // determine what has just happened + if( eventType == SCE_NET_CTL_EVENT_TYPE_DISCONNECTED)// CELL_NET_CTL_EVENT_LINK_DISCONNECTED ) + { + manager->m_bLinkDisconnected = true; + manager->m_listener->HandleDisconnect(false); + } + else //if( event == CELL_NET_CTL_EVENT_ESTABLISH ) + { + manager->m_bLinkDisconnected = false; + } + +} + +// Called when the context has been created, and we are intending to create a room. +void SQRNetworkManager_Orbis::ServerContextValid_CreateRoom() +{ + // First find a world + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_SEARCHING_FOR_WORLD); + + SceNpMatching2GetWorldInfoListRequest reqParam; + + // Request parameters + memset(&reqParam, 0, sizeof(reqParam)); + reqParam.serverId = m_serverId; + + int ret = -1; + if( !ForceErrorPoint(SNM_FORCE_ERROR_GET_WORLD_INFO_LIST) ) + { + ret = sceNpMatching2GetWorldInfoList( m_matchingContext, &reqParam, NULL, &m_getWorldRequestId); + } + if (ret < 0) + { + SetState(SNM_INT_STATE_HOSTING_CREATE_ROOM_FAILED); + return; + } +} + +// Called when the context has been created, and we are intending to join a pre-existing room. +void SQRNetworkManager_Orbis::ServerContextValid_JoinRoom() +{ +// assert( m_state == SNM_INT_STATE_JOINING_SERVER_SEARCH_CREATING_CONTEXT ); + + SetState(SNM_INT_STATE_JOINING_JOIN_ROOM); + + // Join the room, passing the local player mask as initial binary data so that the host knows what local players are here + SceNpMatching2JoinRoomRequest reqParam; + SceNpMatching2BinAttr binAttr; + memset(&reqParam, 0, sizeof(reqParam)); + memset(&binAttr, 0, sizeof(binAttr)); + binAttr.id = SCE_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID; + binAttr.ptr = &m_localPlayerJoinMask; + binAttr.size = sizeof(m_localPlayerJoinMask); + + reqParam.roomId = m_roomToJoin; + reqParam.roomMemberBinAttrInternalNum = 1; + reqParam.roomMemberBinAttrInternal = &binAttr; + + int ret = sceNpMatching2JoinRoom( m_matchingContext, &reqParam, NULL, &m_joinRoomRequestId ); + if ( (ret < 0) || ForceErrorPoint(SNM_FORCE_ERROR_JOIN_ROOM) ) + { + if( ret == SCE_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH) + { + app.SetDisconnectReason( DisconnectPacket::eDisconnect_NATMismatch ); + } + SetState(SNM_INT_STATE_JOINING_JOIN_ROOM_FAILED); + } +} + +const SceNpCommunicationId* SQRNetworkManager_Orbis::GetSceNpCommsId() +{ + return &s_npCommunicationId; +} + +const SceNpCommunicationSignature* SQRNetworkManager_Orbis::GetSceNpCommsSig() +{ + return &s_npCommunicationSignature; +} + +const SceNpTitleId* SQRNetworkManager_Orbis::GetSceNpTitleId() +{ + return &s_npTitleId; +} + +const SceNpTitleSecret* SQRNetworkManager_Orbis::GetSceNpTitleSecret() +{ + return &s_npTitleSecret; +} + +int SQRNetworkManager_Orbis::GetOldMask(SceNpMatching2RoomMemberId memberId) +{ + int oldMask = 0; + for( int i = 0; i < m_roomSyncData.getPlayerCount(); i++ ) + { + if( m_roomSyncData.players[i].m_roomMemberId == memberId ) + { + oldMask |= (1 << m_roomSyncData.players[i].m_localIdx); + } + } + return oldMask; +} + +int SQRNetworkManager_Orbis::GetAddedMask(int newMask, int oldMask) +{ + return newMask & ~oldMask; +} + +int SQRNetworkManager_Orbis::GetRemovedMask(int newMask, int oldMask) +{ + return oldMask & ~newMask; +} + + +void SQRNetworkManager_Orbis::GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) +{ + static SceNpMatching2GetRoomDataExternalListRequest reqParam; + static SceNpMatching2RoomId aRoomId[1]; + static SceNpMatching2AttributeId attr[1]; + + // All parameters will be NULL if this is being called a second time, after creating a new matching context via one of the paths below (using GetMatchingContext). + // NULL parameters therefore basically represents an attempt to retry the last sceNpMatching2GetRoomDataExternalList + if( extData != NULL ) + { + aRoomId[0] = roomId; + attr[0] = SCE_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID; + + memset(&reqParam, 0, sizeof(reqParam)); + reqParam.roomId = aRoomId; + reqParam.roomIdNum = 1; + reqParam.attrIdNum = 1; + reqParam.attrId = attr; + + m_FriendSessionUpdatedFn = FriendSessionUpdatedFn; + m_pParamFriendSessionUpdated = pParam; + m_pExtDataToUpdate = extData; + } + + // Check there's a valid matching context and possibly recreate here + if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) + { + // No matching context, and failed to try and make one. We're really broken here. + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + return; + } + + // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. + if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + app.DebugPrintf("Having to recreate matching context, setting state to SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT\n"); + return; + } + + int ret = sceNpMatching2GetRoomDataExternalList( m_matchingContext, &reqParam, NULL, &m_roomDataExternalListRequestId ); + + // If we hadn't properly detected that a matching context was unvailable, we might still get an error indicating that it is from the previous call. Handle similarly, but we need + // to destroy the context first. + if( ret == SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED ) // Also checking for this as a means of simulating the previous error + { + sceNpMatching2DestroyContext(m_matchingContext); + m_matchingContextValid = false; + if( !GetMatchingContext(SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT) ) + { + // No matching context, and failed to try and make one. We're really broken here. + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + return; + }; + // Kicked off an asynchronous thing that will create a matching context, and then call this method back again (with NULL params) once done, so we can reattempt. Don't do anything more now. + if( m_state == SNM_INT_STATE_IDLE_RECREATING_MATCHING_CONTEXT ) + { + return; + } + } + + if( ret != 0 ) + { + m_FriendSessionUpdatedFn(false, m_pParamFriendSessionUpdated); + } +} + + +#ifdef _CONTENT_PACKAGE +bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError error) +{ + return false; +} +#else +bool SQRNetworkManager_Orbis::aForceError[SNM_FORCE_ERROR_COUNT] = +{ + false, // SNM_FORCE_ERROR_NP2_INIT + false, // SNM_FORCE_ERROR_NET_INITIALIZE_NETWORK + false, // SNM_FORCE_ERROR_NET_CTL_INIT + false, // SNM_FORCE_ERROR_RUDP_INIT + false, // SNM_FORCE_ERROR_NET_START_DIALOG + false, // SNM_FORCE_ERROR_MATCHING2_INIT + false, // SNM_FORCE_ERROR_REGISTER_NP_CALLBACK + false, // SNM_FORCE_ERROR_GET_NPID + false, // SNM_FORCE_ERROR_CREATE_MATCHING_CONTEXT + false, // SNM_FORCE_ERROR_REGISTER_CALLBACKS + false, // SNM_FORCE_ERROR_CONTEXT_START_ASYNC + false, // SNM_FORCE_ERROR_SET_EXTERNAL_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY_COUNT + false, // SNM_FORCE_ERROR_GET_FRIEND_LIST_ENTRY + false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST + false, // SNM_FORCE_ERROR_LEAVE_ROOM + false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL + false, // SNM_FORCE_ERROR_SET_ROOM_MEMBER_DATA_INTERNAL2 + false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT + false, // SNM_FORCE_ERROR_CREATE_JOIN_ROOM + false, // SNM_FORCE_ERROR_GET_SERVER_INFO + false, // SNM_FORCE_ERROR_DELETE_SERVER_CONTEXT + false, // SNM_FORCE_ERROR_SETSOCKOPT_0 + false, // SNM_FORCE_ERROR_SETSOCKOPT_1 + false, // SNM_FORCE_ERROR_SETSOCKOPT_2 + false, // SNM_FORCE_ERROR_SOCK_BIND + false, // SNM_FORCE_ERROR_CREATE_RUDP_CONTEXT + false, // SNM_FORCE_ERROR_RUDP_BIND + false, // SNM_FORCE_ERROR_RUDP_INIT2 + false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA + false, // SNM_FORCE_ERROR_GET_SERVER_INFO_DATA + false, // SNM_FORCE_ERROR_GET_WORLD_INFO_DATA + false, // SNM_FORCE_ERROR_GET_CREATE_JOIN_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_USER_INFO_LIST_DATA + false, // SNM_FORCE_ERROR_GET_JOIN_ROOM_DATA + false, // SNM_FORCE_ERROR_GET_ROOM_MEMBER_DATA_INTERNAL + false, // SNM_FORCE_ERROR_GET_ROOM_EXTERNAL_DATA2 + false, // SNM_FORCE_ERROR_CREATE_SERVER_CONTEXT_CALLBACK + false, // SNM_FORCE_ERROR_SET_ROOM_DATA_CALLBACK + false, // SNM_FORCE_ERROR_UPDATED_ROOM_DATA + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL1 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL2 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL3 + false, // SNM_FORCE_ERROR_UPDATED_ROOM_MEMBER_DATA_INTERNAL4 + false, // SNM_FORCE_ERROR_GET_WORLD_INFO_LIST + false, // SNM_FORCE_ERROR_JOIN_ROOM +}; + +bool SQRNetworkManager_Orbis::ForceErrorPoint(eSQRForceError err) +{ + return aForceError[err]; +} +#endif + + +int ErrorPSNDisconnectedDialogReturned(void *pParam, int iPad, const C4JStorage::EMessageResult iResult) +{ + //SQRNetworkManager_Orbis::CallSignInCompleteCallback(); + SQRNetworkManager_Orbis::m_bCallPSNSignInCallback=true; + + return 0; +} + +void SQRNetworkManager_Orbis::AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed/*=false*/, int iPad/*=-1*/) +{ + s_SignInCompleteCallbackFn = SignInCompleteCallbackFn; + s_signInCompleteCallbackIfFailed = callIfFailed; + s_SignInCompleteParam = pParam; + s_SignInCompleteCallbackPad = iPad; + + // If pad isn't set, get primary pad + iPad = iPad == -1 ? ProfileManager.GetPrimaryPad() : iPad; + + if(ProfileManager.isSignedInPSN(iPad) == false) + { + bool bOpenedDialog = false; + int NPError = ProfileManager.getNPAvailability(iPad); + if(NPError == SCE_NP_ERROR_SIGNED_OUT || NPError == SCE_NP_ERROR_NOT_SIGNED_UP) + { + int32_t ret=sceErrorDialogInitialize(); + if ( ret==SCE_OK ) + { + SceErrorDialogParam dialogParameter; + sceErrorDialogParamInitialize( &dialogParameter ); + dialogParameter.errorCode = SCE_NP_ERROR_SIGNED_OUT; // for force display. + dialogParameter.userId = ProfileManager.getUserID(iPad); + ret = sceErrorDialogOpen( &dialogParameter ); + if( ret < 0 ) + { + app.DebugPrintf("sceErrorDialogOpen failed : 0x%08x\n",ret); + assert(0); + } + else + { + bOpenedDialog = true; + app.DebugPrintf("sceErrorDialogOpen s_errorDialogRunning\n"); + s_errorDialogRunning = true; + } + } + } + + if(!bOpenedDialog) + { + // This shouldn't happen generally + assert(0); + + if(s_SignInCompleteCallbackFn) // MGH - added after crash on PS4 + { + if( s_signInCompleteCallbackIfFailed ) + { + m_bCallPSNSignInCallback=true; + s_signInCompleteCallbackFAIL=true; + + //s_signInCompleteCallbackIfFailed=false; + //s_SignInCompleteCallbackFn(s_SignInCompleteParam, false, iPad); + } + //s_SignInCompleteCallbackFn = NULL; + } + } + } + else if(ProfileManager.isConnectedToPSN(iPad) == false) + { + // we're signed into PSN, but we don't have a net connection, throw up a lan error + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + ui.RequestMessageBox( IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA,1,iPad,ErrorPSNDisconnectedDialogReturned,pParam, app.GetStringTable()); + + } +} + +int SQRNetworkManager_Orbis::SetRichPresence(const void *data) +{ + const sce::Toolkit::NP::PresenceDetails *newPresenceInfo = (const sce::Toolkit::NP::PresenceDetails *)data; + + s_lastPresenceInfo.status = newPresenceInfo->status; + s_lastPresenceInfo.userInfo = newPresenceInfo->userInfo; + + s_presenceStatusDirty = true; + SendLastPresenceInfo(); + + // Return as if no error happened no matter what, as we'll be resending ourselves if we need to and don't want the calling system to retry + return 0; +} + +void SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(void *data, unsigned int dataBytes) +{ + assert(dataBytes <= SCE_TOOLKIT_NP_IN_GAME_PRESENCE_DATA_SIZE_MAX ); + memcpy(s_lastPresenceInfo.data, data, dataBytes); + s_lastPresenceInfo.size = dataBytes; + + s_presenceDataDirty = true; + SendLastPresenceInfo(); +} + +void SQRNetworkManager_Orbis::TickRichPresence() +{ + if( s_resendPresenceTime ) + { + if( s_resendPresenceTime < System::currentTimeMillis() ) + { + s_resendPresenceTime = 0; + SendLastPresenceInfo(); + } + } +} + +void SQRNetworkManager_Orbis::SendLastPresenceInfo() +{ + // Don't attempt to send if we are already waiting to resend + if( s_resendPresenceTime ) return; + + // If we are trying to send at a rate faster than we should be, then use the resend mechanism to send at an appropriate time + if( ( System::currentTimeMillis() - s_lastPresenceTime ) < MIN_PRESENCE_RESEND_TIME ) + { + s_resendPresenceTime = s_lastPresenceTime + MIN_PRESENCE_RESEND_TIME; + return; + } + + // On PS4 we can't set the status and the data at the same time + unsigned int options = 0; + if( s_presenceDataDirty ) + { + // Prioritise data over status as it is critical to discovering the network game + s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_DATA; + } + else if( s_presenceStatusDirty ) + { + s_lastPresenceInfo.presenceType = SCE_TOOLKIT_NP_PRESENCE_STATUS; + } + else + { + return; // nothing to be done. + } + + int err = -1; + // check if we're connected to the PSN first + if(ProfileManager.IsSignedInLive(ProfileManager.getQuadrant(s_lastPresenceInfo.userInfo.userId))) + { + err = sce::Toolkit::NP::Presence::Interface::setPresence(&s_lastPresenceInfo); + app.DebugPrintf("Updating presence type %d @ %dms\n",s_lastPresenceInfo.presenceType,(System::currentTimeMillis()%1000000)); + s_lastPresenceTime = System::currentTimeMillis(); + } + + if( err == SCE_TOOLKIT_NP_SUCCESS ) + { + // Successfully sent something + if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) + { + s_presenceDataDirty = false; + } + else + { + s_presenceStatusDirty = false; + } + } + + // If there's still work to be done, use resend mechanism to do it + if( s_presenceDataDirty || s_presenceStatusDirty ) + { + s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; + } +} + +void SQRNetworkManager_Orbis::SetPresenceFailedCallback() +{ + // Check the last presence type to be sent + if( s_lastPresenceInfo.presenceType == SCE_TOOLKIT_NP_PRESENCE_DATA ) + { + s_presenceDataDirty = true; + } + else + { + s_presenceStatusDirty = true; + } + s_resendPresenceTime = System::currentTimeMillis() + MIN_PRESENCE_RESEND_TIME; +} + + + +void SQRNetworkManager_Orbis::SetPresenceDataStartHostingGame() +{ + if( m_offlineGame ) + { + SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&c_presenceSyncInfoNULL, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); + } + else + { + SQRNetworkManager_Orbis::PresenceSyncInfo presenceInfo; + CPlatformNetworkManagerSony::SetSQRPresenceInfoFromExtData( &presenceInfo, m_joinExtData, m_room, m_serverId ); + SQRNetworkManager_Orbis::UpdateRichPresenceCustomData(&presenceInfo, sizeof(SQRNetworkManager_Orbis::PresenceSyncInfo) ); +// OrbisNPToolkit::createNPSession(); + } +} + +int SQRNetworkManager_Orbis::GetJoiningReadyPercentage() +{ + if ( (m_state == SNM_INT_STATE_HOSTING_SEARCHING_FOR_SERVER) || (m_state == SNM_INT_STATE_JOINING_SEARCHING_FOR_SERVER) ) + { + int completed = ( m_totalServerCount - m_serverCount ) - 1; + int pc = ( completed * 100 ) / m_totalServerCount; + if( pc < 0 ) pc = 0; + if( pc > 100 ) pc = 100; + return pc; + } + else + { + return 100; + } +} + +void SQRNetworkManager_Orbis::removePlayerFromVoiceChat( SQRNetworkPlayer* pPlayer ) +{ + if(pPlayer->IsLocal()) + { + + SonyVoiceChat_Orbis::disconnectLocalPlayer(pPlayer->GetLocalPlayerIndex()); + } + else + { + int numRemotePlayersLeft = 0; + for( int i = 0; i < MAX_ONLINE_PLAYER_COUNT; i++ ) + { + if( m_aRoomSlotPlayers[i] ) + { + if( m_aRoomSlotPlayers[i] != pPlayer ) + { + if(m_aRoomSlotPlayers[i]->m_roomMemberId == pPlayer->m_roomMemberId) + numRemotePlayersLeft++; + } + } + } + if(numRemotePlayersLeft == 0) + { + // no players left on the remote machine once we remove this one + SQRVoiceConnection* pVoice = SonyVoiceChat_Orbis::getVoiceConnectionFromRoomMemberID(pPlayer->m_roomMemberId); + //assert(pVoice); + if(pVoice) + SonyVoiceChat_Orbis::disconnectRemoteConnection(pVoice); + } + } +} + +// While we're in online gameplay notify Sony accordingly (every 5 seconds) +void SQRNetworkManager_Orbis::TickNotify() +{ + if (g_NetworkManager.IsInSession() && !g_NetworkManager.IsLocalGame()) + { + long long currentTime = System::currentTimeMillis(); + + // Note: interval at which to notify Sony of realtime play, according to docs an interval greater than 1 sec is bad + // but in testing NP debug was happy with 5 seconds, to be on the safe side call it 0.75 seconds + int notifyInterval = 750; + + if (currentTime - m_lastNotifyTime > notifyInterval) + { + m_lastNotifyTime = currentTime; + for(int i = 0; i < XUSER_MAX_COUNT; i++) + { + if (ProfileManager.IsSignedInLive(i)) + { + NotifyRealtimePlusFeature(i); + } + } + } + } +} + +// Notify plus feature for given quadrant +void SQRNetworkManager_Orbis::NotifyRealtimePlusFeature(int iQuadrant) +{ + SceNpNotifyPlusFeatureParameter param = SceNpNotifyPlusFeatureParameter(); + param.userId = ProfileManager.getUserID(iQuadrant); + param.size = sizeof(SceNpNotifyPlusFeatureParameter); + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + ZeroMemory(param.padding, sizeof(char) * 4); + ZeroMemory(param.reserved, sizeof(uint8_t) * 32); + + int err = sceNpNotifyPlusFeature(¶m); + if (err != SCE_OK) + { + app.DebugPrintf("SQRNetworkManager_Orbis::NotifyRealtimePlusFeature: sceNpNotifyPlusFeature failed (0x%x)\n", err); + assert(0); + } +} + +// void SQRNetworkManager_Orbis::CallSignInCompleteCallback() +// { +// // If there's a callback +// if( s_SignInCompleteCallbackFn) +// { +// app.DebugPrintf("============ Calling CallSignInCompleteCallback and s_SignInCompleteCallbackFn is OK\n"); +// bool isSignedIn = ProfileManager.IsSignedInLive(s_SignInCompleteCallbackPad); +// +// s_SignInCompleteCallbackFn(s_SignInCompleteParam, isSignedIn, s_SignInCompleteCallbackPad); +// s_SignInCompleteCallbackFn = NULL; +// s_SignInCompleteCallbackPad = -1; +// } +// else +// { +// app.DebugPrintf("============ Calling CallSignInCompleteCallback but s_SignInCompleteCallbackFn is NULL\n"); +// } +//} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h new file mode 100644 index 0000000..5b19f63 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SQRNetworkManager_Orbis.h @@ -0,0 +1,355 @@ +#pragma once +#include +#include +#include +#include +#include + +#include +// #include "SonyVoiceChat_Orbis.h" + +#include "..\..\Common\Network\Sony\SQRNetworkManager.h" + +class SQRNetworkPlayer; +class ISQRNetworkManagerListener; +class SonyVoiceChat_Orbis; +class SQRVoiceConnection; +class C4JThread; + +// This is the lowest level manager for providing network functionality on Sony platforms. This manages various network activities including the players within a gaming session. +// The game shouldn't directly use this class, it is here to provide functionality required by PlatformNetworkManagerSony. + +class SQRNetworkManager_Orbis : public SQRNetworkManager +{ + friend class SonyVoiceChat_Orbis; + friend class SQRNetworkPlayer; + + static const eSQRNetworkManagerState m_INTtoEXTStateMappings[SNM_INT_STATE_COUNT]; + +public: + SQRNetworkManager_Orbis(ISQRNetworkManagerListener *listener); + + // General + void Tick(); + void Initialise(); + void Terminate(); + eSQRNetworkManagerState GetState(); + bool IsHost(); + bool IsReadyToPlayOrIdle(); + bool IsInSession(); + + // Session management + void CreateAndJoinRoom(int hostIndex, int localPlayerMask, void *extData, int extDataSize, bool offline); + void UpdateExternalRoomData(); + bool FriendRoomManagerIsBusy(); + bool FriendRoomManagerSearch(); + bool FriendRoomManagerSearch2(); + int FriendRoomManagerGetCount(); + void FriendRoomManagerGetRoomInfo(int idx, SessionSearchResult *searchResult); + bool JoinRoom(SessionSearchResult *searchResult, int localPlayerMask); + bool JoinRoom(SceNpMatching2RoomId roomId, SceNpMatching2ServerId serverId, int localPlayerMask, const SQRNetworkManager_Orbis::PresenceSyncInfo *presence); + void StartGame(); + void LeaveRoom(bool bActuallyLeaveRoom); + void EndGame(); + bool SessionHasSpace(int spaceRequired); + bool AddLocalPlayerByUserIndex(int idx); + bool RemoveLocalPlayerByUserIndex(int idx); + void SendInviteGUI(); + static void RecvInviteGUI(); + void TickInviteGUI(); + + // Remote play + void UpdateRemotePlay(); + + + +// void GetInviteDataAndProcess(SceNpBasicAttachmentDataId id); +// static bool UpdateInviteData(SQRNetworkManager_Orbis::PresenceSyncInfo *invite); + void GetExtDataForRoom( SceNpMatching2RoomId roomId, void *extData, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ); + + // Player retrieval + int GetPlayerCount(); + int GetOnlinePlayerCount(); + SQRNetworkPlayer *GetPlayerByIndex(int idx); + SQRNetworkPlayer *GetPlayerBySmallId(int idx); + SQRNetworkPlayer *GetPlayerByXuid(PlayerUID xuid); + SQRNetworkPlayer *GetLocalPlayerByUserIndex(int idx); + SQRNetworkPlayer *GetHostPlayer(); + + void removePlayerFromVoiceChat(SQRNetworkPlayer* pPlayer); + // Communication parameter storage + static const SceNpCommunicationId* GetSceNpCommsId(); + static const SceNpCommunicationSignature* GetSceNpCommsSig(); + static const SceNpTitleId* GetSceNpTitleId(); + static const SceNpTitleSecret* GetSceNpTitleSecret(); + + static void GetInviteDataAndProcess(sce::Toolkit::NP::MessageAttachment* pInvite); +private: + void InitialiseAfterOnline(); + void ErrorHandlingTick(); + void UpdateOnlineStatus(int status) { m_onlineStatus = status; } + int GetOnlineStatus() { return m_onlineStatus; } + + ISQRNetworkManagerListener *m_listener; + SQRNetworkPlayer *GetPlayerIfReady(SQRNetworkPlayer *player); + + // Internal state + void SetState(eSQRNetworkManagerInternalState state); + void ResetToIdle(); + eSQRNetworkManagerInternalState m_state; + eSQRNetworkManagerState m_stateExternal; + bool m_nextIdleReasonIsFull; + bool m_isHosting; + SceNpMatching2RoomMemberId m_localMemberId; + SceNpMatching2RoomMemberId m_hostMemberId; // if we're not the host + int m_localPlayerCount; + int m_localPlayerJoined; // Client only, keep a count of how many local players we have confirmed as joined to the application + SceNpMatching2RoomId m_room; + unsigned char m_currentSmallId; + int m_soc; + bool m_offlineGame; + bool m_offlineSQR; + int m_resendExternalRoomDataCountdown; + bool m_matching2initialised; + PresenceSyncInfo m_inviteReceived[MAX_SIMULTANEOUS_INVITES]; + int m_inviteIndex; + static PresenceSyncInfo *m_gameBootInvite; + static PresenceSyncInfo m_gameBootInvite_data; + bool m_doBootInviteCheck; + bool m_isInSession; +// static SceNpBasicAttachmentDataId s_lastInviteIdToRetry; + int m_onlineStatus; + bool m_bLinkDisconnected; + + +private: + + CRITICAL_SECTION m_csRoomSyncData; + RoomSyncData m_roomSyncData; + void *m_joinExtData; + int m_joinExtDataSize; + + std::vector m_vecTempPlayers; + SQRNetworkPlayer *m_aRoomSlotPlayers[MAX_ONLINE_PLAYER_COUNT]; // Maps from the players in m_roomSyncData, to SQRNetworkPlayers + void FindOrCreateNonNetworkPlayer(int slot, int playerType, SceNpMatching2RoomMemberId memberId, int localPlayerIdx, int smallId); + + void MapRoomSlotPlayers(int roomSlotPlayerCount =-1); + void UpdateRoomSyncUIDsFromPlayers(); + void UpdatePlayersFromRoomSyncUIDs(); + void LocalDataSend(SQRNetworkPlayer *playerFrom, SQRNetworkPlayer *playerTo, const void *data, unsigned int dataSize); + int GetSessionIndex(SQRNetworkPlayer *player); + + bool AddRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int playerMask, bool *isFull = NULL ); + void RemoveRemotePlayersAndSync( SceNpMatching2RoomMemberId memberId, int mask ); + void RemoveNetworkPlayers( int mask ); + void SetLocalPlayersAndSync(); + void SyncRoomData(); + SceNpMatching2RequestId m_setRoomDataRequestId; + SceNpMatching2RequestId m_setRoomIntDataRequestId; + SceNpMatching2RequestId m_roomExtDataRequestId; + + // Server context management + bool GetMatchingContext(eSQRNetworkManagerInternalState asyncState); + bool GetServerContext(); + bool GetServerContext2(); + bool GetServerContext(SceNpMatching2ServerId serverId); + void DeleteServerContext(); + bool SelectRandomServer(); + void ServerContextTick(); + int m_totalServerCount; + int m_serverCount; + SceNpMatching2ServerId *m_aServerId; + SceNpMatching2ServerId m_serverId; + bool m_serverContextValid; + SceNpMatching2RequestId m_serverSearchRequestId; + SceNpMatching2RequestId m_serverContextRequestId; + + // Room creation management + SceNpMatching2RequestId m_getWorldRequestId; + SceNpMatching2RequestId m_createRoomRequestId; + SceNpMatching2WorldId m_worldId; + void RoomCreateTick(); + + // Room joining management + SceNpMatching2RoomId m_roomToJoin; + int m_localPlayerJoinMask; + SceNpMatching2RequestId m_joinRoomRequestId; + SceNpMatching2RequestId m_kickRequestId; + + // Room leaving management + SceNpMatching2RequestId m_leaveRoomRequestId; + + // Adding extra network players management + SceNpMatching2RequestId m_setRoomMemberInternalDataRequestId; + + // Player state management + void NetworkPlayerConnectionComplete(SQRNetworkPlayer *player); + void NetworkPlayerSmallIdAllocated(SQRNetworkPlayer *player, unsigned char smallId); + void NetworkPlayerInitialDataReceived(SQRNetworkPlayer *player,void *data); + void NonNetworkPlayerComplete(SQRNetworkPlayer *player, unsigned char smallId); + void HandlePlayerJoined(SQRNetworkPlayer *player); + CRITICAL_SECTION m_csPlayerState; + + // State and thread for managing basic event type messages + C4JThread *m_basicEventThread; + SceKernelEqueue m_basicEventQueue; + static int BasicEventThreadProc( void *lpParameter); + + // State and storage for managing search for friends' games + eSQRNetworkManagerFriendSearchState m_friendSearchState; + SceNpMatching2ContextId m_matchingContext; + bool m_matchingContextValid; + SceNpMatching2RequestId m_friendSearchRequestId; + unsigned int m_friendCount; + C4JThread *m_getFriendCountThread; + static int GetFriendsThreadProc( void* lpParameter ); + void FriendSearchTick(); + SceNpMatching2RequestId m_roomDataExternalListRequestId; + void (* m_FriendSessionUpdatedFn)(bool success, void *pParam); + void *m_pParamFriendSessionUpdated; + void *m_pExtDataToUpdate; + + // Results from searching for rooms that friends are playing in - 5 matched arrays to store their NpIds, rooms, servers, whether a room was found, and whether the external data had been received for the room. Also a count of how many elements are used in this array. + class FriendSearchResult + { + public: + SceNpId m_NpId; + SceNpMatching2RoomId m_RoomId; + SceNpMatching2ServerId m_ServerId; + bool m_RoomFound; + void *m_RoomExtDataReceived; + }; + std::vector m_aFriendSearchResults; + + // Rudp management and local players + std::unordered_map m_RudpCtxToPlayerMap; + + std::unordered_map m_NetAddrToVoiceConnectionMap; + + bool CreateRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask, SceNpMatching2RoomMemberId playersPeerMemberId); + bool CreateVoiceRudpConnections(SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, int playerMask); + bool CreateSocket(); + SQRNetworkPlayer *GetPlayerFromRudpCtx(int rudpCtx); + SQRVoiceConnection* GetVoiceConnectionFromRudpCtx(int rudpCtx); + + SQRNetworkPlayer *GetPlayerFromRoomMemberAndLocalIdx(int roomMember, int localIdx); + SceNpMatching2RequestId m_roomMemberDataRequestId; + + // Callbacks (for matching) + bool RegisterCallbacks(); + static void ContextCallback(SceNpMatching2ContextId id, SceNpMatching2Event event, SceNpMatching2EventCause eventCause, int errorCode, void *arg); + + static void DefaultRequestCallback(SceNpMatching2ContextId id, SceNpMatching2RequestId reqId, SceNpMatching2Event event, int errorCode, const void *data, void *arg); + static void RoomEventCallback(SceNpMatching2ContextId id, SceNpMatching2RoomId roomId, SceNpMatching2Event event, const void *data, void *arg); + + + // MGH - changed this to queue up the signalling events from the callback and process them later on the server thread + class SignallingEvent + { + public: + SceNpMatching2ContextId ctxId; + SceNpMatching2RoomId roomId; + SceNpMatching2RoomMemberId peerMemberId; + SceNpMatching2Event event; + int error_code; + }; + std::vector m_signallingEventList; + CRITICAL_SECTION m_signallingEventListCS; + + static void SignallingCallback(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code, void *arg); + void ProcessSignallingEvent(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId peerMemberId, SceNpMatching2Event event, int error_code); + void SignallingEventsTick(); + + // Callback for NpBasic + static int BasicEventCallback(int event, int retCode, uint32_t reqId, void *arg); + + // Callback for NpManager + static void ManagerCallback(int event, int result, void *arg); + + // Callback for sys util + static void SysUtilCallback(uint64_t status, uint64_t param, void *userdata); + + // Callbacks for rudp + static void RudpContextCallback(int ctx_id, int event_id, int error_code, void *arg); + static int RudpEventCallback(int event_id, int soc, uint8_t const *data, size_t datalen, struct SceNetSockaddr const *addr, SceNetSocklen_t addrlen, void *arg); + + // Callback for netctl + static void NetCtlCallback(int eventType, void *arg); + + // Methods to be called when the server context has been created + void ServerContextValid_CreateRoom(); + void ServerContextValid_JoinRoom(); + + // Mask utilities + int GetOldMask(SceNpMatching2RoomMemberId memberId); + int GetAddedMask(int newMask, int oldMask); + int GetRemovedMask(int newMask, int oldMask); + +#ifndef _CONTENT_PACKAGE + static bool aForceError[SNM_FORCE_ERROR_COUNT]; +#endif + bool ForceErrorPoint(eSQRForceError err); + +public: + static void AttemptPSNSignIn(int (*SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad), void *pParam, bool callIfFailed = false, int iPad = -1); + //static void CallSignInCompleteCallback(); + static int (*s_SignInCompleteCallbackFn)(void *pParam, bool bContinue, int pad); + static bool s_signInCompleteCallbackIfFailed; + static bool s_signInCompleteCallbackFAIL; + + static void *s_SignInCompleteParam; + static bool s_SignInCompleteCallbackPending; + static long long s_errorDialogClosed; + static long long s_systemDialogClosed; + static int s_SignInCompleteCallbackPad; + + // Time to wait for system UI before we check result + #define SYSTEM_UI_WAIT_TIME 1000 + + static int SetRichPresence(const void *data); + void SetPresenceDataStartHostingGame(); + int GetJoiningReadyPercentage(); + static void SetPresenceFailedCallback(); + + // 4J-PB - so we can stop the crash when Iggy's LoadMovie is called from the ContextCallback + static bool m_bCallPSNSignInCallback; + +private: + static void UpdateRichPresenceCustomData(void *data, unsigned int dataBytes); + static void TickRichPresence(); + static void SendLastPresenceInfo(); + + void OnlineCheck(); + void tickErrorDialog(); + + static sce::Toolkit::NP::PresenceDetails s_lastPresenceInfo; + + static const int MIN_PRESENCE_RESEND_TIME = 30 * 1000; // Minimum presence send rate - doesn't seem possible to find out what this actually should be + static __int64 s_lastPresenceTime; + static __int64 s_resendPresenceTime; + + static bool s_presenceStatusDirty; + static bool s_presenceDataDirty; + + static PresenceSyncInfo s_lastPresenceSyncInfo; + static PresenceSyncInfo c_presenceSyncInfoNULL; + static bool b_inviteRecvGUIRunning; + + // Debug + static long long s_roomStartTime; + + // Error dialog + static bool s_errorDialogRunning; + + // NP Notify + void TickNotify(); + void NotifyRealtimePlusFeature(int iQuadrant); + long long m_lastNotifyTime; + + static void RefreshChatAndContentRestrictionsReturned_HandleInvite(void *pParam); + bool m_bRefreshingRestrictionsForInvite; + +public: + static bool s_bInviteDialogRunning; +}; + diff --git a/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp new file mode 100644 index 0000000..34ab67e --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.cpp @@ -0,0 +1,1314 @@ +#include "stdafx.h" + +#include "SonyCommerce_Orbis.h" +#include "PS3\PS3Extras\ShutdownManager.h" +#include + + +bool SonyCommerce_Orbis::m_bCommerceInitialised = false; +// SceNpCommerce2SessionInfo SonyCommerce_Orbis::m_sessionInfo; +SonyCommerce_Orbis::State SonyCommerce_Orbis::m_state = e_state_noSession; +int SonyCommerce_Orbis::m_errorCode = 0; +LPVOID SonyCommerce_Orbis::m_callbackParam = NULL; + +void* SonyCommerce_Orbis::m_receiveBuffer = NULL; +SonyCommerce_Orbis::Event SonyCommerce_Orbis::m_event; +std::queue SonyCommerce_Orbis::m_messageQueue; +std::vector* SonyCommerce_Orbis::m_pProductInfoList = NULL; +SonyCommerce_Orbis::ProductInfoDetailed* SonyCommerce_Orbis::m_pProductInfoDetailed = NULL; +SonyCommerce_Orbis::ProductInfo* SonyCommerce_Orbis::m_pProductInfo = NULL; + +SonyCommerce_Orbis::CategoryInfo* SonyCommerce_Orbis::m_pCategoryInfo = NULL; +const char* SonyCommerce_Orbis::m_pProductID = NULL; +char* SonyCommerce_Orbis::m_pCategoryID = NULL; +SonyCommerce_Orbis::CheckoutInputParams SonyCommerce_Orbis::m_checkoutInputParams; +SonyCommerce_Orbis::DownloadListInputParams SonyCommerce_Orbis::m_downloadInputParams; + +SonyCommerce_Orbis::CallbackFunc SonyCommerce_Orbis::m_callbackFunc = NULL; +// sys_memory_container_t SonyCommerce_Orbis::m_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; +bool SonyCommerce_Orbis::m_bUpgradingTrial = false; + +SonyCommerce_Orbis::CallbackFunc SonyCommerce_Orbis::m_trialUpgradeCallbackFunc; +LPVOID SonyCommerce_Orbis::m_trialUpgradeCallbackParam; + +CRITICAL_SECTION SonyCommerce_Orbis::m_queueLock; + +uint32_t SonyCommerce_Orbis::m_contextId=0; ///< The npcommerce2 context ID +bool SonyCommerce_Orbis::m_contextCreated=false; ///< npcommerce2 context ID created? +SonyCommerce_Orbis::Phase SonyCommerce_Orbis::m_currentPhase = e_phase_stopped; ///< Current commerce2 util +// char SonyCommerce_Orbis::m_commercebuffer[SCE_NP_COMMERCE2_RECV_BUF_SIZE]; + +C4JThread* SonyCommerce_Orbis::m_tickThread = NULL; +bool SonyCommerce_Orbis::m_bLicenseChecked=false; // Check the trial/full license for the game + + + + +sce::Toolkit::NP::Utilities::Future > g_productList; +sce::Toolkit::NP::Utilities::Future g_categoryInfo; +sce::Toolkit::NP::Utilities::Future g_detailedProductInfo; + + +SonyCommerce_Orbis::ProductInfoDetailed s_trialUpgradeProductInfoDetailed; +void SonyCommerce_Orbis::Delete() +{ + m_pProductInfoList=NULL; + m_pProductInfoDetailed=NULL; + m_pProductInfo=NULL; + m_pCategoryInfo = NULL; + m_pProductID = NULL; + m_pCategoryID = NULL; +} + +void SonyCommerce_Orbis::Init() +{ + assert(m_state == e_state_noSession); + if(!m_bCommerceInitialised) + { + m_bCommerceInitialised = true; + m_pCategoryID=(char *)malloc(sizeof(char) * 100); + InitializeCriticalSection(&m_queueLock); + } +} + + + +void SonyCommerce_Orbis::CheckForTrialUpgradeKey_Callback(LPVOID param, bool bFullVersion) +{ + ProfileManager.SetFullVersion(bFullVersion); + if(ProfileManager.IsFullVersion()) + { + StorageManager.SetSaveDisabled(false); + ConsoleUIController::handleUnlockFullVersionCallback(); + // licence has been checked, so we're ok to install the trophies now +// ProfileManager.InitialiseTrophies( SQRNetworkManager_Orbis::GetSceNpCommsId(), +// SQRNetworkManager_Orbis::GetSceNpCommsSig()); +// + } + m_bLicenseChecked=true; +} + +bool SonyCommerce_Orbis::LicenseChecked() +{ + return m_bLicenseChecked; +} + +void SonyCommerce_Orbis::CheckForTrialUpgradeKey() +{ + StorageManager.CheckForTrialUpgradeKey(CheckForTrialUpgradeKey_Callback, NULL); +} + +int SonyCommerce_Orbis::Shutdown() +{ + int ret=0; + m_bCommerceInitialised = false; + if (m_contextCreated) + { + m_contextId = 0; + m_contextCreated = false; + } + + delete m_pCategoryID; + DeleteCriticalSection(&m_queueLock); + + // clear any possible callback function + m_callbackFunc = NULL; + + return ret; +} + + + +int SonyCommerce_Orbis::TickLoop(void* lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eCommerceThread); + while( (m_currentPhase != e_phase_stopped) && ShutdownManager::ShouldRun(ShutdownManager::eCommerceThread) ) + { + processEvent(); + processMessage(); + Sleep(16); // sleep for a frame +// ((SonyCommerce_Orbis*)app.GetCommerce())->Test(); + } + + Shutdown(); + ShutdownManager::HasFinished(ShutdownManager::eCommerceThread); + + return 0; +} + +void SonyCommerce_Orbis::copyProductList(std::vector* pProductList, std::vector* pNPProductList) +{ + ProductInfo tempInfo; + std::vector tempProductVec; + // Reserve some space + int numProducts = pNPProductList->size(); + tempProductVec.reserve(numProducts); + for(int i=0;iat(i); + + // reset tempInfo + memset(&tempInfo, 0x0, sizeof(tempInfo)); + strncpy(tempInfo.productId, npInfo.productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + strncpy(tempInfo.productName, npInfo.productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + strncpy(tempInfo.shortDescription, npInfo.shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strcpy(tempInfo.longDescription,"Missing long description"); + strncpy(tempInfo.spName, npInfo.spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + strncpy(tempInfo.imageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + tempInfo.releaseDate = npInfo.releaseDate; + tempInfo.purchasabilityFlag = npInfo.purchasabilityFlag; + // Take out the price. Nicely formatted + // but also keep the price as a value in case it's 0 - we need to show "free" for that + tempInfo.ui32Price = -1;// not available here + strncpy(tempInfo.price, npInfo.price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + tempProductVec.push_back(tempInfo); + } + pNPProductList->clear(); // clear the vector now we're done, this doesn't happen automatically for the next query + + // Set our result + *pProductList = tempProductVec; +} + +int SonyCommerce_Orbis::getProductList(std::vector* productList, char *categoryId) +{ + int ret; + sce::Toolkit::NP::ProductListInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + params.serviceLabel = 0; + app.DebugPrintf("Getting Product List ...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::getProductList(&g_productList, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::getProductList() error. ret = 0x%x\n", ret); + return ret; + } + + if (g_productList.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyProductList(productList, g_productList.get()); + m_event = e_event_commerceGotProductList; + } + + return ret; +} + + + +void SonyCommerce_Orbis::copyCategoryInfo(CategoryInfo *pInfo, sce::Toolkit::NP::CategoryInfo *pNPInfo) +{ + strcpy(pInfo->current.categoryId, pNPInfo->current.categoryId); + strcpy(pInfo->current.categoryName, pNPInfo->current.categoryName); + strcpy(pInfo->current.categoryDescription, pNPInfo->current.categoryDescription); + strcpy(pInfo->current.imageUrl, pNPInfo->current.imageUrl); + pInfo->countOfProducts = pNPInfo->countOfProducts; + pInfo->countOfSubCategories = pNPInfo->countOfSubCategories; + if(pInfo->countOfSubCategories > 0) + { + std::list::iterator iter = pNPInfo->subCategories.begin(); + std::list::iterator iterEnd = pNPInfo->subCategories.end(); + + while(iter != iterEnd) + { + // For each sub category, obtain information + CategoryInfoSub tempSubCatInfo; + strcpy(tempSubCatInfo.categoryId, iter->categoryId); + strcpy(tempSubCatInfo.categoryName, iter->categoryName); + strcpy(tempSubCatInfo.categoryDescription, iter->categoryDescription); + strcpy(tempSubCatInfo.imageUrl, iter->imageUrl); + // Add to the list + pInfo->subCategories.push_back(tempSubCatInfo); + iter++; + } + } +} + +int SonyCommerce_Orbis::getCategoryInfo(CategoryInfo *pInfo, char *categoryId) +{ + + + int ret; + sce::Toolkit::NP::CategoryInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, "");//categoryId); + params.serviceLabel = 0; + + app.DebugPrintf("Getting Category Information...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::getCategoryInfo(&g_categoryInfo, params, true); + if (ret < 0) + { + // error + app.DebugPrintf("Commerce::Interface::getCategoryInfo error: 0x%x\n", ret); + return ret; + } + else if (g_categoryInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyCategoryInfo(pInfo, g_categoryInfo.get()); + m_event = e_event_commerceGotCategoryInfo; + } + + return ret; +} + +void SonyCommerce_Orbis::copyDetailedProductInfo(ProductInfoDetailed *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo) +{ + // populate our temp struct + // pInfo->ratingDescriptors = npInfo.ratingSystemId; + strncpy(pInfo->productId, pNPInfo->productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + strncpy(pInfo->productName, pNPInfo->productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + strncpy(pInfo->shortDescription, pNPInfo->shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strncpy(pInfo->longDescription, pNPInfo->longDescription, SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN); + strncpy(pInfo->legalDescription, pNPInfo->legalDescription, SCE_NP_COMMERCE2_PRODUCT_LEGAL_DESCRIPTION_LEN); + strncpy(pInfo->spName, pNPInfo->spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + strncpy(pInfo->imageUrl, pNPInfo->imageUrl, SCE_NP_COMMERCE2_URL_LEN); + pInfo->releaseDate = pNPInfo->releaseDate; + strncpy(pInfo->ratingSystemId, pNPInfo->ratingSystemId, SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN); + strncpy(pInfo->ratingImageUrl, pNPInfo->imageUrl, SCE_NP_COMMERCE2_URL_LEN); + strncpy(pInfo->skuId, pNPInfo->skuId, SCE_NP_COMMERCE2_SKU_ID_LEN); + pInfo->purchasabilityFlag = pNPInfo->purchasabilityFlag; + pInfo->ui32Price= pNPInfo->intPrice; + strncpy(pInfo->price, pNPInfo->price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + +} +int SonyCommerce_Orbis::getDetailedProductInfo(ProductInfoDetailed *pInfo, const char *productId, char *categoryId) +{ + int ret; + sce::Toolkit::NP::DetailedProductInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + strcpy(params.productId, productId); + + + app.DebugPrintf("Getting Detailed Product Information ... \n"); + ret = sce::Toolkit::NP::Commerce::Interface::getDetailedProductInfo(&g_detailedProductInfo, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::getDetailedProductInfo() error. ret = 0x%x\n", ret); + return ret; + } + + if (g_detailedProductInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyDetailedProductInfo(pInfo, g_detailedProductInfo.get()); + m_event = e_event_commerceGotDetailedProductInfo; + } + return ret; +} + +void SonyCommerce_Orbis::copyAddDetailedProductInfo(ProductInfo *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo) +{ + + // populate our temp struct + // pInfo->ratingDescriptors = npInfo.ratingSystemId; + // strncpy(pInfo->productId, npInfo.productId, SCE_NP_COMMERCE2_PRODUCT_ID_LEN); + // strncpy(pInfo->productName, npInfo.productName, SCE_NP_COMMERCE2_PRODUCT_NAME_LEN); + // strncpy(pInfo->shortDescription, npInfo.shortDescription, SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN); + strncpy(pInfo->longDescription, pNPInfo->longDescription, SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN); + // strncpy(pInfo->legalDescription, npInfo.legalDescription, SCE_NP_COMMERCE2_PRODUCT_LEGAL_DESCRIPTION_LEN); + // strncpy(pInfo->spName, npInfo.spName, SCE_NP_COMMERCE2_SP_NAME_LEN); + // strncpy(pInfo->imageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + // pInfo->releaseDate = npInfo.releaseDate; + // strncpy(pInfo->ratingSystemId, npInfo.ratingSystemId, SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN); + // strncpy(pInfo->ratingImageUrl, npInfo.imageUrl, SCE_NP_COMMERCE2_URL_LEN); + strncpy(pInfo->skuId, pNPInfo->skuId, SCE_NP_COMMERCE2_SKU_ID_LEN); + pInfo->purchasabilityFlag = pNPInfo->purchasabilityFlag; + pInfo->ui32Price= pNPInfo->intPrice; + strncpy(pInfo->price, pNPInfo->price, SCE_TOOLKIT_NP_SKU_PRICE_LEN); + +} + +int SonyCommerce_Orbis::addDetailedProductInfo(ProductInfo *pInfo, const char *productId, char *categoryId) +{ + int ret; + sce::Toolkit::NP::DetailedProductInfoInputParams params; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + + params.userInfo.userId = userId; + strcpy(params.categoryId, categoryId); + strcpy(params.productId, productId); + + + app.DebugPrintf("Getting Detailed Product Information ... \n"); + ret = sce::Toolkit::NP::Commerce::Interface::getDetailedProductInfo(&g_detailedProductInfo, params, true); + if (ret < 0) + { + app.DebugPrintf("CommerceInterface::addDetailedProductInfo() error. ret = 0x%x\n", ret); + } + + if (g_detailedProductInfo.hasResult()) + { + // result has returned immediately (don't think this should happen, but was handled in the samples + copyAddDetailedProductInfo(pInfo, g_detailedProductInfo.get()); + m_event = e_event_commerceAddedDetailedProductInfo; + } + return ret; +} + + +int SonyCommerce_Orbis::checkout(CheckoutInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::CheckoutInputParams npParams; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Checkout...\n"); + + ret = sce::Toolkit::NP::Commerce::Interface::checkout(npParams, false); + if (ret < 0) + { + app.DebugPrintf("Sample menu checkout() error. ret = 0x%x\n", ret); + } + + + return ret; +} + + +int SonyCommerce_Orbis::downloadList(DownloadListInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::DownloadListInputParams npParams; + //memset(&npParams,0,sizeof(sce::Toolkit::NP::DownloadListInputParams)); + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + npParams.skuIds.clear(); + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Store Download List...\n"); + ret = sce::Toolkit::NP::Commerce::Interface::displayDownloadList(npParams, true); + if (ret < 0) + { + app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); + } + + return ret; +} + +int SonyCommerce_Orbis::checkout_game(CheckoutInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::CheckoutInputParams npParams; + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Checkout...\n"); + sce::Toolkit::NP::ProductBrowseParams Myparams; + + Myparams.serviceLabel = 0; + Myparams.userInfo.userId = userId; + strncpy(Myparams.productId, "MINECRAFTPS40000", strlen("MINECRAFTPS40000")); + + ret = sce::Toolkit::NP::Commerce::Interface::productBrowse(Myparams, false); + if (ret < 0) { + // Error handling + } + + + //ret = sce::Toolkit::NP::Commerce::Interface::checkout(npParams, false); + if (ret < 0) + { + app.DebugPrintf("Sample menu checkout() error. ret = 0x%x\n", ret); + } + + + return ret; +} + +int SonyCommerce_Orbis::downloadList_game(DownloadListInputParams ¶ms) +{ + + int ret; + sce::Toolkit::NP::DownloadListInputParams npParams; + //memset(&npParams,0,sizeof(sce::Toolkit::NP::DownloadListInputParams)); + SceUserServiceUserId userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad()); + npParams.userInfo.userId = userId; + npParams.serviceLabel = 0; + npParams.skuIds.clear(); + + std::list::iterator iter = params.skuIds.begin(); + std::list::iterator iterEnd = params.skuIds.end(); + while(iter != iterEnd) + { + npParams.skuIds.push_back((char*)*iter); // have to remove the const here, not sure why the libs pointers aren't const + iter++; + } + + app.DebugPrintf("Starting Store Download List...\n"); +// ret = sce::Toolkit::NP::Commerce::Interface::displayDownloadList(npParams, true); +// if (ret < 0) +// { +// app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); +// } + + sce::Toolkit::NP::ProductBrowseParams Myparams; + + Myparams.serviceLabel = 0; + Myparams.userInfo.userId = userId; + strncpy(Myparams.productId, "MINECRAFTPS40000", strlen("MINECRAFTPS40000")); + + ret = sce::Toolkit::NP::Commerce::Interface::productBrowse(Myparams, false); + if (ret < 0) { + // Error handling + app.DebugPrintf("Commerce::Interface::displayDownloadList error: 0x%x\n", ret); + } + + + + return ret; +} + +void SonyCommerce_Orbis::UpgradeTrialCallback2(LPVOID lpParam,int err) +{ + SonyCommerce* pCommerce = (SonyCommerce*)lpParam; + app.DebugPrintf(4,"SonyCommerce_UpgradeTrialCallback2 : err : 0x%08x\n", err); + pCommerce->CheckForTrialUpgradeKey(); + if(err != SCE_OK) + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PRO_UNLOCKGAME_TITLE, IDS_NO_DLCOFFERS, uiIDA,1,ProfileManager.GetPrimaryPad()); + } + m_trialUpgradeCallbackFunc(m_trialUpgradeCallbackParam, m_errorCode); +} + +void SonyCommerce_Orbis::UpgradeTrialCallback1(LPVOID lpParam,int err) +{ + SonyCommerce* pCommerce = (SonyCommerce*)lpParam; + app.DebugPrintf(4,"SonyCommerce_UpgradeTrialCallback1 : err : 0x%08x\n", err); + if(err == SCE_OK) + { + char* skuID = s_trialUpgradeProductInfoDetailed.skuId; + if(s_trialUpgradeProductInfoDetailed.purchasabilityFlag == SCE_TOOLKIT_NP_COMMERCE_NOT_PURCHASED) + { + app.DebugPrintf(4,"UpgradeTrialCallback1 - Checkout\n"); + pCommerce->Checkout_Game(UpgradeTrialCallback2, pCommerce, skuID); + + + + } + else + { + app.DebugPrintf(4,"UpgradeTrialCallback1 - DownloadAlreadyPurchased\n"); + pCommerce->DownloadAlreadyPurchased_Game(UpgradeTrialCallback2, pCommerce, skuID); + } + } + else + { + UINT uiIDA[1]; + uiIDA[0]=IDS_CONFIRM_OK; + C4JStorage::EMessageResult result = ui.RequestMessageBox( IDS_PRO_UNLOCKGAME_TITLE, IDS_NO_DLCOFFERS, uiIDA,1,ProfileManager.GetPrimaryPad()); + m_trialUpgradeCallbackFunc(m_trialUpgradeCallbackParam, m_errorCode); + } +} + + + +// global func, so we can call from the profile lib +void SonyCommerce_UpgradeTrial() +{ + // we're now calling the app function here, which manages pending requests + app.UpgradeTrial(); +} + +void SonyCommerce_Orbis::UpgradeTrial(CallbackFunc cb, LPVOID lpParam) +{ + m_trialUpgradeCallbackFunc = cb; + m_trialUpgradeCallbackParam = lpParam; + +// static char szTrialUpgradeSkuID[64]; +// sprintf(szTrialUpgradeSkuID, "%s-TRIALUPGRADE0001", app.GetCommerceCategory());//, szSKUSuffix); + GetDetailedProductInfo(UpgradeTrialCallback1, this, &s_trialUpgradeProductInfoDetailed, app.GetUpgradeKey(), app.GetCommerceCategory()); +} + + +int SonyCommerce_Orbis::createContext() +{ +// SceNpId npId; +// int ret = sceNpManagerGetNpId(&npId); +// if(ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpManagerGetNpId problem\n"); +// return ret; +// } +// +// if (m_contextCreated) { +// ret = sceNpCommerce2DestroyCtx(m_contextId); +// if (ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpCommerce2DestroyCtx problem\n"); +// return ret; +// } +// } +// +// // Create commerce2 context +// ret = sceNpCommerce2CreateCtx(SCE_NP_COMMERCE2_VERSION, &npId, commerce2Handler, NULL, &m_contextId); +// if (ret < 0) +// { +// app.DebugPrintf(4,"createContext sceNpCommerce2CreateCtx problem\n"); +// return ret; +// } + + m_contextCreated = true; + + return SCE_OK; +} + +int SonyCommerce_Orbis::createSession() +{ + // From the Sony docs + // + // sce::Toolkit::NP::Commerce::Interface::CreateSession + // + // This function is provided to maintain compatibility with the PlayStationVita and PlayStation3 platforms. Because commerce on the PlayStation4 is not session based, SCE_TOOLKIT_NP_SUCCESS is always returned. + + int ret = sce::Toolkit::NP::Commerce::Interface::createSession(); + + if (ret < 0) + { + return ret; + } + m_currentPhase = e_phase_creatingSessionPhase; + + return ret; +} + + +void SonyCommerce_Orbis::commerce2Handler( const sce::Toolkit::NP::Event& event) +{ +// Event reply; +// reply.service = Toolkit::NP::commerce; +// + if(m_bCommerceInitialised==false) + { + // 4J-PB - this happens when we've signed out of the PSn, and we were in the process of retrieving DLC product info + // Ignore this event + app.DebugPrintf("@@@@@@@@ IGNORING COMMERCE EVENT AFTER COMMERCE SHUTDOWN @@@@@@@@@\n"); + return; + } + EnterCriticalSection(&m_queueLock); + + switch (event.event) + { + case sce::Toolkit::NP::Event::UserEvent::commerceError: + { + m_messageQueue.push(e_message_commerceEnd); + m_errorCode = event.returnCode; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceSessionCreated: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionCreated; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceSessionAborted: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionAborted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceCheckoutStarted: + { + m_currentPhase = e_phase_checkoutPhase; + m_event = e_event_commerceCheckoutStarted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceGotCategoryInfo: + { + copyCategoryInfo(m_pCategoryInfo, g_categoryInfo.get()); + m_pCategoryInfo = NULL; + m_event = e_event_commerceGotCategoryInfo; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceGotProductList: + { + copyProductList(m_pProductInfoList, g_productList.get()); + m_pProductInfoDetailed = NULL; + m_event = e_event_commerceGotProductList; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceGotDetailedProductInfo: + { + if(m_pProductInfoDetailed) + { + copyDetailedProductInfo(m_pProductInfoDetailed, g_detailedProductInfo.get()); + m_pProductInfoDetailed = NULL; + } + else + { + copyAddDetailedProductInfo(m_pProductInfo, g_detailedProductInfo.get()); + m_pProductInfo = NULL; + } + m_event = e_event_commerceGotDetailedProductInfo; + break; + } + + + +// case SCE_NP_COMMERCE2_EVENT_DO_CHECKOUT_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceCheckoutSuccess; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_CHECKOUT_BACK: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceCheckoutAborted; +// break; +// } + case sce::Toolkit::NP::Event::UserEvent::commerceCheckoutFinished: + { + m_event = e_event_commerceCheckoutFinished; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceDownloadListStarted: + { + m_currentPhase = e_phase_downloadListPhase; + m_event = e_event_commerceDownloadListStarted; + break; + } +// case SCE_NP_COMMERCE2_EVENT_DO_DL_LIST_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceDownloadListSuccess; +// break; +// } + case sce::Toolkit::NP::Event::UserEvent::commerceDownloadListFinished: + { + m_event = e_event_commerceDownloadListFinished; + break; + } + + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseStarted: + { + m_currentPhase = e_phase_productBrowsePhase; + m_event = e_event_commerceProductBrowseStarted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseSuccess: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceProductBrowseSuccess; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseAborted: + { + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceProductBrowseAborted; + break; + } + case sce::Toolkit::NP::Event::UserEvent::commerceProductBrowseFinished: + { + m_event = e_event_commerceProductBrowseFinished; + break; + } + +// case SCE_NP_COMMERCE2_EVENT_DO_PROD_BROWSE_OPENED: +// break; +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_STARTED: +// { +// m_currentPhase = e_phase_voucherRedeemPhase; +// m_event = e_event_commerceVoucherInputStarted; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_SUCCESS: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceVoucherInputSuccess; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_BACK: +// { +// m_messageQueue.push(e_message_commerceEnd); +// m_event = e_event_commerceVoucherInputAborted; +// break; +// } +// case SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_FINISHED: +// { +// m_event = e_event_commerceVoucherInputFinished; +// break; +// } + default: + break; + }; + + LeaveCriticalSection(&m_queueLock); +} + + + +void SonyCommerce_Orbis::processMessage() +{ + EnterCriticalSection(&m_queueLock); + int ret; + if(m_messageQueue.empty()) + { + LeaveCriticalSection(&m_queueLock); + return; + } + Message msg = m_messageQueue.front(); + m_messageQueue.pop(); + + switch (msg) + { + + case e_message_commerceCreateSession: + ret = createSession(); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + + case e_message_commerceGetCategoryInfo: + { + ret = getCategoryInfo(m_pCategoryInfo, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + app.DebugPrintf(4,"ERROR - e_event_commerceGotCategoryInfo - %s\n",m_pCategoryID); + m_errorCode = ret; + } + break; + } + + case e_message_commerceGetProductList: + { + ret = getProductList(m_pProductInfoList, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + } + break; + } + + case e_message_commerceGetDetailedProductInfo: + { + ret = getDetailedProductInfo(m_pProductInfoDetailed, m_pProductID, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + case e_message_commerceAddDetailedProductInfo: + { + ret = addDetailedProductInfo(m_pProductInfo, m_pProductID, m_pCategoryID); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + +// +// case e_message_commerceStoreProductBrowse: +// { +// ret = productBrowse(*(ProductBrowseParams *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// _TOOLKIT_NP_DEL (ProductBrowseParams *)msg.inputArgs; +// break; +// } +// +// case e_message_commerceUpgradeTrial: +// { +// ret = upgradeTrial(); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// break; +// } +// +// case e_message_commerceRedeemVoucher: +// { +// ret = voucherCodeInput(*(VoucherInputParams *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } +// _TOOLKIT_NP_DEL (VoucherInputParams *)msg.inputArgs; +// break; +// } +// +// case e_message_commerceGetEntitlementList: +// { +// Job > tmpJob(static_cast > *>(msg.output)); +// +// int state = 0; +// int ret = sceNpManagerGetStatus(&state); +// +// // We don't want to process this if we are offline +// if (ret < 0 || state != SCE_NP_MANAGER_STATUS_ONLINE) { +// m_event = e_event_commerceError; +// reply.returnCode = SCE_TOOLKIT_NP_OFFLINE; +// tmpJob.setError(SCE_TOOLKIT_NP_OFFLINE); +// } else { +// getEntitlementList(&tmpJob); +// } +// break; +// } +// +// case e_message_commerceConsumeEntitlement: +// { +// int state = 0; +// int ret = sceNpManagerGetStatus(&state); +// +// // We don't want to process this if we are offline +// if (ret < 0 || state != SCE_NP_MANAGER_STATUS_ONLINE) { +// m_event = e_event_commerceError; +// reply.returnCode = SCE_TOOLKIT_NP_OFFLINE; +// } else { +// +// ret = consumeEntitlement(*(EntitlementToConsume *)msg.inputArgs); +// if (ret < 0) { +// m_event = e_event_commerceError; +// m_errorCode = ret; +// } else { +// m_event = e_event_commerceConsumedEntitlement; +// } +// } +// _TOOLKIT_NP_DEL (EntitlementToConsume *)msg.inputArgs; +// +// break; +// } +// + case e_message_commerceCheckout: + { + ret = checkout(m_checkoutInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceDownloadList: + { + ret = downloadList(m_downloadInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceCheckout_Game: + { + ret = checkout_game(m_checkoutInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceDownloadList_Game: + { + ret = downloadList_game(m_downloadInputParams); + if (ret < 0) { + m_event = e_event_commerceError; + m_errorCode = ret; + } + break; + } + + case e_message_commerceEnd: + app.DebugPrintf("XXX - e_message_commerceEnd!\n"); + ret = commerceEnd(); + if (ret < 0) + { + m_event = e_event_commerceError; + m_errorCode = ret; + } + // 4J-PB - we don't seem to handle the error code here + else if(m_errorCode!=0) + { + m_event = e_event_commerceError; + } + break; + + default: + break; + } + + LeaveCriticalSection(&m_queueLock); +} + + +void SonyCommerce_Orbis::processEvent() +{ + int ret = 0; + + switch (m_event) + { + case e_event_none: + break; + case e_event_commerceSessionCreated: + app.DebugPrintf(4,"Commerce Session Created.\n"); + runCallback(); + break; + case e_event_commerceSessionAborted: + app.DebugPrintf(4,"Commerce Session aborted.\n"); + runCallback(); + break; + case e_event_commerceGotProductList: + app.DebugPrintf(4,"Got product list.\n"); + runCallback(); + break; + case e_event_commerceGotCategoryInfo: + app.DebugPrintf(4,"Got category info\n"); + runCallback(); + break; + case e_event_commerceGotDetailedProductInfo: + app.DebugPrintf(4,"Got detailed product info.\n"); + runCallback(); + break; + case e_event_commerceAddedDetailedProductInfo: + app.DebugPrintf(4,"Added detailed product info.\n"); + runCallback(); + break; + case e_event_commerceProductBrowseStarted: + break; + case e_event_commerceProductBrowseSuccess: + break; + case e_event_commerceProductBrowseAborted: + break; + case e_event_commerceProductBrowseFinished: + app.DebugPrintf(4,"e_event_commerceProductBrowseFinished succeeded: 0x%x\n", m_errorCode); + + if(m_callbackFunc!=NULL) + { + runCallback(); + } + + //assert(0); +// ret = sys_memory_container_destroy(s_memContainer); +// if (ret < 0) { +// printf("Failed to destroy memory container"); +// } +// s_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; + break; + case e_event_commerceVoucherInputStarted: + break; + case e_event_commerceVoucherInputSuccess: + break; + case e_event_commerceVoucherInputAborted: + break; + case e_event_commerceVoucherInputFinished: + assert(0); +// ret = sys_memory_container_destroy(s_memContainer); +// if (ret < 0) { +// printf("Failed to destroy memory container"); +// } +// s_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID; + break; + case e_event_commerceGotEntitlementList: + break; + case e_event_commerceConsumedEntitlement: + break; + case e_event_commerceCheckoutStarted: + app.DebugPrintf(4,"Checkout Started\n"); + break; + case e_event_commerceCheckoutSuccess: + app.DebugPrintf(4,"Checkout succeeded: 0x%x\n", m_errorCode); + // clear the DLC installed and check again + app.ClearDLCInstalled(); + ui.HandleDLCInstalled(0); + break; + case e_event_commerceCheckoutAborted: + app.DebugPrintf(4,"Checkout aborted: 0x%x\n", m_errorCode); + break; + case e_event_commerceCheckoutFinished: + app.DebugPrintf(4,"Checkout Finished: 0x%x\n", m_errorCode); + if (ret < 0) { + app.DebugPrintf(4,"Failed to destroy memory container"); + } + + // 4J-PB - if there's been an error - like dlc already purchased, the runcallback has already happened, and will crash this time + if(m_callbackFunc!=NULL) + { + runCallback(); + } + break; + case e_event_commerceDownloadListStarted: + app.DebugPrintf(4,"Download List Started\n"); + break; + case e_event_commerceDownloadListSuccess: + app.DebugPrintf(4,"Download succeeded: 0x%x\n", m_errorCode); + break; + case e_event_commerceDownloadListFinished: + app.DebugPrintf(4,"Download Finished: 0x%x\n", m_errorCode); + if (ret < 0) { + app.DebugPrintf(4,"Failed to destroy memory container"); + } + + // 4J-PB - if there's been an error - like dlc already purchased, the runcallback has already happened, and will crash this time + if(m_callbackFunc!=NULL) + { + runCallback(); + } + break; + case e_event_commerceError: + app.DebugPrintf(4,"Commerce Error 0x%x\n", m_errorCode); + runCallback(); + break; + default: + break; + } + m_event = e_event_none; +} + + +int SonyCommerce_Orbis::commerceEnd() +{ + int ret = 0; + +// if (m_currentPhase == e_phase_voucherRedeemPhase) +// ret = sceNpCommerce2DoProductCodeFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_productBrowsePhase) +// ret = sceNpCommerce2DoProductBrowseFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_creatingSessionPhase) +// ret = sceNpCommerce2CreateSessionFinish(m_contextId, &m_sessionInfo); +// else if (m_currentPhase == e_phase_checkoutPhase) +// ret = sceNpCommerce2DoCheckoutFinishAsync(m_contextId); +// else if (m_currentPhase == e_phase_downloadListPhase) +// ret = sceNpCommerce2DoDlListFinishAsync(m_contextId); + + m_currentPhase = e_phase_idle; + + return ret; +} + +void SonyCommerce_Orbis::CreateSession( CallbackFunc cb, LPVOID lpParam ) +{ + // 4J-PB - reset any previous error code + // I had this happen when I was offline on Vita, and accepted the PSN sign-in + // the m_errorCode was picked up in the message queue after the commerce init call + if(m_errorCode!=0) + { + app.DebugPrintf("m_errorCode was set!\n"); + m_errorCode=0; + } + Init(); + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + + // We don't need to create a session on PS4, from the Sony docs - +// This function is provided to maintain compatibility with the PlayStationVita and PlayStation3 +// platforms. Because commerce on the PlayStation4 is not session based, SCE_TOOLKIT_NP_SUCCESS is always returned. + + + +// m_messageQueue.push(e_message_commerceCreateSession); + m_messageQueue.push(e_message_commerceEnd); + m_event = e_event_commerceSessionCreated; + + if(m_tickThread == NULL) + m_tickThread = new C4JThread(TickLoop, NULL, "SonyCommerce_Orbis tick"); + if(m_tickThread->isRunning() == false) + { + m_currentPhase = e_phase_idle; + m_tickThread->Run(); + } + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::CloseSession() +{ + assert(m_currentPhase == e_phase_idle); + m_currentPhase = e_phase_stopped; + // 4J-PB - don't call shutdown here - the SonyCommerce_Orbis::TickLoop thread is still running and could crash accessing the critical section that Shutdown destroys + //Shutdown(); +} + +void SonyCommerce_Orbis::GetProductList( CallbackFunc cb, LPVOID lpParam, std::vector* productList, const char *categoryId) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfoList = productList; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetProductList); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::GetDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfoDetailed* productInfo, const char *productId, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfoDetailed = productInfo; + m_pProductID = productId; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetDetailedProductInfo); + LeaveCriticalSection(&m_queueLock); +} + +// 4J-PB - fill out the long description and the price for the product +void SonyCommerce_Orbis::AddDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfo* productInfo, const char *productId, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pProductInfo = productInfo; + m_pProductID = productId; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceAddDetailedProductInfo); + LeaveCriticalSection(&m_queueLock); +} +void SonyCommerce_Orbis::GetCategoryInfo( CallbackFunc cb, LPVOID lpParam, CategoryInfo *info, const char *categoryId ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_pCategoryInfo = info; + strcpy(m_pCategoryID,categoryId); + m_messageQueue.push(e_message_commerceGetCategoryInfo); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::Checkout( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_checkoutInputParams.skuIds.clear(); + m_checkoutInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceCheckout); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::DownloadAlreadyPurchased( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_downloadInputParams.skuIds.clear(); + m_downloadInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceDownloadList); + LeaveCriticalSection(&m_queueLock); +} + +void SonyCommerce_Orbis::Checkout_Game( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_checkoutInputParams.skuIds.clear(); + m_checkoutInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceCheckout_Game); + LeaveCriticalSection(&m_queueLock); +} +void SonyCommerce_Orbis::DownloadAlreadyPurchased_Game( CallbackFunc cb, LPVOID lpParam, const char* skuID ) +{ + EnterCriticalSection(&m_queueLock); + setCallback(cb,lpParam); + m_downloadInputParams.skuIds.clear(); + m_downloadInputParams.skuIds.push_back(skuID); + m_messageQueue.push(e_message_commerceDownloadList_Game); + LeaveCriticalSection(&m_queueLock); +} + + +/* +bool g_bDoCommerceCreateSession = false; +bool g_bDoCommerceGetProductList = false; +bool g_bDoCommerceGetCategoryInfo = false; +bool g_bDoCommerceGetProductInfoDetailed = false; +bool g_bDoCommerceCheckout = false; +bool g_bDoCommerceCloseSession = false; +const char* g_category = "EP4433-CUSA00265_00"; +const char* g_skuID = "SKINPACK00000001-E001"; +std::vector g_productInfo; +SonyCommerce::CategoryInfo g_categoryInfo2; +SonyCommerce::ProductInfoDetailed g_productInfoDetailed; + +void testCallback(LPVOID lpParam, int error_code) +{ + app.DebugPrintf("Callback hit, error 0x%08x\n", error_code); +} + +void SonyCommerce_Orbis::Test() +{ + int err = SCE_OK; + if(g_bDoCommerceCreateSession) + { + CreateSession(testCallback, this); + g_bDoCommerceCreateSession = false; + } + if(g_bDoCommerceGetProductList) + { + GetProductList(testCallback, this, &g_productInfo, g_category); + g_bDoCommerceGetProductList = false; + } + + if(g_bDoCommerceGetCategoryInfo) + { + GetCategoryInfo(testCallback, this, &g_categoryInfo2, g_category); + g_bDoCommerceGetCategoryInfo = false; + } + + if(g_bDoCommerceGetProductInfoDetailed) + { + GetDetailedProductInfo(testCallback, this, &g_productInfoDetailed, g_productInfo[0].productId, g_category); + g_bDoCommerceGetProductInfoDetailed = false; + } + + if(g_bDoCommerceCheckout) + { + //Checkout(testCallback, this, g_skuID);//g_productInfoDetailed.skuId); + Checkout(testCallback, this, g_productInfoDetailed.skuId); + g_bDoCommerceCheckout = false; + } + if(g_bDoCommerceCloseSession) + { + CloseSession(); + g_bDoCommerceCloseSession = false; + } + +} +*/ \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h new file mode 100644 index 0000000..a2a42d5 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyCommerce_Orbis.h @@ -0,0 +1,182 @@ +#pragma once + +#include "Common\Network\Sony\SonyCommerce.h" +class OrbisNPToolkit; + +class SonyCommerce_Orbis : public SonyCommerce +{ + friend class OrbisNPToolkit; + enum State + { + e_state_noSession, + e_state_creatingSession, + e_state_createSessionDone, + e_state_idle, + + + }; + /// This enum is used to verify the current utility that is running + enum Phase + { + e_phase_stopped = 0, + e_phase_idle, + e_phase_voucherRedeemPhase, + e_phase_productBrowsePhase, + e_phase_creatingSessionPhase, + e_phase_checkoutPhase, + e_phase_downloadListPhase + }; + + enum Message + { + e_message_commerceNone, + e_message_commerceCreateSession, ///< Create a commerce session + e_message_commerceGetCategoryInfo, ///< Information about a category in the Store + e_message_commerceGetProductList, ///< Get a list of products available in the Store + e_message_commerceGetDetailedProductInfo, ///< Get a list of products available in the Store, with additional details + e_message_commerceAddDetailedProductInfo, ///< Add additional details to a ProdcutInfo already retrieved + e_message_commerceStoreProductBrowse, ///< Launches the Store to a specified product + e_message_commerceUpgradeTrial, ///< Upgrade a trial to full game + e_message_commerceRedeemVoucher, ///< Redeem a voucher code + e_message_commerceGetEntitlementList, ///< Get a list of entitlements associated with the current PSN user. + e_message_commerceConsumeEntitlement, ///< Consume an amount from a consumable entitlement. + e_message_commerceCheckout, ///< Launch the Store checkout + e_message_commerceDownloadList, ///< Launch the download list + e_message_commerceCheckout_Game, ///< Launch the Store checkout + e_message_commerceDownloadList_Game, ///< Launch the download list + e_message_commerceEnd ///< End commerce2 processing + }; + + enum Event + { + e_event_none, + e_event_commerceSessionCreated, ///< An event generated when a commerce session has successfully been created. + e_event_commerceSessionAborted, ///< An event generated when the creation of commerce session has been aborted. + e_event_commerceGotCategoryInfo, ///< An event generated when some category information has been retrieved from the store. + e_event_commerceGotProductList, ///< An event generated when a list of products that are available has been retrieved from the store. + e_event_commerceGotDetailedProductInfo, ///< An event generated when some detailed product information has been retrieved from the store. + e_event_commerceAddedDetailedProductInfo, ///< An event generated when some detailed product information has been retrieved from the store. + e_event_commerceProductBrowseStarted, ///< An event generated when product overlay has started. + e_event_commerceProductBrowseSuccess, ///< An event generated when a product browse was completed successfully, and the user purchased the product. + e_event_commerceProductBrowseAborted, ///< An event generated when a product browse was aborted by the user (the user pressed back). + e_event_commerceProductBrowseFinished, ///< An event generated when a product browse has finished and it is now safe to free memory. + e_event_commerceVoucherInputStarted, ///< An event generated when a voucher code input overlay was started. + e_event_commerceVoucherInputSuccess, ///< An event generated when a voucher code input completed successfully. + e_event_commerceVoucherInputAborted, ///< An event generated when a voucher code input was aborted by the user (user pressed back). + e_event_commerceVoucherInputFinished, ///< An event generated when a voucher code input has finished. It is now safe to free memory. + e_event_commerceGotEntitlementList, ///< An event generated when a the list of entitlements has been received for the current user. + e_event_commerceConsumedEntitlement, ///< An event generated when the has successfully consumed an entitlement. + e_event_commerceCheckoutStarted, ///< An event generated when a store checkout overlay has started. + e_event_commerceCheckoutSuccess, ///< An event generated when user has successfully purchased from the checkout. + e_event_commerceCheckoutAborted, ///< An event generated when the checkout was aborted by the user (user pressed back). + e_event_commerceCheckoutFinished, ///< An event generated when a store checkout overlay has finished. + e_event_commerceDownloadListStarted, ///< An event generated when a download list overlay has started. + e_event_commerceDownloadListSuccess, ///< An event generated when the user has ended the download list. + e_event_commerceDownloadListFinished, ///< An event generated when a download list overlay has finished. + e_event_commerceError ///< An event generated when a commerce error has occurred. + }; + + static bool m_bLicenseChecked; + static bool m_bCommerceInitialised; +// static SceNpCommerce2SessionInfo m_sessionInfo; + static State m_state; + static int m_errorCode; + static LPVOID m_callbackParam; + static Event m_event; + static Message m_message; + // static uint32_t m_requestID; + static void* m_receiveBuffer; + static std::vector *m_pProductInfoList; + static ProductInfoDetailed *m_pProductInfoDetailed; + static ProductInfo *m_pProductInfo; + static CategoryInfo* m_pCategoryInfo; + static char* m_pCategoryID; + static const char* m_pProductID; + static std::queue m_messageQueue; + static CallbackFunc m_callbackFunc; + static CheckoutInputParams m_checkoutInputParams; + static DownloadListInputParams m_downloadInputParams; +// static sys_memory_container_t m_memContainer; + static bool m_bUpgradingTrial; + static C4JThread* m_tickThread; + static CallbackFunc m_trialUpgradeCallbackFunc; + static LPVOID m_trialUpgradeCallbackParam; + static CRITICAL_SECTION m_queueLock; + + static void runCallback() + { + assert(m_callbackFunc); + CallbackFunc func = m_callbackFunc; + m_callbackFunc = NULL; + if(func) + func(m_callbackParam, m_errorCode); + m_errorCode = SCE_OK; + } + static void setCallback(CallbackFunc cb,LPVOID lpParam) + { + assert(m_callbackFunc == NULL); + m_callbackFunc = cb; + m_callbackParam = lpParam; + } + + + static uint32_t m_contextId; ///< The npcommerce2 context ID + static bool m_contextCreated; ///< npcommerce2 context ID created? + static Phase m_currentPhase; ///< Current commerce2 util +// static char m_commercebuffer[SCE_NP_COMMERCE2_RECV_BUF_SIZE]; + + + + static void commerce2Handler( const sce::Toolkit::NP::Event& event); + static void processMessage(); + static void processEvent(); + + static int createContext(); + static int createSession(); + static void setError(int err) { m_errorCode = err; } + static int getCategoryInfo(CategoryInfo *info, char *categoryId); + static int getProductList(std::vector* productList, char *categoryId); + static int getDetailedProductInfo(ProductInfoDetailed *info, const char *productId, char *categoryId); + static int addDetailedProductInfo(ProductInfo *info, const char *productId, char *categoryId); + static int checkout(CheckoutInputParams ¶ms); + static int downloadList(DownloadListInputParams ¶ms); + static int checkout_game(CheckoutInputParams ¶ms); + static int downloadList_game(DownloadListInputParams ¶ms); + static void UpgradeTrialCallback1(LPVOID lpParam,int err); + static void UpgradeTrialCallback2(LPVOID lpParam,int err); + static void Delete(); + static void copyCategoryInfo(CategoryInfo *pInfo, sce::Toolkit::NP::CategoryInfo *pNPInfo); + static void copyProductList(std::vector* pProductList, std::vector* pNPProductList); + static void copyDetailedProductInfo(ProductInfoDetailed *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo); + static void copyAddDetailedProductInfo(ProductInfo *pInfo, sce::Toolkit::NP::ProductInfoDetailed* pNPInfo); + + + static int commerceEnd(); + // static int upgradeTrial(); + + static int TickLoop(void* lpParam); + //void Test(); + + static void Init(); + static int Shutdown(); + + static void CheckForTrialUpgradeKey_Callback(LPVOID param, bool bFullVersion); + +public: + + virtual void CreateSession(CallbackFunc cb, LPVOID lpParam); + virtual void CloseSession(); + + virtual void GetCategoryInfo(CallbackFunc cb, LPVOID lpParam, CategoryInfo *info, const char *categoryId); + virtual void GetProductList(CallbackFunc cb, LPVOID lpParam, std::vector* productList, const char *categoryId); + virtual void GetDetailedProductInfo(CallbackFunc cb, LPVOID lpParam, ProductInfoDetailed* productInfoDetailed, const char *productId, const char *categoryId); + virtual void AddDetailedProductInfo( CallbackFunc cb, LPVOID lpParam, ProductInfo* productInfo, const char *productId, const char *categoryId ); + virtual void Checkout(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void DownloadAlreadyPurchased(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void Checkout_Game(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void DownloadAlreadyPurchased_Game(CallbackFunc cb, LPVOID lpParam, const char* skuID); + virtual void UpgradeTrial(CallbackFunc cb, LPVOID lpParam); + virtual void CheckForTrialUpgradeKey(); + virtual bool LicenseChecked(); + +}; diff --git a/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp new file mode 100644 index 0000000..7f7c62d --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.cpp @@ -0,0 +1,285 @@ +#include "stdafx.h" +#include "SonyHttp_Orbis.h" + +static const int sc_SSLHeapSize = (304 * 1024U); +static const int sc_HTTPHeapSize = (48 * 1024); +static const int sc_NetHeapSize = (16 * 1024); + +#define TEST_USER_AGENT "SimpleSample/1.00" + + +int SonyHttp_Orbis::libnetMemId = 0; +int SonyHttp_Orbis::libsslCtxId = 0; +int SonyHttp_Orbis::libhttpCtxId = 0; +bool SonyHttp_Orbis:: bInitialised = false; + + + + + +bool SonyHttp_Orbis::init() +{ + int ret = sceNetPoolCreate("simple", sc_NetHeapSize, 0); + assert(ret >= 0); + libnetMemId = ret; + + ret = sceSslInit(sc_SSLHeapSize); + assert(ret >= 0); + libsslCtxId = ret; + + ret = sceHttpInit(libnetMemId, libsslCtxId, sc_HTTPHeapSize); + assert(ret >= 0); + libhttpCtxId = ret; + + bInitialised = true; + return true; +} + +void SonyHttp_Orbis::shutdown() +{ + int ret = sceHttpTerm(libhttpCtxId); + assert(ret == SCE_OK); + + ret = sceSslTerm(libsslCtxId); + assert(ret == SCE_OK); + + /* libnet */ + ret = sceNetPoolDestroy(libnetMemId); + assert(ret == SCE_OK); + +} +void SonyHttp_Orbis::printSslError(SceInt32 sslErr, SceUInt32 sslErrDetail) +{ + switch (sslErr) + { + case (SCE_HTTPS_ERROR_CERT): /* Verify error */ + /* Internal error at verifying certificate*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_INTERNAL){ + app.DebugPrintf("ssl verify error: unexpcted error\n"); + } + /* Error of server certificate or CA certificate */ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_INVALID_CERT){ + app.DebugPrintf("ssl verify error: invalid server cert or CA cert\n"); + } + /* Server hostname and server certificate are mismatched*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_CN_CHECK){ + app.DebugPrintf("ssl verify error: invalid server hostname\n"); + } + /* Server certificate or CA certificate is expired.*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_NOT_AFTER_CHECK){ + app.DebugPrintf("ssl verify error: server cert or CA cert had expired\n"); + } + /* Server certificate or CA certificate is before validated.*/ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_NOT_BEFORE_CHECK){ + app.DebugPrintf("ssl verify error: server cert or CA cert isn't validated yet.\n"); + } + /* Unknown CA error */ + if (sslErrDetail & SCE_HTTPS_ERROR_SSL_UNKNOWN_CA){ + app.DebugPrintf("ssl verify error: unknown CA\n"); + } + break; + case (SCE_HTTPS_ERROR_HANDSHAKE): /* fail to ssl-handshake */ + app.DebugPrintf("ssl error: handshake error\n"); + break; + case (SCE_HTTPS_ERROR_IO): /* Error of Socket IO */ + app.DebugPrintf("ssl error: io error\n"); + break; + case (SCE_HTTP_ERROR_OUT_OF_MEMORY): /* Out of memory*/ + app.DebugPrintf("ssl error: out of memory\n"); + break; + case (SCE_HTTPS_ERROR_INTERNAL): /* Unexpected Internal Error*/ + app.DebugPrintf("ssl error: unexpcted error\n"); + break; + default: + break; + } + return; +} + + +void SonyHttp_Orbis::printSslCertInfo(int libsslCtxId,SceSslCert *sslCert) +{ + SceInt32 ret; + SceUChar8 *sboData = NULL ; + SceSize sboLen, counter; + + ret = sceSslGetSerialNumber(libsslCtxId, sslCert, NULL, &sboLen); + if (ret < 0){ + app.DebugPrintf("sceSslGetSerialNumber() returns 0x%x\n", ret); + } + else { + sboData = (SceUChar8*)malloc(sboLen); + if ( sboData != NULL ) { + ret = sceSslGetSerialNumber(libsslCtxId, sslCert, sboData, &sboLen); + if (ret < 0){ + app.DebugPrintf ("sceSslGetSerialNumber() returns 0x%x\n", ret); + } + else { + app.DebugPrintf("Serial number="); + for (counter = 0; counter < sboLen; counter++){ + app.DebugPrintf("%02X", sboData[counter]); + } + app.DebugPrintf("\n"); + } + free(sboData); + } + } +} + + +bool SonyHttp_Orbis::getDataFromURL( const char* szURL, void** ppOutData, int* pDataSize) +{ + if(!bInitialised) + return false; + return http_get(szURL, ppOutData, pDataSize); +} + + +SceInt32 SonyHttp_Orbis::sslCallback(int libsslCtxId,unsigned int verifyErr,SceSslCert * const sslCert[],int certNum,void *userArg) +{ + SceInt32 i; + (void)userArg; + + app.DebugPrintf("Ssl callback:\n"); + app.DebugPrintf("\tbase tmpl[%x]\n", (*(SceInt32*)(userArg)) ); + + if (verifyErr != 0){ + printSslError((SceInt32)SCE_HTTPS_ERROR_CERT, verifyErr); + } + for (i = 0; i < certNum; i++){ + printSslCertInfo(libsslCtxId,sslCert[i]); + } + if (verifyErr == 0){ + return SCE_OK; + } else { + return -1; + } +} + +bool SonyHttp_Orbis::http_get_close(bool bOK, SceInt32 tmplId, SceInt32 connId, SceInt32 reqId) +{ + SceInt32 ret; + if (reqId > 0) + { + ret = sceHttpDeleteRequest(reqId); + assert(ret >= 0); + } + if (connId > 0) + { + ret = sceHttpDeleteConnection(connId); + assert(ret >= 0); + } + if (tmplId > 0) + { + ret = sceHttpDeleteTemplate(tmplId); + assert(ret >= 0); + } + assert(bOK); + return bOK; +} + +bool SonyHttp_Orbis::http_get(const char *targetUrl, void** ppOutData, int* pDataSize) +{ + SceInt32 ret, tmplId=0, connId=0, reqId=0, statusCode; + SceULong64 contentLength=0; + SceBool finFlag=SCE_FALSE; + SceUChar8* recvBuf; + + ret = sceHttpCreateTemplate(libhttpCtxId, TEST_USER_AGENT, SCE_HTTP_VERSION_1_1, SCE_TRUE); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateTemplate() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + tmplId = ret; + + /* Perform http_get without server verification */ + ret = sceHttpsDisableOption(tmplId,SCE_HTTPS_FLAG_SERVER_VERIFY); + if (ret < 0) + { + app.DebugPrintf("sceHttpsDisableOption() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + /* Register SSL callback */ + ret = sceHttpsSetSslCallback(tmplId, sslCallback, (void*)&tmplId); + if (ret < 0) + { + app.DebugPrintf("sceHttpsSetSslCallback() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + ret = sceHttpCreateConnectionWithURL(tmplId, targetUrl, SCE_TRUE); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateConnectionWithURL() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + connId = ret; + + ret = sceHttpCreateRequestWithURL(connId, SCE_HTTP_METHOD_GET, targetUrl, 0); + if (ret < 0) + { + app.DebugPrintf("sceHttpCreateRequestWithURL() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + reqId = ret; + + ret = sceHttpSendRequest(reqId, NULL, 0); + if (ret < 0) + { + app.DebugPrintf("sceHttpSendRequest() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + + ret = sceHttpGetStatusCode(reqId, &statusCode); + if (ret < 0) + { + app.DebugPrintf("sceHttpGetStatusCode() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + app.DebugPrintf("response code = %d\n", statusCode); + + if(statusCode == 200) + { + int contentLengthType; + ret = sceHttpGetResponseContentLength(reqId, &contentLengthType, &contentLength); + if(ret < 0) + { + app.DebugPrintf("sceHttpGetContentLength() error: 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + else + { + if (contentLengthType == SCE_HTTP_CONTENTLEN_EXIST) + { + app.DebugPrintf("Content-Length = %lu\n", contentLength); + } + } + recvBuf = new SceUChar8[contentLength+1]; + int bufferLeft = contentLength+1; + SceUChar8* pCurrBuffPos = recvBuf; + int totalBytesRead = 0; + while(finFlag != SCE_TRUE) + { + ret = sceHttpReadData(reqId, pCurrBuffPos, bufferLeft); + if (ret < 0) + { + app.DebugPrintf("\n sceHttpReadData() failed 0x%08X\n", ret); + return http_get_close(false, tmplId, connId, reqId); + } + else if (ret == 0) + { + finFlag = SCE_TRUE; + } + app.DebugPrintf("\n sceHttpReadData() read %d bytes\n", ret); + pCurrBuffPos += ret; + totalBytesRead += ret; + bufferLeft -= ret; + } + } + + *ppOutData = recvBuf; + *pDataSize = contentLength; + return http_get_close(true, tmplId, connId, reqId); +} diff --git a/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h new file mode 100644 index 0000000..591a62c --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyHttp_Orbis.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +class SonyHttp_Orbis +{ + static SceInt32 sslCallback(int libsslCtxId,unsigned int verifyErr,SceSslCert * const sslCert[],int certNum,void *userArg); + static bool http_get(const char *targetUrl, void** ppOutData, int* pDataSize); + static bool http_get_close(bool bOK, SceInt32 tmplId, SceInt32 connId, SceInt32 reqId); + + static void printSslError(SceInt32 sslErr, SceUInt32 sslErrDetail); + static void printSslCertInfo(int libsslCtxId,SceSslCert *sslCert); + + static int libnetMemId; + static int libsslCtxId; + static int libhttpCtxId; + + static bool bInitialised; + +public: + bool init(); + void shutdown(); + bool getDataFromURL(const char* szURL, void** ppOutData, int* pDataSize); + + static int getHTTPContextID() { return libhttpCtxId; } +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp new file mode 100644 index 0000000..e248f60 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.cpp @@ -0,0 +1,371 @@ +#include "stdafx.h" + +#include "SonyRemoteStorage_Orbis.h" +#include "SonyHttp_Orbis.h" +#include +#include +#include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + + + + +#define AUTH_SCOPE "psn:s2s" +#define CLIENT_ID "969e9d21-527c-4c22-b539-f8e479f690bc" +static SceRemoteStorageData s_getDataOutput; + + +void SonyRemoteStorage_Orbis::staticInternalCallback(const SceRemoteStorageEvent event, int32_t retCode, void * userData) +{ + ((SonyRemoteStorage_Orbis*)userData)->internalCallback(event, retCode); +} +void SonyRemoteStorage_Orbis::internalCallback(const SceRemoteStorageEvent event, int32_t retCode) +{ + m_lastErrorCode = retCode; + + switch(event) + { + case ERROR_OCCURRED: + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred with retCode: 0x%x \n", retCode); + m_status = e_error; +// shutdown(); // removed, as the remote storage lib now tries to reconnect if an error has occurred + runCallback(); + break; + + case GET_DATA_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Get Data success \n"); + m_status = e_getDataSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Get Data was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + if(StorageManager.SaveGameDirUnMount(m_mountPoint) == false) + { + app.DebugPrintf("Failed to unmount %s\n", m_mountPoint); + } + + m_mountPoint[0] = 0; + runCallback(); + break; + + case GET_DATA_PROGRESS: + app.DebugPrintf("SonyRemoteStorage_Orbis: Get data progress: %i%%\n", retCode); + m_status = e_getDataInProgress; + m_dataProgress = retCode; + m_startTime = System::currentTimeMillis(); + break; + + case GET_STATUS_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Get Status success \n"); + app.DebugPrintf("SonyRemoteStorage_Orbis: Remaining Syncs for this user: %llu\n", outputGetStatus->remainingSyncs); + app.DebugPrintf("SonyRemoteStorage_Orbis: Number of files on the cloud: %d\n", outputGetStatus->numFiles); + for(int i = 0; i < outputGetStatus->numFiles; i++) + { + app.DebugPrintf("\n*** File %d information: ***\n", (i + 1)); + app.DebugPrintf("File name: %s \n", outputGetStatus->data[i].fileName); + app.DebugPrintf("File description: %s \n", outputGetStatus->data[i].fileDescription); + app.DebugPrintf("MD5 Checksum: %s \n", outputGetStatus->data[i].md5Checksum); + app.DebugPrintf("Size of the file: %u bytes \n", outputGetStatus->data[i].fileSize); + app.DebugPrintf("Timestamp: %s \n", outputGetStatus->data[i].timeStamp); + app.DebugPrintf("Visibility: \"%s\" \n", (outputGetStatus->data[i].visibility == 0)?"Private":((outputGetStatus->data[i].visibility == 1)?"Public read only":"Public read and write")); + } + m_status = e_getStatusSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Get Status was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + runCallback(); + break; + + case PSN_SIGN_IN_REQUIRED: + app.DebugPrintf("SonyRemoteStorage_Orbis: User's PSN sign-in through web browser is required \n"); + m_status = e_signInRequired; + runCallback(); + break; + + case SET_DATA_RESULT: + if(retCode >= 0) + { + app.DebugPrintf("SonyRemoteStorage_Orbis: Set Data success \n"); + m_status = e_setDataSucceeded; + } + else + { + app.DebugPrintf("SonyRemoteStorage_Orbis: An error occurred while Set Data was being processed. retCode: 0x%x \n", retCode); + m_status = e_error; + } + runCallback(); + break; + + case SET_DATA_PROGRESS: + app.DebugPrintf("SonyRemoteStorage_Orbis: Set data progress: %i%%\n", retCode); + m_status = e_setDataInProgress; + m_dataProgress = retCode; + + break; + + case USER_ACCOUNT_LINKED: + app.DebugPrintf("SonyRemoteStorage_Orbis: User's account has been linked with PSN \n"); + m_bInitialised = true; + m_status = e_accountLinked; + runCallback(); + break; + + case WEB_BROWSER_RESULT: + app.DebugPrintf("SonyRemoteStorage_Orbis: This function is not used on PS Vita, as the account will be linked, it is not needed to open a browser to link it \n"); + assert(0); + break; + + default: + app.DebugPrintf("SonyRemoteStorage_Orbis: This should never happen \n"); + assert(0); + break; + + } +} + +bool SonyRemoteStorage_Orbis::init(CallbackFunc cb, LPVOID lpParam) +{ + + int ret = 0; + int reqId = 0; + SceNpId npId; + SceUserServiceUserId userId; + SceRemoteStorageInitParams params; + + m_callbackFunc = cb; + m_callbackParam = lpParam; + + if(m_bInitialised) + { + runCallback(); + return true; + } + + ret = sceUserServiceGetInitialUser(&userId); + if (ret < 0) + { + app.DebugPrintf("Couldn't retrieve user ID 0x%x\n", ret); + return false; + } + + ret = sceNpGetNpId(userId, &npId); + if (ret < 0) { + app.DebugPrintf("Couldn't retrieve NP ID 0x%x\n", ret); + return false; + } + +// ret = sceNpAuthCreateRequest(); +// if (ret < 0) { +// app.DebugPrintf("Couldn't create auth request 0x%x\n", ret); +// return false; +// } +// +// reqId = ret; +// +// SceNpClientId clientId; +// memset(&clientId, 0x0, sizeof(clientId)); + +// SceNpAuthorizationCode authCode; +// memset(&authCode, 0x0, sizeof(authCode)); + +// SceNpAuthGetAuthorizationCodeParameter authParams; +// memset(&authParams, 0x0, sizeof(authParams)); +// +// authParams.size = sizeof(authParams); +// authParams.pOnlineId = &npId.handle; +// authParams.pScope = AUTH_SCOPE; + +// memcpy(clientId.id, CLIENT_ID, strlen(CLIENT_ID)); +// authParams.pClientId = &clientId; + +// ret = sceNpAuthGetAuthorizationCode(reqId, &authParams, &authCode, NULL); +// if (ret < 0) { +// app.DebugPrintf("Failed to get auth code 0x%x\n", ret); +// } + +// ret = sceNpAuthDeleteRequest(reqId); +// if (ret < 0) { +// app.DebugPrintf("Couldn't delete auth request 0x%x\n", ret); +// } + + params.callback = staticInternalCallback; + params.userData = this; +// memcpy(params.authCode, authCode.code, SCE_NP_AUTHORIZATION_CODE_MAX_LEN); + params.userId = userId; + params.environment = DEVELOPMENT; + params.httpContextId = SonyHttp_Orbis::getHTTPContextID(); + strcpy(params.clientId, CLIENT_ID); + + params.thread.threadAffinity = SCE_KERNEL_CPUMASK_USER_ALL; + params.thread.threadPriority = SCE_KERNEL_PRIO_FIFO_DEFAULT; + + params.timeout.connectMs = 30 * 1000; //30 seconds is the default + params.timeout.resolveMs = 30 * 1000; //30 seconds is the default + params.timeout.receiveMs = 120 * 1000; //120 seconds is the default + params.timeout.sendMs = 120 * 1000; //120 seconds is the default + + params.pool.memPoolSize = 7 * 1024 * 1024; + if(m_memPoolBuffer == NULL) + m_memPoolBuffer = malloc(params.pool.memPoolSize); + + params.pool.memPoolBuffer = m_memPoolBuffer; + + ret = sceRemoteStorageInit(params); + if (ret >= 0) + { + app.DebugPrintf("Session will be created \n"); + } + else if(ret == SCE_REMOTE_STORAGE_ERROR_ALREADY_INITIALISED) + { + app.DebugPrintf("Already initialised: 0x%x \n", ret); + runCallback(); + } + else + { + app.DebugPrintf("Error creating session: 0x%x \n", ret); + return false; + } + return true; +} + + + +bool SonyRemoteStorage_Orbis::getRemoteFileInfo(SceRemoteStorageStatus* pInfo, CallbackFunc cb, LPVOID lpParam) +{ + m_callbackFunc = cb; + m_callbackParam = lpParam; + outputGetStatus = pInfo; + + SceRemoteStorageStatusReqParams params; + reqId = sceRemoteStorageGetStatus(params, outputGetStatus); + m_status = e_getStatusInProgress; + + if(reqId >= 0) + { + app.DebugPrintf("Get Status request sent \n"); + return true; + } + else + { + app.DebugPrintf("Error sending Get Status request: 0x%x \n", reqId); + return false; + } +} + +void SonyRemoteStorage_Orbis::abort() +{ + SceRemoteStorageAbortReqParams params; + params.requestId = reqId; + int ret = sceRemoteStorageAbort(params); + + if(ret >= 0) + { + app.DebugPrintf("Abort request done \n"); + } + else + { + app.DebugPrintf("Error in Abort request: 0x%x \n", ret); + } +} + + +bool SonyRemoteStorage_Orbis::setData( PSAVE_INFO info, CallbackFunc cb, LPVOID lpParam ) +{ + assert(0); + return false; + +// m_callbackFunc = cb; +// m_callbackParam = lpParam; +// +// strcpy(m_saveFilename, savePath); +// strcpy(m_saveFileDesc, saveDesc); +// m_status = e_setDataInProgress; +// +// +// SceRemoteStorageSetDataReqParams params; +// params.visibility = PUBLIC_READ_WRITE; +// strcpy(params.pathLocation, m_saveFilename); +// sprintf(params.fileName, "/%s/GAMEDATA", m_saveFilename); +// // strcpy(params.fileName, "/test/small.txt"); +// strcpy(params.fileDescription, m_saveFileDesc); +// strcpy(params.ps3DataFilename, "GAMEDATA"); +// +// params.ps3FileType = CELL_SAVEDATA_FILETYPE_NORMALFILE; +// memcpy(params.secureFileId, m_secureFileId, CELL_SAVEDATA_SECUREFILEID_SIZE); +// +// +// +// reqId = sceRemoteStorageSetData(params); +// +// app.DebugPrintf("\n*******************************\n"); +// if(reqId >= 0) +// { +// app.DebugPrintf("Set Data request sent \n"); +// return true; +// } +// else +// { +// app.DebugPrintf("Error sending Set Data request: 0x%x \n", reqId); +// return false; +// } +} + +bool SonyRemoteStorage_Orbis::getData( const char* remotePath, const char* localPath, CallbackFunc cb, LPVOID lpParam ) +{ + m_callbackFunc = cb; + m_callbackParam = lpParam; + + if(StorageManager.SaveGameDirMountExisting(m_mountPoint) == false) + { + app.DebugPrintf("Error mounting save dir \n"); + m_mountPoint[0] = 0; + return false; + } + + SceRemoteStorageGetDataReqParams params; + sprintf(params.pathLocation, "%s/GAMEDATA", m_mountPoint); + // strcpy(params.fileName, "/test/small.txt"); + strcpy(params.fileName, remotePath); + SceRemoteStorageData s_getDataOutput; + reqId = sceRemoteStorageGetData(params, &s_getDataOutput); + + app.DebugPrintf("\n*******************************\n"); + if(reqId >= 0) + { + app.DebugPrintf("Get Data request sent \n"); + return true; + } + else + { + app.DebugPrintf("Error sending Get Data request: 0x%x \n", reqId); + return false; + } +} + +void SonyRemoteStorage_Orbis::runCallback() +{ + assert(m_callbackFunc); + if(m_callbackFunc) + { + m_callbackFunc(m_callbackParam, m_status, m_lastErrorCode); + } + m_lastErrorCode = SCE_OK; +} diff --git a/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h new file mode 100644 index 0000000..0b36c41 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyRemoteStorage_Orbis.h @@ -0,0 +1,42 @@ +#pragma once + + +#include "Common\Network\Sony\SonyRemoteStorage.h" + +class SonyRemoteStorage_Orbis : public SonyRemoteStorage +{ +public: + + + virtual bool init(CallbackFunc cb, LPVOID lpParam); + virtual bool setData(PSAVE_INFO info, CallbackFunc cb, LPVOID lpParam); + + virtual bool getRemoteFileInfo(SceRemoteStorageStatus* pInfo, CallbackFunc cb, LPVOID lpParam); + virtual bool getData(const char* remotePath, const char* localPath, CallbackFunc cb, LPVOID lpParam); + + virtual void abort(); + virtual bool setDataInternal(){ assert(0); } + +private: + int reqId; + void * psnTicket; + size_t psnTicketSize; + bool m_waitingForTicket; + bool initialized; + SceRemoteStorageStatus* outputGetStatus; + SceRemoteStorageData outputGetData; + + int32_t m_lastErrorCode; + int m_getDataProgress; + int m_setDataProgress; + char m_saveFilename[SCE_REMOTE_STORAGE_DATA_NAME_MAX_LEN]; + char m_saveFileDesc[SCE_REMOTE_STORAGE_DATA_DESCRIPTION_MAX_LEN]; + char m_remoteFilename[SCE_REMOTE_STORAGE_DATA_NAME_MAX_LEN]; + char m_mountPoint[SCE_SAVE_DATA_MOUNT_POINT_DATA_MAXSIZE]; + + static void staticInternalCallback(const SceRemoteStorageEvent event, int32_t retCode, void * userData); + void internalCallback(const SceRemoteStorageEvent event, int32_t retCode); + + void runCallback(); +}; + diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp new file mode 100644 index 0000000..4777e59 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.cpp @@ -0,0 +1,355 @@ +#include "stdafx.h" + +#include "SonyVoiceChat_Orbis.h" + + + +bool m_bIsPartyAPIInitialized; +bool m_bIsPartyBinaryMessageAPIReady; + +SceNpPartyState m_stPartyState; + + SonyVoiceChatParty_Orbis::PartyInfo SonyVoiceChatParty_Orbis::m_partyInfo; + +void SonyVoiceChatParty_Orbis::init() +{ +// m_bIsRunning = false; +// m_pRenderer = NULL; +// m_pPadContext = NULL; +// m_pGraphicsContext = NULL; +// m_iPartyMemberCount = 0; +// m_iPartyLeader = 0; + int ret = SCE_OK; + SceNpPartyInitializeParam stPartyInit; + SceUserServiceLoginUserIdList userIdList; + + + // Run PartyInitializeParamInitialize inline + sceNpPartyInitializeParamInitialize( &stPartyInit ); + + // Initialize the Party API + ret = sceNpPartyInitialize( &stPartyInit ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyInitialize failed result:0x%x\n", ret ); + return; + } + + // Register handlers for party room events + SceNpPartyEventHandlers stPartyEventHandler; + memset( &stPartyEventHandler, 0, sizeof( SceNpPartyEventHandlers ) ); + stPartyEventHandler.roomEventHandler = partyRoomEventHandler; + stPartyEventHandler.voiceEventHandler = partyVoiceEventHandler; + stPartyEventHandler.binaryMessageEventHandler = partyBinaryMessageEventHandler; + ret = sceNpPartyRegisterHandler( &stPartyEventHandler, NULL ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyRegisterHandler failed result:0x%x\n", ret ); + return; + } + + + // Get current party state + ret = sceNpPartyGetState( &m_stPartyState ); + if( ret != SCE_OK ) + { + app.DebugPrintf( "Error: sceNpPartyGetState failed result:0x%x\n", ret ); + return; + } + + m_bIsPartyAPIInitialized = true; +} + +void SonyVoiceChatParty_Orbis::shutdown() +{ +} + +void SonyVoiceChatParty_Orbis::setEnabled( bool bEnabled ) +{ +} + + + + +void SonyVoiceChatParty_Orbis::tick() +{ + sceNpPartyCheckCallback(); +} + + + +bool SonyVoiceChatParty_Orbis::hasMicConnected(const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + { + // in the party, might not have a mic though, still need to check for this + return true; + } + return false; +} + +void SonyVoiceChatParty_Orbis::mute( bool bMute ) +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetVoiceMuting(bMute); +// assert(err == CELL_OK); +// } +} + +void SonyVoiceChatParty_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */ +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetPlayerVoiceMuting(member_id, bMute); +// assert(err == CELL_OK); +// } +} + +void SonyVoiceChatParty_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */ +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// int err = cellSysutilAvc2SetVoiceMuting(bMute); +// assert(err == CELL_OK); +// } +} + + +bool SonyVoiceChatParty_Orbis::isMuted() +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetVoiceMuting(&bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + +bool SonyVoiceChatParty_Orbis::isMutedPlayer( const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + return pInfo->m_voiceMuted; + assert(0 && "Didn't find playerUID"); // + return false; +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetPlayerVoiceMuting(member_id, &bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + +bool SonyVoiceChatParty_Orbis::isMutedLocalPlayer() +{ +// if(sm_bLoaded && !sm_bUnloading) +// { +// uint8_t bMute; +// int err = cellSysutilAvc2GetVoiceMuting(&bMute); +// assert(err == CELL_OK); +// return bMute; +// } +// return false; +} + + +bool SonyVoiceChatParty_Orbis::isTalking( const PlayerUID& memberUID) +{ + MemberInfo* pInfo = m_partyInfo.getMember(memberUID); + if(pInfo) + { + DWORD currTime = GetTickCount(); + DWORD timeElapsed = currTime - pInfo->m_lastTimeTalking; + return (timeElapsed < 1000); + } + assert(0 && "Didn't find playerUID"); // + return false; +} + + + + +void SonyVoiceChatParty_Orbis::partyRoomEventHandler(SceNpPartyRoomEventType eventType, const void* data, void* userdata) +{ + + switch(eventType) + { + case SCE_NP_PARTY_ROOM_EVENT_JOINED: + { + // local player joined party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_JOINED\n"); + SceNpPartyJoinedInfo* pPartyJoinedInfo = (SceNpPartyJoinedInfo*) data; + + m_partyInfo.clear(); + app.DebugPrintf("Local user joined party.\n"); + for(int i=0; imemberNum;i++) + { + m_partyInfo.addMember(pPartyJoinedInfo->memberIds[i], pPartyJoinedInfo->members[i], false, false); + app.DebugPrintf("Member room ID : %d - Member PSN ID : %s\n", pPartyJoinedInfo->memberIds[i], pPartyJoinedInfo->members[i].handle.data); + } + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_MEMBER_JOINED: + { + // remote player joined party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_MEMBER_JOINED\n"); + SceNpPartyMemberInfo *pNewPartyMemberInfo = (SceNpPartyMemberInfo*) data; + app.DebugPrintf("A remote player[%s] has a joined the party.\n",pNewPartyMemberInfo->npId.handle.data); + + m_partyInfo.addMember(pNewPartyMemberInfo->memberId, pNewPartyMemberInfo->npId, pNewPartyMemberInfo->memberFlags & SCE_NP_PARTY_MEMBER_FLAG_IS_LOCAL, pNewPartyMemberInfo->memberFlags & SCE_NP_PARTY_MEMBER_FLAG_IS_PARTY_LEADER); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_MEMBER_LEFT: + { + // remote player left party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_MEMBER_LEFT\n"); + SceNpPartyMemberInfo *pLeavingPartyMemberInfo = (SceNpPartyMemberInfo*) data; + app.DebugPrintf("A remote player[%s] has left the party.\n", pLeavingPartyMemberInfo->npId.handle.data); + m_partyInfo.removeMember(pLeavingPartyMemberInfo->memberId); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_LEFT: + { + // local player left party + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_LEFT\n"); + SceNpPartyRoomLeftInfo *pLeftInfo = (SceNpPartyRoomLeftInfo*)data; + printf("Local party member[%s] id:%d has left the party. Reason[%d]\n", pLeftInfo->npId.handle.data, pLeftInfo->memberId, pLeftInfo->reason); + m_partyInfo.removeMember(pLeftInfo->memberId); + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_CREATE_RESPONSE: + { + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_CREATE_RESPONSE\n"); + SceNpPartyCreateResponseInfo *pCreateResponseInfo = (SceNpPartyCreateResponseInfo*) data; + + switch( pCreateResponseInfo->status ) + { + case SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_OK: + app.DebugPrintf( "Local party member userId[0x%x] response for creating a party. Status = SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_OK\n" , pCreateResponseInfo->userId ); + break; + case SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_CANCELLED: + app.DebugPrintf( "Local party member userId[0x%x] response for creating a party. Status = SCE_NP_PARTY_ROOM_CREATE_RESPONSE_STATUS_CANCELLED\n" , pCreateResponseInfo->userId ); + break; + default: + app.DebugPrintf( "Warning: Unknown SceNpPartyCreateResponseStatus [%d]. Ignore.\n", pCreateResponseInfo->status ); + break; + } + } + break; + + case SCE_NP_PARTY_ROOM_EVENT_SHOW_INVITATION_RESPONSE: + { + app.DebugPrintf("SCE_NP_PARTY_ROOM_EVENT_SHOW_INVITATION_RESPONSE\n"); + SceNpPartyShowInvitationResponseInfo *pShowInvitationResponseInfo = (SceNpPartyShowInvitationResponseInfo*) data; + + switch( pShowInvitationResponseInfo->status ) + { + case SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_OK: + app.DebugPrintf( "Local party member userId[0x%x] response for sending an invitation/suggestion. Status = SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_OK\n" , pShowInvitationResponseInfo->userId ); + break; + case SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_CANCELLED: + app.DebugPrintf( "Local party member userId[0x%x] response for sending an invitation/suggestion. Status = SCE_NP_PARTY_ROOM_SHOW_INVITATION_RESPONSE_STATUS_CANCELLED\n" , pShowInvitationResponseInfo->userId ); + break; + default: + app.DebugPrintf( "Warning: Unknown SceNpPartyShowInvitationResponseStatus [%d]. Ignore.\n", pShowInvitationResponseInfo->status ); + break; + } + } + break; + + default: + { + app.DebugPrintf( "Warning: Un-handled party event[%d]. Ignore.\n", eventType ); + } + break; + + } + +} + + + +void SonyVoiceChatParty_Orbis::partyVoiceEventHandler( const SceNpPartyMemberVoiceInfo* memberVoiceInfo, void* userdata ) +{ + + switch(memberVoiceInfo->memberVoiceState) + { + case SCE_NP_PARTY_MEMBER_VOICE_STATE_DISCONNECTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_DISCONNECTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceConnected = false; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_CONNECTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_CONNECTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceConnected = true; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_TALKING: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_TALKING\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + { + pInfo->m_voiceMuted = false; + pInfo->m_lastTimeTalking = GetTickCount(); + } + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_MUTED: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_MUTED\n"); + MemberInfo* pInfo = m_partyInfo.getMember(memberVoiceInfo->memberId); + assert(pInfo); + if(pInfo) + pInfo->m_voiceMuted = true; + } + break; + case SCE_NP_PARTY_MEMBER_VOICE_STATE_UNKNOWN: + { + app.DebugPrintf("SCE_NP_PARTY_MEMBER_VOICE_STATE_UNKNOWN\n"); + } + break; + default: + { + app.DebugPrintf("Warning: Un-handled voice event. Ignore.\n"); + } + break; + } +} + + +void SonyVoiceChatParty_Orbis::partyBinaryMessageEventHandler( SceNpPartyBinaryMessageEvent event, + const void *data, + void * userdata + ) +{ + switch(event) + { + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_READY: + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_DATA: + case SCE_NP_PARTY_BINARY_MESSAGE_EVENT_COMPATIBILITY: + default: +// app.DebugPrintf("partyBinaryMessageEventHandler not supported"); +// assert(0); + break; + } +} diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h new file mode 100644 index 0000000..84f19c2 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChatParty_Orbis.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +#include + +class SonyVoiceChatParty_Orbis +{ +public: + + static void init(); + static void shutdown(); + static void tick(); + static void setEnabled(bool bEnabled); + static bool hasMicConnected(const PlayerUID& memberUID); + static bool isTalking( const PlayerUID& memberUID); + static void mute(bool bMute); //Turn chat audio on or off + static void mutePlayer(const SceNpMatching2RoomMemberId member_id, bool bMute); //Turn chat audio from a specified player on or off; + static void muteLocalPlayer(bool bMute); //Turn microphone input on or off; + + static bool isMuted(); + static bool isMutedPlayer(const PlayerUID& memberUID); + static bool isMutedLocalPlayer(); //Turn microphone input on or off; + + +private: + //NP Party Event Handlers + static void partyRoomEventHandler(SceNpPartyRoomEventType eventType,const void* data, void* userdata); + static void partyVoiceEventHandler(const SceNpPartyMemberVoiceInfo* memberVoiceInfo,void* userdata); + static void partyBinaryMessageEventHandler(SceNpPartyBinaryMessageEvent event, const void *data, void * userdata); + + class MemberInfo + { + public: + SceNpId m_NpId; + SceNpPartyRoomMemberId m_roomID; + bool m_voiceConnected; + bool m_voiceMuted; + DWORD m_lastTimeTalking; + bool m_localUser; + bool m_partyLeader; + MemberInfo() + { + m_voiceConnected = false; + m_voiceMuted = false; + m_lastTimeTalking = 0; + m_localUser = false; + m_partyLeader = false; + } + }; + class PartyInfo + { + public: + int m_numMembers; + MemberInfo m_members[SCE_NP_PARTY_MEMBER_NUM_MAX]; + bool m_privateParty; + + MemberInfo* getMember(SceNpPartyRoomMemberId roomID) + { + for(int i=0;i= 0); + + m_numMembers--; + if(m_numMembers > 0) + m_members[index] = m_members[m_numMembers]; + } + void clear() { m_numMembers = 0;} + }; + static PartyInfo m_partyInfo; +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp new file mode 100644 index 0000000..d869d38 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.cpp @@ -0,0 +1,1105 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include + +#include "SonyVoiceChat_Orbis.h" + +std::vector SonyVoiceChat_Orbis::m_remoteConnections; +bool SonyVoiceChat_Orbis::m_bVoiceStarted = false; +int SonyVoiceChat_Orbis::m_numLocalDevicesConnected = 0; +SQRLocalVoiceDevice SonyVoiceChat_Orbis::m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT]; +uint32_t SonyVoiceChat_Orbis::m_voiceOutPort; +bool SonyVoiceChat_Orbis::m_forceSendPacket = false; // force a packet across the network, even if there's no data, so we can update flags +RingBuffer SonyVoiceChat_Orbis::m_recordRingBuffer(sc_ringBufferSize); +VoicePacket::Flags SonyVoiceChat_Orbis::m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; +bool SonyVoiceChat_Orbis::m_bInitialised = false; +CRITICAL_SECTION SonyVoiceChat_Orbis::m_csRemoteConnections; + +// sample related variables +SceVoiceStartParam startParam; +int32_t playSize = 0; + +static const int sc_thresholdValue = 100; + +static const bool sc_verbose = false; + +// #define _USE_PCM_AUDIO_ +//#define USE_PCM_MIC_DATA + + + +int g_loadedPCMVoiceDataSizes[4]; +int g_loadedPCMVoiceDataPos[4]; +char* g_loadedPCMVoiceData[4]; + +static void CreatePort(uint32_t *portId, const SceVoicePortParam *pArg) +{ + C4JThread::PushAffinityAllCores(); // PS4 only + + int err = sceVoiceCreatePort( portId, pArg ); + assert(err == SCE_OK); + assert(*portId != SCE_VOICE_INVALID_PORT_ID); + C4JThread::PopAffinity(); // PS4 only +} + +static void DeletePort(uint32_t& port) +{ + int32_t result; + if (port != SCE_VOICE_INVALID_PORT_ID) + { + result = sceVoiceDeletePort( port ); + if (result != SCE_OK) + { + app.DebugPrintf("sceVoiceDeletePort failed %0x\n", result); + assert(0); + } + port = SCE_VOICE_INVALID_PORT_ID; + } +} + + +void LoadPCMVoiceData() +{ + for(int i=0;i<4;i++) + { + char filename[64]; + sprintf(filename, "voice%d.pcm", i+1); + HANDLE file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + DWORD dwHigh=0; + g_loadedPCMVoiceDataSizes[i] = GetFileSize(file,&dwHigh); + + if(g_loadedPCMVoiceDataSizes[i]!=0) + { + g_loadedPCMVoiceData[i] = new char[g_loadedPCMVoiceDataSizes[i]]; + DWORD bytesRead; + BOOL bSuccess = ReadFile(file, g_loadedPCMVoiceData[i], g_loadedPCMVoiceDataSizes[i], &bytesRead, NULL); + assert(bSuccess); + } + g_loadedPCMVoiceDataPos[i] = 0; + } +} + + +void SonyVoiceChat_Orbis::init() +{ + int returnCode = SCE_OK; + SceUserServiceUserId initialUserId; + + if (returnCode < 0) + { + app.DebugPrintf("Error: sceSysmoduleLoadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode); + assert(0); + } + + + SceVoiceInitParam params; + SceVoicePortParam portArgs; + memset( ¶ms, 0, sizeof(params) ); + params.appType = SCE_VOICE_APPTYPE_GAME; + params.onEvent = 0; + returnCode = sceVoiceInit( ¶ms , SCE_VOICE_VERSION_100); + if (returnCode < 0) + { + app.DebugPrintf("Error: sceVoiceInit(), ret 0x%08x\n", returnCode); + assert(0); + } + +#ifdef _USE_PCM_AUDIO_ + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.format.sampleRate = SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.bufSize = 4096; +#else + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_VOICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.voice.bitrate = VOICE_ENCODED_FORMAT; +#endif + CreatePort( &m_voiceOutPort, &portArgs ); + + start(); + m_bInitialised = true; + InitializeCriticalSection(&m_csRemoteConnections); +} + + +void SonyVoiceChat_Orbis::start() +{ + if( m_bVoiceStarted == false) + { + startParam.container = malloc(SCE_VOICE_MEMORY_CONTAINER_SIZE); + startParam.memSize = SCE_VOICE_MEMORY_CONTAINER_SIZE; + + int err; + + C4JThread::PushAffinityAllCores(); // PS4 only + err = sceVoiceStart(&startParam); + assert(err == SCE_OK); + C4JThread::PopAffinity(); // PS4 only + + m_bVoiceStarted = true; + } +} + +void SonyVoiceChat_Orbis::checkFinished() +{ + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=0;im_bFlaggedForShutdown); + } +// assert(m_numLocalDevicesConnected == 0); + + LeaveCriticalSection(&m_csRemoteConnections); +} + + +void SonyVoiceChat_Orbis::shutdown() +{ + m_bInitialised = false; + int32_t result; + result = sceVoiceStop(); + assert(result == SCE_OK); + result = sceVoiceEnd(); + assert(result == SCE_OK); + free(startParam.container); + + int returnCode = sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE); + if (returnCode < 0) + { + app.DebugPrintf("Error: sceSysmoduleUnloadModule(SCE_SYSMODULE_VOICE), ret 0x%08x\n", returnCode); + assert(0); + } +} + +void SonyVoiceChat_Orbis::setEnabled( bool bEnabled ) +{ +} + + + +// Internal send function. This attempts to send as many elements in the queue as possible until the write function tells us that we can't send any more. This way, +// we are guaranteed that if there *is* anything more in the queue left to send, we'll get a CELL_RUDP_CONTEXT_EVENT_WRITABLE event when whatever we've managed to +// send here is complete, and can continue on. +void SQRVoiceConnection::SendMoreInternal() +{ + EnterCriticalSection(&m_csQueue); + bool keepSending; + do + { + keepSending = false; + if( m_sendQueue.size() > 0) + { + // Attempt to send the full data in the first element in our queue + unsigned char *data= m_sendQueue.front().current; + int dataSize = m_sendQueue.front().end - m_sendQueue.front().current; + int ret = sceRudpWrite( m_rudpCtx, data, dataSize, 0);//CELL_RUDP_MSG_LATENCY_CRITICAL ); + int wouldBlockFlag = SCE_RUDP_ERROR_WOULDBLOCK; + + if( ret == dataSize ) + { + // Fully sent, remove from queue - will loop in the while loop to see if there's anything else in the queue we could send + delete [] m_sendQueue.front().start; + m_sendQueue.pop(); + if( m_sendQueue.size() ) + { + keepSending = true; + } + } + else if( ( ret >= 0 ) || ( ret == wouldBlockFlag ) ) + { + + + // Things left to send - adjust this element in the queue + int remainingBytes; + if( ret >= 0 ) + { + // Only ret bytes sent so far + remainingBytes = dataSize - ret; + assert(remainingBytes > 0 ); + } + else + { + // Is CELL_RUDP_ERROR_WOULDBLOCK, nothing has yet been sent + remainingBytes = dataSize; + } + m_sendQueue.front().current = m_sendQueue.front().end - remainingBytes; + } + } + } while (keepSending); + LeaveCriticalSection(&m_csQueue); +} + +void SQRVoiceConnection::SendInternal(const void *data, unsigned int dataSize) +{ + EnterCriticalSection(&m_csQueue); + + QueuedSendBlock sendBlock; + + unsigned char *dataCurrent = (unsigned char *)data; + unsigned int dataRemaining = dataSize; + + while( dataRemaining ) + { + int dataSize = dataRemaining; + if( dataSize > SNP_MAX_PAYLOAD ) dataSize = SNP_MAX_PAYLOAD; + sendBlock.start = new unsigned char [dataSize]; + sendBlock.end = sendBlock.start + dataSize; + sendBlock.current = sendBlock.start; + memcpy( sendBlock.start, dataCurrent, dataSize); + m_sendQueue.push(sendBlock); + dataRemaining -= dataSize; + dataCurrent += dataSize; + } + +// app.DebugPrintf("voice sent %d bytes\n", dataSize); + + // Now try and send as much as we can + SendMoreInternal(); + + LeaveCriticalSection(&m_csQueue); +} + +void SQRVoiceConnection::readRemoteData() +{ + unsigned int dataSize = sceRudpGetSizeReadable(m_rudpCtx); + if( dataSize > 0 ) + { + VoicePacket packet; + int bytesRead = sceRudpRead( m_rudpCtx, &packet, dataSize, 0, NULL ); + unsigned int writeSize; + if( bytesRead > 0 ) + { +// app.DebugPrintf("voice received %d bytes\n", bytesRead); + writeSize = bytesRead; + packet.verifyData(bytesRead, 19); + addPacket(packet); +// m_playRingBuffer.Write((char*)data, writeSize); + + } + } + +} + + + +SQRVoiceConnection::SQRVoiceConnection( int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId ) + : m_rudpCtx(rudpCtx) + , m_remoteRoomMemberId(remoteRoomMemberId) + , m_bConnected(false) + , m_headsetConnectionMask(0) + , m_playRingBuffer(sc_ringBufferSize) +{ + InitializeCriticalSection(&m_csQueue); + InitializeCriticalSection(&m_csPacketQueue); + + SceVoiceInitParam params; + SceVoicePortParam portArgs; +#ifdef _USE_PCM_AUDIO_ + portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 100; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.bufSize = 4096; +#else + portArgs.portType = SCE_VOICE_PORTTYPE_IN_VOICE; + portArgs.bMute = false; + portArgs.threshold = sc_thresholdValue; // compensate network jitter + portArgs.volume = 1.0f; + portArgs.voice.bitrate = VOICE_ENCODED_FORMAT; +#endif + CreatePort( &m_voiceInPort, &portArgs ); + m_nextExpectedFrameIndex = 0; + m_bFlaggedForShutdown = false; +} + +SQRVoiceConnection::~SQRVoiceConnection() +{ + DeleteCriticalSection(&m_csQueue); + DeleteCriticalSection(&m_csPacketQueue); + extern int g_numRUDPContextsBound; + int err = sceRudpTerminate( m_rudpCtx ); + app.DebugPrintf(sc_verbose, "-----------------::::::::::::: sceRudpTerminate\n" ); + + app.DebugPrintf("-----------------------------\n"); + if(err<0) + app.DebugPrintf("Voice rudp context failed to delete!!! %d\n", m_rudpCtx); + else + { + g_numRUDPContextsBound--; + app.DebugPrintf("Voice rudp context deleted %d\n", m_rudpCtx); + } + app.DebugPrintf("-----------------------------\n"); + + DeletePort(m_voiceInPort); + +} + +bool SQRVoiceConnection::getNextPacket( VoicePacket& packet ) +{ + EnterCriticalSection(&m_csPacketQueue); + bool retVal = false; + if(m_receivedVoicePackets.size() > 0) + { + retVal = true; + packet = m_receivedVoicePackets.front(); + m_receivedVoicePackets.pop(); + } + LeaveCriticalSection(&m_csPacketQueue); + return retVal; +} + +void SQRVoiceConnection::addPacket( VoicePacket& packet ) +{ + EnterCriticalSection(&m_csPacketQueue); + m_receivedVoicePackets.push(packet); + LeaveCriticalSection(&m_csPacketQueue); +} + +int g_frameNum = 0; +bool g_bRecording = false; + + +uint32_t frameSendIndex = 0; +uint32_t lastReadFrameCnt = 0; + + +void PrintAllOutputVoiceStates( std::vector& connections) +{ + for(int rIdx=0;rIdxm_voiceInPort, &portInfo ); + static SceVoicePortState lastPortState = SCE_VOICE_PORTSTATE_IDLE; + if(portInfo.state != lastPortState) + { + lastPortState = portInfo.state; + switch(portInfo.state) + { + case SCE_VOICE_PORTSTATE_IDLE: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_IDLE\n"); + break; + case SCE_VOICE_PORTSTATE_BUFFERING: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_BUFFERING\n"); + break; + case SCE_VOICE_PORTSTATE_RUNNING: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_RUNNING\n"); + break; + case SCE_VOICE_PORTSTATE_READY: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_READY\n"); + break; + case SCE_VOICE_PORTSTATE_NULL: + default: + app.DebugPrintf(" ----- SCE_VOICE_PORTSTATE_NULL\n"); + break; + } + } + } + +} + + +void SonyVoiceChat_Orbis::sendPCMMicData() +{ + int32_t result; + uint32_t outputPortBytes; + VoicePacket packetToSend; + uint32_t readSize; + SceVoiceBasePortInfo portInfo; + memset( &portInfo, 0, sizeof(portInfo) ); + uint16_t frameGap = 0; + + DWORD tick = GetTickCount(); + static DWORD lastTick = 0; + int numFrames = ceilf((tick - lastTick)/16.0f); + lastTick = tick; + readSize = 512 * numFrames; + + if(g_loadedPCMVoiceDataPos[0] + readSize < g_loadedPCMVoiceDataSizes[0]) + { + for(int i=0;i (g_loadedPCMVoiceDataSizes[0] + 8192)) + g_loadedPCMVoiceDataPos[0] = 0; + + +} + +void SonyVoiceChat_Orbis::sendAllVoiceData() +{ + int32_t result; + uint32_t outputPortBytes; + VoicePacket packetToSend; + uint32_t readSize; + SceVoiceBasePortInfo portInfo; + memset( &portInfo, 0, sizeof(portInfo) ); + uint16_t frameGap = 0; + + VoicePacket::Flags lastPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + + for(int i=0; isizeof(packetToSend.m_data))?sizeof(packetToSend.m_data):outputPortBytes; + if( outputPortBytes || flagsChanged || m_forceSendPacket) + { + frameSendIndex += lastReadFrameCnt; + if(outputPortBytes) + { + readSize = outputPortBytes; + result = sceVoiceReadFromOPort(m_voiceOutPort, packetToSend.m_data, &readSize ); + if (result != SCE_OK) + { + app.DebugPrintf("sceVoiceReadFromOPort failed %0x\n", result); + assert(0); + return; + } + lastReadFrameCnt = readSize/portInfo.frameSize; + assert(readSize%portInfo.frameSize == 0); + + packetToSend.m_numFrames = lastReadFrameCnt; + packetToSend.m_frameSendIndex = frameSendIndex; + packetToSend.setChecksum(readSize); + } + else + { + readSize = 0; + packetToSend.m_numFrames = 0; + packetToSend.m_frameSendIndex = frameSendIndex; + packetToSend.setChecksum(readSize); + + } + + + int packetSize = packetToSend.getPacketSize(readSize); + + EnterCriticalSection(&m_csRemoteConnections); + + // send this packet out to all our remote connections + for(int rIdx=0;rIdxm_bConnected) + m_remoteConnections[rIdx]->SendInternal(&packetToSend, packetSize); + } + + LeaveCriticalSection(&m_csRemoteConnections); + } + m_forceSendPacket = false; +} + +bool g_bPlaying = false; + +void SonyVoiceChat_Orbis::playAllReceivedData() +{ + EnterCriticalSection(&m_csRemoteConnections); + // write all the incoming data from the network to each of the input voices + for(int rIdx=0;rIdxgetNextPacket(packet)) // MGH - changed to a while loop, so all the packets are sent to the voice port, and it can handle delayed packets due to the size of it's internal buffer + { + int frameGap; + if (pVoice->m_nextExpectedFrameIndex == packet.m_frameSendIndex) + { + // no voice frame drop, continuous frames + frameGap = 0; + app.DebugPrintf(sc_verbose, "index@%d gets expected frame\n",pVoice->m_nextExpectedFrameIndex); + pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames; + } + else if (pVoice->m_nextExpectedFrameIndex < packet.m_frameSendIndex) + { + // has voice frame drop, dropped forwarding frames + frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex; + app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n",pVoice->m_nextExpectedFrameIndex, frameGap); + pVoice->m_nextExpectedFrameIndex = packet.m_frameSendIndex + packet.m_numFrames; + } + else if (pVoice->m_nextExpectedFrameIndex > packet.m_frameSendIndex) + { + // has voice frame drop, dropped preceding frames, no reset on pVoice->m_nextExpectedFrameIndex + frameGap = packet.m_frameSendIndex - pVoice->m_nextExpectedFrameIndex; + app.DebugPrintf(sc_verbose, "index@%d gets dropped forwarding frames %d\n", pVoice->m_nextExpectedFrameIndex, frameGap); + } + + SceVoiceBasePortInfo portInfo; + int result = sceVoiceGetPortInfo(pVoice->m_voiceInPort, &portInfo ); + if (result != SCE_OK) + { + app.DebugPrintf(sc_verbose, "sceVoiceGetPortInfo LoopbackVoiceInPort failed %x\n", result); + assert(0); + LeaveCriticalSection(&m_csRemoteConnections); + return; + } + uint32_t writeSize = packet.m_numFrames * portInfo.frameSize; + int inputPortBytes = portInfo.numByte; + inputPortBytes = (inputPortBytes>writeSize)?writeSize:inputPortBytes; + writeSize = inputPortBytes; + result = sceVoiceWriteToIPort(pVoice->m_voiceInPort, packet.m_data, &writeSize, frameGap); + if (result != SCE_OK) + { + app.DebugPrintf(sc_verbose, "sceVoiceWriteToIPort failed %0x\n", result); + assert(0); + LeaveCriticalSection(&m_csRemoteConnections); + return; + } + if (writeSize != inputPortBytes) + { + // libvoice internal voice in port buffer fulls + app.DebugPrintf(sc_verbose, "internal voice in port buffer fulls. \n"); + } + packet.m_numFrames = 0; + + // copy the flags + for(int flagIndex=0;flagIndexm_remotePlayerFlags[flagIndex] = packet.m_localPlayerFlags[flagIndex]; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + +} + +void SonyVoiceChat_Orbis::tick() +{ + if(m_bInitialised) + { +// DWORD tick = GetTickCount(); +// static DWORD lastTick = 0; +// app.DebugPrintf("Time since last voice tick : %d ms\n", tick - lastTick); +// lastTick = tick; + g_frameNum++; +#ifdef USE_PCM_MIC_DATA + sendPCMMicData(); +#endif + sendAllVoiceData(); + playAllReceivedData(); + + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=m_remoteConnections.size()-1;i>=0;i--) + { + if(m_remoteConnections[i]->m_bFlaggedForShutdown) + { + delete m_remoteConnections[i]; + m_remoteConnections.erase(m_remoteConnections.begin() + i); + } + } + + LeaveCriticalSection(&m_csRemoteConnections); + + // MGH - added to hopefully fix a lockup with shutdowns happening on different threads, when more than 1 player leaves the game + for(int i=0;iIsLocal()) + { + return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected; + } + else + { + EnterCriticalSection(&m_csRemoteConnections); + for(int i=0;im_remoteRoomMemberId == pNetPlayer->m_roomMemberId) + { + bool bMicConnected = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bHasMicConnected; + LeaveCriticalSection(&m_csRemoteConnections); + return bMicConnected; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + } + // if we get here we've not found the player, panic!! + assert(0); + return false; +} + +void SonyVoiceChat_Orbis::mute( bool bMute ) +{ +} + +void SonyVoiceChat_Orbis::mutePlayer( const SceNpMatching2RoomMemberId member_id, bool bMute ) /*Turn chat audio from a specified player on or off */ +{ +} + +void SonyVoiceChat_Orbis::muteLocalPlayer( bool bMute ) /*Turn microphone input on or off */ +{ +} + +bool SonyVoiceChat_Orbis::isMuted() +{ +} + +bool SonyVoiceChat_Orbis::isMutedPlayer( const PlayerUID& memberUID) +{ + return false; +} + +bool SonyVoiceChat_Orbis::isMutedLocalPlayer() +{ + return false; +} + + +bool SonyVoiceChat_Orbis::isTalking(SQRNetworkPlayer* pNetPlayer) +{ + if(pNetPlayer->IsLocal()) + { + return m_localPlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking; + } + else + { + EnterCriticalSection(&m_csRemoteConnections); + for(int i=0;im_remoteRoomMemberId == pNetPlayer->m_roomMemberId) + { + bool bTalking = pVoice->m_remotePlayerFlags[pNetPlayer->GetLocalPlayerIndex()].m_bTalking; + LeaveCriticalSection(&m_csRemoteConnections); + return bTalking; + } + } + LeaveCriticalSection(&m_csRemoteConnections); + } + // if we get here we've not found the player, panic!! + assert(0); + return false; +} + + +void SQRLocalVoiceDevice::init(SceUserServiceUserId localUserID, bool bChatRestricted) +{ + SceVoiceInitParam params; + SceVoicePortParam portArgs; + + int returnCode = 0; + m_bChatRestricted = bChatRestricted; + +#ifdef USE_PCM_MIC_DATA + portArgs.portType = SCE_VOICE_PORTTYPE_IN_PCMAUDIO; + portArgs.bMute = false; + portArgs.threshold = 100; + portArgs.volume = 1.0f; + portArgs.pcmaudio.format.sampleRate= SCE_VOICE_SAMPLINGRATE_16000; + portArgs.pcmaudio.format.dataType = SCE_VOICE_PCM_SHORT_LITTLE_ENDIAN; + portArgs.pcmaudio.bufSize = 4096; + CreatePort( &m_microphonePort, &portArgs ); +#else + portArgs.portType = SCE_VOICE_PORTTYPE_IN_DEVICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.device.userId = localUserID; + portArgs.device.type = SCE_AUDIO_IN_TYPE_VOICE; + portArgs.device.index = 0; + CreatePort( &m_microphonePort, &portArgs ); + +#endif + portArgs.portType = SCE_VOICE_PORTTYPE_OUT_DEVICE; + portArgs.bMute = false; + portArgs.threshold = 0; + portArgs.volume = 1.0f; + portArgs.device.userId = localUserID; + portArgs.device.type = SCE_AUDIO_OUT_PORT_TYPE_VOICE; + portArgs.device.index = 0; + CreatePort( &m_headsetPort, &portArgs ); + + m_localUserID = localUserID; + +} + + + +void SQRLocalVoiceDevice::shutdownWhenFlagged() +{ + + assert(isValid()); + m_localUserID = SCE_USER_SERVICE_USER_ID_INVALID; + DeletePort(m_microphonePort); + DeletePort(m_headsetPort); + m_bFlaggedForShutdown = false; +} + + + +SQRVoiceConnection* SonyVoiceChat_Orbis::addRemoteConnection( int RudpCxt, SceNpMatching2RoomMemberId peerMemberId) +{ + EnterCriticalSection(&m_csRemoteConnections); + SQRVoiceConnection* pConn = new SQRVoiceConnection(RudpCxt, peerMemberId); + m_remoteConnections.push_back(pConn); + m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags + LeaveCriticalSection(&m_csRemoteConnections); + + return pConn; +} + +void SonyVoiceChat_Orbis::connectPorts(uint32_t inPort, uint32_t outPort) +{ + int returnCode = sceVoiceConnectIPortToOPort(inPort, outPort); + if (returnCode != SCE_OK ) + { + app.DebugPrintf("sceVoiceConnectIPortToOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort); + assert(0); + } +} +void SonyVoiceChat_Orbis::disconnectPorts(uint32_t inPort, uint32_t outPort) +{ + int returnCode = sceVoiceDisconnectIPortFromOPort(inPort, outPort); + if (returnCode != SCE_OK ) + { + app.DebugPrintf("sceVoiceDisconnectIPortFromOPort failed (0x%08x), inPort 0x%08x, outPort 0x%08x\n", returnCode, inPort, outPort); + assert(0); + } +} + + +void SonyVoiceChat_Orbis::makeLocalConnections() +{ + // connect all mics to other devices headsets, for local chat + for(int i=0;iisValid()) + { + for(int j=0;jisValid()) + { + if(pConnectFrom->m_localConnections[j] == false) + { + if(pConnectTo->m_bChatRestricted == false && pConnectFrom->m_bChatRestricted == false) + { + connectPorts(pConnectFrom->m_microphonePort, pConnectTo->m_headsetPort); + pConnectFrom->m_localConnections[j] = true; + } + } + } + } + } + } +} + +void SonyVoiceChat_Orbis::breakLocalConnections(int playerIdx) +{ + // break any connections with devices that are no longer valid + for(int i=0;im_localConnections[j] == true) + { + SQRLocalVoiceDevice* pConnectedTo = &m_localVoiceDevices[j]; + if(i==playerIdx || j==playerIdx) + { + if(pConnectedTo->m_bChatRestricted == false && pConnectedFrom->m_bChatRestricted == false) + { + disconnectPorts(pConnectedFrom->m_microphonePort, pConnectedTo->m_headsetPort); + pConnectedFrom->m_localConnections[j] = false; + } + } + } + } + } +} + + +void SonyVoiceChat_Orbis::initLocalPlayer(int playerIndex) +{ + if(m_localVoiceDevices[playerIndex].isValid() == false) + { + bool chatRestricted = false; + ProfileManager.GetChatAndContentRestrictions(ProfileManager.GetPrimaryPad(),false,&chatRestricted,NULL,NULL); + + // create all device ports required + m_localVoiceDevices[playerIndex].init(ProfileManager.getUserID(playerIndex), chatRestricted); + m_numLocalDevicesConnected++; + if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false) + { + connectPorts(m_localVoiceDevices[playerIndex].m_microphonePort, m_voiceOutPort); + } + m_forceSendPacket = true; // new local device, so we'll force a packet through for the flags + + } + makeLocalConnections(); +} + +void SonyVoiceChat_Orbis::connectPlayer(SQRVoiceConnection* pConnection, int playerIndex) +{ + if((pConnection->m_headsetConnectionMask & (1 << playerIndex)) == 0) + { + initLocalPlayer(playerIndex); // added this as we can get a client->client connection coming in first, and the network player hasn't been created yet (so this hasn't been initialised) + if(m_localVoiceDevices[playerIndex].m_bChatRestricted == false) + { + connectPorts(pConnection->m_voiceInPort, m_localVoiceDevices[playerIndex].m_headsetPort); + } + pConnection->m_headsetConnectionMask |= (1 << playerIndex); + app.DebugPrintf("Connecting player %d to rudp context %d\n", playerIndex, pConnection->m_rudpCtx); + m_forceSendPacket = true; // new connection, so we'll force a packet through for the flags + } +} + +SQRVoiceConnection* SonyVoiceChat_Orbis::GetVoiceConnectionFromRudpCtx( int RudpCtx ) +{ + for(int i=0;im_rudpCtx == RudpCtx) + return m_remoteConnections[i]; + } + return NULL; +} + +void SonyVoiceChat_Orbis::connectPlayerToAll( int playerIndex ) +{ + EnterCriticalSection(&m_csRemoteConnections); + + for(int i=0;im_remoteRoomMemberId == roomMemberID) + { + return m_remoteConnections[i]; + } + } + + return NULL; +} + +void SonyVoiceChat_Orbis::disconnectLocalPlayer( int localIdx ) +{ + EnterCriticalSection(&m_csRemoteConnections); + + if(m_localVoiceDevices[localIdx].m_bChatRestricted == false) + { + disconnectPorts(m_localVoiceDevices[localIdx].m_microphonePort, m_voiceOutPort); + + for(int i=0;im_voiceInPort, m_localVoiceDevices[localIdx].m_headsetPort); + m_remoteConnections[i]->m_headsetConnectionMask &= (~(1 << localIdx)); + app.DebugPrintf("disconnecting player %d from rudp context %d\n", localIdx, m_remoteConnections[i]->m_rudpCtx); + } + } + LeaveCriticalSection(&m_csRemoteConnections); + + breakLocalConnections(localIdx); + m_localVoiceDevices[localIdx].flagForShutdown(); + m_numLocalDevicesConnected--; + + if(m_numLocalDevicesConnected == 0) // no more local players, kill all the remote connections + { + for(int i=0;i=0); + if(voiceIdx>=0) + { + m_remoteConnections[voiceIdx]->m_bFlaggedForShutdown = true; + } + + LeaveCriticalSection(&m_csRemoteConnections); + +} + +void SonyVoiceChat_Orbis::setConnected( int RudpCtx ) +{ + SQRVoiceConnection* pVoice = GetVoiceConnectionFromRudpCtx(RudpCtx); + if(pVoice) + { + pVoice->m_bConnected = true; + m_forceSendPacket = true; + } + else + { + assert(false); + } +} + + + + +RingBuffer::RingBuffer( int sizeBytes ) +{ + buffer = new char[sizeBytes]; + buf_size = sizeBytes; + buf_full = buf_free = 0; +} + + +int RingBuffer::Write( char* data, int len_ ) +{ + if (len_ <= 0) return len_; + unsigned int len = (unsigned int)len_; + unsigned int data_size = buf_size - (buf_free - buf_full); + if (len > data_size) + len = data_size; + data_size = buf_size - (buf_free % buf_size); + if (data_size > len) + data_size = len; + memcpy(buffer + (buf_free % buf_size), data, data_size); + if (data_size != len) + memcpy(buffer, data + data_size, len - data_size); + buf_free += len; + return len; +} + +int RingBuffer::Read( char* data, int max_bytes_ ) +{ + if (max_bytes_ <= 0) return max_bytes_; + unsigned int max_bytes = (unsigned int)max_bytes_; + unsigned int result = buf_free - buf_full; + if (result > max_bytes) + result = max_bytes; + unsigned int chunk = buf_size - (buf_full % buf_size); + if (chunk > result) + chunk = result; + memcpy(data, buffer + (buf_full % buf_size), chunk); + if (chunk != result) + memcpy(data + chunk, buffer, result - chunk); + buf_full += result; + return result; +} diff --git a/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h new file mode 100644 index 0000000..950ca09 --- /dev/null +++ b/Minecraft.Client/Orbis/Network/SonyVoiceChat_Orbis.h @@ -0,0 +1,215 @@ +#pragma once + +#include +#include +#include +#include +#include "Common/Network/Sony/SQRNetworkPlayer.h" + +static const int sc_maxVoiceDataSize = 2048; + + +class VoicePacket +{ + static const int MAX_LOCAL_PLAYER_COUNT = 4; + +public: + struct Flags + { + bool m_bTalking : 1; + bool m_bHasMicConnected : 1; + }; + + Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + uint32_t m_frameSendIndex; + uint32_t m_numFrames; + uint32_t m_checkSum; + uint32_t m_playerIndexFlags; + char m_data[sc_maxVoiceDataSize]; + + static int getPacketSize(int dataSize) { return (uint64_t)&((VoicePacket*)0)->m_data[dataSize];} + void setChecksum(int dataSize) + { + m_checkSum = 0; + for(int i=0;i, +// the reader thread changes pointer +class RingBuffer +{ +public: + RingBuffer(int sizeBytes); + ~RingBuffer() { delete buffer; } + void Reset(void) { buf_full = buf_free = 0; } + unsigned int DataSize(void) { return (buf_free - buf_full); } + int Write(char* data, int len_); + int Read(char* data, int max_bytes_); + int getDataSize() { return buf_free - buf_full; } + void ResetByWriter(void) { buf_free = buf_full; } + void ResetByReader(void) { buf_full = buf_free; } + +private: + char* buffer; + unsigned int buf_size; + unsigned int buf_full; + unsigned int buf_free; +}; + + +static const int sc_ringBufferSize = 16384; + +class SQRLocalVoiceDevice +{ +public: + uint32_t m_headsetPort; + uint32_t m_microphonePort; + uint8_t m_localConnections[4]; // connection between this devices mic and other local player's headsets + bool m_bChatRestricted; + bool m_bFlaggedForShutdown; + SceUserServiceUserId m_localUserID; + +public: + SQRLocalVoiceDevice() + : m_headsetPort(SCE_VOICE_INVALID_PORT_ID) + , m_microphonePort(SCE_VOICE_INVALID_PORT_ID) + , m_localUserID(SCE_USER_SERVICE_USER_ID_INVALID) + { + for(int i=0;i<4;i++) + m_localConnections[i] = 0; + } + + void init(SceUserServiceUserId localUserID, bool bChatRestricted); + void flagForShutdown() { m_bFlaggedForShutdown = true;} + void shutdownWhenFlagged(); + bool isValid() { return m_localUserID != SCE_USER_SERVICE_USER_ID_INVALID; } +// void setBitRate() +// { +// int err = sceVoiceSetBitRate(uint32_t portId, +// SceVoiceBitRate bitrate +// ); +// } +}; + +#define VOICE_ENCODED_FORMAT SCE_VOICE_BITRATE_7300 + + + +class SQRVoiceConnection +{ + static const int MAX_LOCAL_PLAYER_COUNT = 4; + static const int SNP_MAX_PAYLOAD = 1346; // This is the default RUDP payload size - if we want to change this we'll need to use cellRudpSetOption to set something else & adjust segment size + class QueuedSendBlock + { + public: + unsigned char *start; + unsigned char *end; + unsigned char *current; + }; + + std::queue m_sendQueue; + CRITICAL_SECTION m_csQueue; + +public: + int m_rudpCtx; + bool m_bConnected; + uint32_t m_voiceInPort; // 1 input port per connection, incoming UDP packets are written to each of these, and then they're connected out to all headsets + int m_headsetConnectionMask; // 1 bit per player, if the headset connection has been made + RingBuffer m_playRingBuffer; + SceNpMatching2RoomMemberId m_remoteRoomMemberId; // Assigned by Matching2 lib, we can use to indicate which machine this player belongs to (note - 16 bits) + std::queue m_receivedVoicePackets; + CRITICAL_SECTION m_csPacketQueue; + VoicePacket::Flags m_remotePlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + uint32_t m_nextExpectedFrameIndex; + bool m_bFlaggedForShutdown; + SQRVoiceConnection(int rudpCtx, SceNpMatching2RoomMemberId remoteRoomMemberId); + ~SQRVoiceConnection(); + + void SendInternal(const void *data, unsigned int dataSize); + void SendMoreInternal(); + void readRemoteData(); + bool getNextPacket(VoicePacket& packet); + void addPacket(VoicePacket& packet); + + +}; + +class SonyVoiceChat_Orbis +{ +public: + + static void init(); + static void start(); + static void shutdown(); + static void tick(); + static void checkFinished(); + static void setEnabled(bool bEnabled); + static bool hasMicConnected(SQRNetworkPlayer* pNetPlayer); + static bool isTalking(SQRNetworkPlayer* pNetPlayer); + static void mute(bool bMute); //Turn chat audio on or off + static void mutePlayer(const SceNpMatching2RoomMemberId member_id, bool bMute); //Turn chat audio from a specified player on or off; + static void muteLocalPlayer(bool bMute); //Turn microphone input on or off; + + static bool isMuted(); + static bool isMutedPlayer(const PlayerUID& memberUID); + static bool isMutedLocalPlayer(); //Turn microphone input on or off; + + static void initLocalPlayer(int playerIndex); + + static SQRVoiceConnection* addRemoteConnection(int RudpCxt, SceNpMatching2RoomMemberId peerMemberId); + static void connectPlayer(SQRVoiceConnection* pConnection, int playerIndex); + static void connectPlayerToAll(int playerIndex); + static void disconnectLocalPlayer(int localIdx); + static void disconnectRemoteConnection( SQRVoiceConnection* pVoice ); + + static void VoiceEventCallback( SceVoiceEventData* pEvent ); + + static std::vector m_remoteConnections; + static void connectPorts(uint32_t inPort, uint32_t outPort); + static void disconnectPorts(uint32_t inPort, uint32_t outPort); + static void makeLocalConnections(); + static void breakLocalConnections(int playerIdx); + + static void sendAllVoiceData(); + static void playAllReceivedData(); + + static void sendPCMMicData(); + static SQRVoiceConnection* getVoiceConnectionFromRoomMemberID(SceNpMatching2RoomMemberId roomMemberID); + + static SQRVoiceConnection* GetVoiceConnectionFromRudpCtx(int RudpCtx); + static void setConnected(int RudpCtx); + +private: + static const int MAX_LOCAL_PLAYER_COUNT = 4; + + static bool m_bVoiceStarted; + static int m_numLocalDevicesConnected; + static SQRLocalVoiceDevice m_localVoiceDevices[MAX_LOCAL_PLAYER_COUNT]; + + static uint32_t m_voiceOutPort; // single output port that all local devices are mixed to, and then sent out to all other remote machines + + static RingBuffer m_recordRingBuffer; + static RingBuffer m_playRingBuffer; + static VoicePacket::Flags m_localPlayerFlags[MAX_LOCAL_PLAYER_COUNT]; + static bool m_forceSendPacket; // force a packet across the network, even if there's no data, so we can update flags + static bool m_bInitialised; + static CRITICAL_SECTION m_csRemoteConnections; + + +}; \ No newline at end of file diff --git a/Minecraft.Client/Orbis/OrbisExtras/OrbisMaths.h b/Minecraft.Client/Orbis/OrbisExtras/OrbisMaths.h new file mode 100644 index 0000000..fa6c395 --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/OrbisMaths.h @@ -0,0 +1,11 @@ +#pragma once +#include +using namespace sce::Vectormath::Simd::Aos; + +typedef Vector4 XMVECTOR; +typedef Matrix4 XMMATRIX; +typedef Vector4 XMFLOAT4; + +XMMATRIX XMMatrixMultiply(XMMATRIX a, XMMATRIX b); +XMVECTOR XMMatrixDeterminant(XMMATRIX a); +XMMATRIX XMMatrixInverse(Vector4 *a, XMMATRIX b); \ No newline at end of file diff --git a/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.cpp b/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.cpp new file mode 100644 index 0000000..d3a21ac --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.cpp @@ -0,0 +1,790 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#include + +// These are required so that the heap will automatically expand - default is limited to 256KB +size_t sceLibcHeapSize = SCE_LIBC_HEAP_SIZE_EXTENDED_ALLOC_NO_LIMIT; +unsigned int sceLibcHeapExtendedAlloc = 1; + +//static char dirName[128]; +//static char contentInfoPath[128]; +static char usrdirPath[128]; +int32_t hBGMAudio; + +//static char sc_loadPath[] = {"/app0/"}; +//const char* getConsoleHomePath() { return sc_loadPath; } + +char* getUsrDirPath() +{ + return usrdirPath; +} + + +int _wcsicmp( const wchar_t * dst, const wchar_t * src ) +{ + wchar_t f,l; + + // validation section + // _VALIDATE_RETURN(dst != NULL, EINVAL, _NLSCMPERROR); + // _VALIDATE_RETURN(src != NULL, EINVAL, _NLSCMPERROR); + + do { + f = towlower(*dst); + l = towlower(*src); + dst++; + src++; + } while ( (f) && (f == l) ); + return (int)(f - l); +} + +size_t wcsnlen(const wchar_t *wcs, size_t maxsize) +{ + size_t n; + +// Note that we do not check if s == NULL, because we do not +// return errno_t... + + for (n = 0; n < maxsize && *wcs; n++, wcs++) + ; + + return n; +} + + +VOID GetSystemTime( LPSYSTEMTIME lpSystemTime) +{ + SceRtcDateTime dateTime; + int err = sceRtcGetCurrentClock(&dateTime, 0); + assert(err == SCE_OK); + + lpSystemTime->wYear = sceRtcGetYear(&dateTime); + lpSystemTime->wMonth = sceRtcGetMonth(&dateTime); + lpSystemTime->wDay = sceRtcGetDay(&dateTime); + lpSystemTime->wDayOfWeek = sceRtcGetDayOfWeek(lpSystemTime->wYear, lpSystemTime->wMonth, lpSystemTime->wDay); + lpSystemTime->wHour = sceRtcGetHour(&dateTime); + lpSystemTime->wMinute = sceRtcGetMinute(&dateTime); + lpSystemTime->wSecond = sceRtcGetSecond(&dateTime); + lpSystemTime->wMilliseconds = sceRtcGetMicrosecond(&dateTime)/1000; +} +BOOL FileTimeToSystemTime(CONST FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime) { ORBIS_STUBBED; return false; } +BOOL SystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime) { ORBIS_STUBBED; return false; } +VOID GetLocalTime(LPSYSTEMTIME lpSystemTime) +{ + SceRtcDateTime dateTime; + int err = sceRtcGetCurrentClockLocalTime(&dateTime); + assert(err == SCE_OK ); + + lpSystemTime->wYear = sceRtcGetYear(&dateTime); + lpSystemTime->wMonth = sceRtcGetMonth(&dateTime); + lpSystemTime->wDay = sceRtcGetDay(&dateTime); + lpSystemTime->wDayOfWeek = sceRtcGetDayOfWeek(lpSystemTime->wYear, lpSystemTime->wMonth, lpSystemTime->wDay); + lpSystemTime->wHour = sceRtcGetHour(&dateTime); + lpSystemTime->wMinute = sceRtcGetMinute(&dateTime); + lpSystemTime->wSecond = sceRtcGetSecond(&dateTime); + lpSystemTime->wMilliseconds = sceRtcGetMicrosecond(&dateTime)/1000; +} + +HANDLE CreateEvent(void* lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName) { ORBIS_STUBBED; return NULL; } +VOID Sleep(DWORD dwMilliseconds) +{ + C4JThread::Sleep(dwMilliseconds); +} + +BOOL SetThreadPriority(HANDLE hThread, int nPriority) { ORBIS_STUBBED; return FALSE; } +DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { ORBIS_STUBBED; return false; } + +LONG InterlockedCompareExchangeRelease(LONG volatile *Destination, LONG Exchange,LONG Comperand ) +{ + return sceAtomicCompareAndSwap32((int32_t*)Destination, (int32_t)Comperand, (int32_t)Exchange); +} + +LONG64 InterlockedCompareExchangeRelease64(LONG64 volatile *Destination, LONG64 Exchange, LONG64 Comperand) +{ + return sceAtomicCompareAndSwap64((int64_t*)Destination, (int64_t)Comperand, (int64_t)Exchange); +} + + +ScePthreadMutexattr mutexParams; +int64_t g_OpStorage[4*1024]; /* 32KiB */ +int64_t g_ChunkStorage[8*1024]; /* 64KiB */ +int64_t g_FHStorage[2*1024]; /* 16KiB */ +int64_t g_DHStorage[512]; /* 4KiB */ + +VOID OrbisInit() +{ + static bool initialised = false; + + if( initialised ) return; + initialised = true; + + sceSysmoduleLoadModule(SCE_SYSMODULE_PNG_ENC); + sceSysmoduleLoadModule(SCE_SYSMODULE_PNG_DEC); + sceSysmoduleLoadModule(SCE_SYSMODULE_APP_CONTENT); + sceSysmoduleLoadModule(SCE_SYSMODULE_SAVE_DATA_DIALOG); + sceSysmoduleLoadModule(SCE_SYSMODULE_IME_DIALOG); + sceSysmoduleLoadModule(SCE_SYSMODULE_RUDP); + sceSysmoduleLoadModule(SCE_SYSMODULE_NP_MATCHING2); + sceSysmoduleLoadModule(SCE_SYSMODULE_INVITATION_DIALOG); + sceSysmoduleLoadModule(SCE_SYSMODULE_NP_PARTY ); + sceSysmoduleLoadModule(SCE_SYSMODULE_GAME_CUSTOM_DATA_DIALOG ); + sceSysmoduleLoadModule(SCE_SYSMODULE_NP_SCORE_RANKING ); + sceSysmoduleLoadModule(SCE_SYSMODULE_NP_AUTH ); + sceSysmoduleLoadModule(SCE_SYSMODULE_NP_COMMERCE); + sceSysmoduleLoadModule(SCE_SYSMODULE_REMOTE_PLAY); + sceSysmoduleLoadModule(SCE_SYSMODULE_ERROR_DIALOG); + sceSysmoduleLoadModule(SCE_SYSMODULE_MESSAGE_DIALOG); + sceSysmoduleLoadModule(SCE_SYSMODULE_VOICE); + sceSysmoduleLoadModule(SCE_SYSMODULE_GAME_LIVE_STREAMING); + + SceFiosParams fiosParams = SCE_FIOS_PARAMS_INITIALIZER; + fiosParams.opStorage.pPtr = g_OpStorage; + fiosParams.opStorage.length = sizeof(g_OpStorage); + fiosParams.chunkStorage.pPtr = g_ChunkStorage; + fiosParams.chunkStorage.length = sizeof(g_ChunkStorage); + fiosParams.fhStorage.pPtr = g_FHStorage; + fiosParams.fhStorage.length = sizeof(g_FHStorage); + fiosParams.dhStorage.pPtr = g_DHStorage; + fiosParams.dhStorage.length = sizeof(g_DHStorage); + + int err = sceFiosInitialize(&fiosParams); + assert(err == SCE_FIOS_OK); + + scePthreadMutexattrInit(&mutexParams); + scePthreadMutexattrSettype(&mutexParams, SCE_PTHREAD_MUTEX_ADAPTIVE); +#ifdef USE_RAZOR + const int RAZOR_BUFFER_SIZE = 65536; + void *razorMem = malloc(RAZOR_BUFFER_SIZE); + err = sceRazorCpuInit(razorMem, RAZOR_BUFFER_SIZE); + assert(err == 0 ); +#endif + scePthreadSetaffinity(scePthreadSelf(), 1); + + sceAudioOutInit(); + hBGMAudio=sceAudioOutOpen( + SCE_USER_SERVICE_USER_ID_SYSTEM, + SCE_AUDIO_OUT_PORT_TYPE_BGM,0, + 256, + 48000, + 2); + + err = sceCommonDialogInitialize(); + //err = sceNpCommerceDialogInitialize(); + assert(err == SCE_OK); + + // 4J-PB - Can't keep this initialised as we monitor and handle the finished status in the main menu for patches +// err = sceErrorDialogInitialize(); +// assert(err == SCE_OK); + + // 4J-PB - can't init this here - it conflicts with the commerce dialog for dlc checkouts/PSPlus upsells. We need to init it when we use it, and then terminate it. + //err = sceMsgDialogInitialize(); + assert(err == SCE_OK); +} + +int32_t GetAudioBGMHandle() +{ + return hBGMAudio; +} + +VOID InitializeCriticalSection(PCRITICAL_SECTION CriticalSection) +{ + char name[1] = {0}; + + int err = scePthreadMutexInit(&CriticalSection->mutex, &mutexParams, name); + CriticalSection->m_cLock = 0; + assert(err == SCE_OK); +#ifdef _DEBUG + CriticalSection->m_pOwnerThread = NULL; +#endif + +} + + +VOID InitializeCriticalSectionAndSpinCount(PCRITICAL_SECTION CriticalSection, ULONG SpinCount) +{ + InitializeCriticalSection(CriticalSection); +} + +VOID DeleteCriticalSection(PCRITICAL_SECTION CriticalSection) +{ + int err = scePthreadMutexDestroy(&CriticalSection->mutex); + assert(err == SCE_OK); +} + +extern CRITICAL_SECTION g_singleThreadCS; + +VOID EnterCriticalSection(PCRITICAL_SECTION CriticalSection) +{ + int err = scePthreadMutexLock(&CriticalSection->mutex); + assert(err == SCE_OK || err == SCE_KERNEL_ERROR_EDEADLK ); + CriticalSection->m_cLock++; + +#ifdef _DEBUG + __thread static bool bRecursing = false; + if(bRecursing == false) + { + bRecursing = true; + CriticalSection->m_pOwnerThread = C4JThread::getCurrentThread(); + bRecursing = false; + } +#endif +} + + +VOID LeaveCriticalSection(PCRITICAL_SECTION CriticalSection) +{ + if(--CriticalSection->m_cLock == 0 ) + { + int err = scePthreadMutexUnlock(&CriticalSection->mutex); + assert(err == SCE_OK ); +#ifdef _DEBUG + CriticalSection->m_pOwnerThread = NULL; +#endif + + } +} + +ULONG TryEnterCriticalSection(PCRITICAL_SECTION CriticalSection) +{ + int err = scePthreadMutexTrylock(&CriticalSection->mutex); + if((err == SCE_OK || err == SCE_KERNEL_ERROR_EDEADLK )) + { + CriticalSection->m_cLock++; + return true; + } + return false; +} + +DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds) { ORBIS_STUBBED; return 0; } + +BOOL CloseHandle(HANDLE hObject) +{ + sceFiosFHCloseSync(NULL,(SceFiosFH)((int64_t)hObject)); + return true; +// ORBIS_STUBBED; +// return false; +} + +BOOL SetEvent(HANDLE hEvent) { ORBIS_STUBBED; return false; } + +HMODULE GetModuleHandle(LPCSTR lpModuleName) { ORBIS_STUBBED; return 0; } + +DWORD GetCurrentThreadId(VOID) +{ + return 0; // TODO +} +DWORD WaitForMultipleObjectsEx(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds,BOOL bAlertable ) { ORBIS_STUBBED; return 0; } +BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) { ORBIS_STUBBED; return false;} + +DWORD TlsAlloc(VOID) { return TLSStorageOrbis::Instance()->Alloc(); } +BOOL TlsFree(DWORD dwTlsIndex) { return TLSStorageOrbis::Instance()->Free(dwTlsIndex); } +LPVOID TlsGetValue(DWORD dwTlsIndex) { return TLSStorageOrbis::Instance()->GetValue(dwTlsIndex); } +BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) { return TLSStorageOrbis::Instance()->SetValue(dwTlsIndex, lpTlsValue); } + + +// we have to manage our own virtual allocs here, so this class stores all the info for each of them +class OrbisVAlloc +{ +public: + class PageInfo + { + public: + off_t m_physAddr; + void* m_virtualAddr; + uint64_t m_size; + + PageInfo(off_t physAddr, void* virtualAddr, uint64_t size) + : m_physAddr(physAddr) + , m_virtualAddr(virtualAddr) + , m_size(size) + {} + }; + void* m_virtualAddr; + uint64_t m_virtualSize; + std::vector m_pagesAllocated; + uint64_t m_allocatedSize; + + OrbisVAlloc(void* addr, uint64_t size) + : m_virtualAddr(addr) + , m_virtualSize(size) + , m_allocatedSize(0) + { + } + + ~OrbisVAlloc() + { + Decommit(); + int err = sceKernelMunmap(m_virtualAddr, m_virtualSize); + assert( err == SCE_OK ); + } + + void* Commit(uint64_t size) + { + uint64_t sizeToAdd = size - m_allocatedSize; // the extra memory size that we have to add on + assert(sizeToAdd >= 0); + + if(sizeToAdd == 0) + return m_virtualAddr; // nothing to add + + + off_t physAddr; + // Allocate the physical memory here + int err = sceKernelAllocateDirectMemory( 0, SCE_KERNEL_MAIN_DMEM_SIZE, sizeToAdd, 16*1024, SCE_KERNEL_WB_ONION, &physAddr); + if(err != SCE_OK) + { + assert(0); + return NULL; + } + // work out where the next page should be in virtual addr space, and pass that to the mapping function + void* pageVirtualAddr = ((char*)m_virtualAddr) + m_allocatedSize; + + void* inAddr = pageVirtualAddr; + err = sceKernelMapDirectMemory( + &inAddr, + sizeToAdd, + SCE_KERNEL_PROT_CPU_READ | SCE_KERNEL_PROT_CPU_WRITE, + SCE_KERNEL_MAP_FIXED, + physAddr, + 16*1024 ); + + if(inAddr != pageVirtualAddr) // make sure we actually get the virtual address that we requested + { + assert(0); + return NULL; + } + if(err != SCE_OK) + { + assert(0); + return NULL; + } + m_pagesAllocated.push_back(PageInfo(physAddr, pageVirtualAddr, sizeToAdd)); + m_allocatedSize += sizeToAdd; + return m_virtualAddr; + } + + + + void Decommit() + { + // spin round all the pages, unmapping and deallocating them + for(int i=0;i s_orbisVAllocs; + + +LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) +{ + if(lpAddress == NULL) + { + void *pAddr = (void*)SCE_KERNEL_APP_MAP_AREA_START_ADDR; + int err = sceKernelReserveVirtualRange(&pAddr, dwSize, 0, 16*1024); + if( err != SCE_OK ) + { + app.DebugPrintf("sceKernelReserveVirtualRange failed: 0x%08X\n", err); + return NULL; + } + s_orbisVAllocs.push_back(new OrbisVAlloc(pAddr, dwSize)); + return (LPVOID)pAddr; + } + else + { + if( flAllocationType & MEM_COMMIT ) + { + for(int i=0;im_virtualAddr == lpAddress) + { + return s_orbisVAllocs[i]->Commit(dwSize); + } + } + assert(0); // failed to find the virtual alloc in our table + return NULL; + } + } + return NULL; +} + +BOOL VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) +{ + int idx = -1; + for(int i=0;im_virtualAddr == lpAddress) + { + idx = i; + } + } + + assert(idx >= 0); + assert(dwSize == s_orbisVAllocs[idx]->m_virtualSize); // only supporting decommitting the entire memory size + + if(dwFreeType == MEM_DECOMMIT) + { + s_orbisVAllocs[idx]->Decommit(); + } + else if(dwFreeType == MEM_RELEASE) + { + delete s_orbisVAllocs[idx]; + s_orbisVAllocs.erase(s_orbisVAllocs.begin()+idx); + } + + return TRUE; +} + +DWORD GetFileSize( HANDLE hFile, LPDWORD lpFileSizeHigh ) +{ + SceFiosSize FileSize; + SceFiosFH fh = (SceFiosFH)((int64_t)hFile); + //DWORD FileSizeLow; + FileSize=sceFiosFHGetSize(fh); + if(lpFileSizeHigh) + *lpFileSizeHigh= (DWORD)(FileSize>>32); + else + { + assert(FileSize>>32 == 0); + } + + return (DWORD)FileSize; +} + +BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize ) +{ + SceFiosSize FileSize; + SceFiosFH fh = (SceFiosFH)((int64_t)hFile); + + FileSize=sceFiosFHGetSize(fh); + lpFileSize->QuadPart=FileSize; + + return true; +} +BOOL WriteFile( + HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ) +{ + SceFiosFH fh = (SceFiosFH)((int64_t)hFile); + // sceFiosFHReadSync - Non-negative values are the number of bytes read, 0 <= result <= length. Negative values are error codes. + SceFiosSize bytesRead = sceFiosFHWriteSync(NULL, fh, lpBuffer, (SceFiosSize)nNumberOfBytesToWrite); + if(bytesRead < 0) + { + // error + return FALSE; + } + else + { + *lpNumberOfBytesWritten = (DWORD)bytesRead; + return TRUE; + } +} + +BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ) +{ + SceFiosFH fh = (SceFiosFH)((int64_t)hFile); + // sceFiosFHReadSync - Non-negative values are the number of bytes read, 0 <= result <= length. Negative values are error codes. + SceFiosSize bytesRead = sceFiosFHReadSync(NULL, fh, lpBuffer, (SceFiosSize)nNumberOfBytesToRead); + *lpNumberOfBytesRead = (DWORD)bytesRead; + if(bytesRead < 0) + { + // error + return FALSE; + } + else + { + return TRUE; + } +} + +BOOL SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) +{ + SceFiosFH fd = (SceFiosFH)((int64_t)hFile); + + uint64_t bitsToMove = (int64_t) lDistanceToMove; + SceFiosOffset pos = 0; + + if (lpDistanceToMoveHigh != NULL) + bitsToMove |= ((uint64_t) (*lpDistanceToMoveHigh)) << 32; + + SceFiosWhence whence = SCE_FIOS_SEEK_SET; + switch(dwMoveMethod) + { + case FILE_BEGIN: whence = SCE_FIOS_SEEK_SET; break; + case FILE_CURRENT: whence = SCE_FIOS_SEEK_CUR; break; + case FILE_END: whence = SCE_FIOS_SEEK_END; break; + }; + + pos = sceFiosFHSeek(fd, (int64_t) lDistanceToMove, whence); + + return (pos != -1); +} + + +HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + char filePath[256]; + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + +#ifndef _CONTENT_PACKAGE + app.DebugPrintf("*** Opening %s\n",filePath); +#endif + + SceFiosFH fh; + SceFiosOpenParams openParams; + ZeroMemory(&openParams, sizeof(SceFiosOpenParams)); + + switch(dwDesiredAccess) + { + case GENERIC_READ: + openParams.openFlags = SCE_FIOS_O_RDONLY; break; + case GENERIC_WRITE: + openParams.openFlags = SCE_FIOS_O_WRONLY; break; + default: + openParams.openFlags = SCE_FIOS_O_READ | SCE_FIOS_O_WRITE; break; + } + + switch(dwCreationDisposition) + { + case CREATE_ALWAYS: + openParams.openFlags |= SCE_FIOS_O_CREAT; break; + case CREATE_NEW: + openParams.openFlags |= SCE_FIOS_O_CREAT; break; + case OPEN_ALWAYS: + openParams.openFlags |= SCE_FIOS_O_CREAT; break; + case OPEN_EXISTING: + break; + case TRUNCATE_EXISTING: + break; + } + int err = sceFiosFHOpenSync(NULL, &fh, filePath, &openParams); + + if(err != SCE_FIOS_OK) + { + return INVALID_HANDLE_VALUE; + } + //assert( err == SCE_FIOS_OK ); + + return (void*)fh; +} + +BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes){ ORBIS_STUBBED; return false; } +BOOL DeleteFileA(LPCSTR lpFileName) { ORBIS_STUBBED; return false; } + +// BOOL XCloseHandle(HANDLE a) +// { +// sceFiosFHCloseSync(NULL,(SceFiosFH)((int64_t)a)); +// return true; +// } + + +DWORD GetFileAttributesA(LPCSTR lpFileName) +{ + char filePath[256]; + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); // set to load from host + + // check if the file exists first + SceFiosStat statData; + if(sceFiosStatSync(NULL, filePath, &statData) != SCE_FIOS_OK) + { + app.DebugPrintf("*** sceFiosStatSync Failed\n"); + return -1; + } + if(statData.statFlags & SCE_FIOS_STATUS_DIRECTORY ) + return FILE_ATTRIBUTE_DIRECTORY; + else + return FILE_ATTRIBUTE_NORMAL; +} + + +BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) { ORBIS_STUBBED; return false; } + + +DWORD GetLastError(VOID) { ORBIS_STUBBED; return 0; } +VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer) +{ + SceLibcMallocManagedSize stat; + int err = malloc_stats(&stat); + if(err != 0) + { + app.DebugPrintf("Failed to get mem stats\n"); + } + + lpBuffer->dwTotalPhys = stat.maxSystemSize; + lpBuffer->dwAvailPhys = stat.maxSystemSize - stat.currentSystemSize; + lpBuffer->dwAvailVirtual = stat.maxSystemSize - stat.currentInuseSize; +} + +DWORD GetTickCount() +{ + // This function returns the current system time at this function is called. + // The system time is represented the time elapsed since the system starts up in microseconds. + uint64_t sysTime = sceKernelGetProcessTime(); + return (DWORD)(sysTime / 1000); +} + +// we should really use libperf for this kind of thing, but this will do for now. +BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) +{ + // microseconds + lpFrequency->QuadPart = (1000 * 1000); + return false; +} +BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) +{ + // microseconds + lpPerformanceCount->QuadPart = sceKernelGetProcessTime(); + return true; +} + +#ifndef _FINAL_BUILD + +VOID OutputDebugStringW(LPCWSTR lpOutputString) +{ + wprintf(lpOutputString); +} + +VOID OutputDebugStringA(LPCSTR lpOutputString) +{ + printf(lpOutputString); +} + +VOID OutputDebugString(LPCSTR lpOutputString) +{ + printf(lpOutputString); +} +#endif // _CONTENT_PACKAGE + +BOOL GetFileAttributesExA(LPCSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation) +{ + ORBIS_STUBBED; + return false; +} +HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData) { ORBIS_STUBBED; return 0;} +BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) { ORBIS_STUBBED; return false;} + +errno_t _itoa_s(int _Value, char * _DstBuf, size_t _Size, int _Radix) { if(_Radix==10) sprintf(_DstBuf,"%d",_Value); else if(_Radix==16) sprintf(_DstBuf,"%lx",_Value); else return -1; return 0; } +errno_t _i64toa_s(__int64 _Val, char * _DstBuf, size_t _Size, int _Radix) { if(_Radix==10) sprintf(_DstBuf,"%lld",_Val); else return -1; return 0; } + +DWORD XGetLanguage() +{ + unsigned char ucLang = app.GetMinecraftLanguage(0); + int iLang; + + // check if we should override the system language or not + if(ucLang==MINECRAFT_LANGUAGE_DEFAULT) + { + sceSystemServiceParamGetInt(SCE_SYSTEM_SERVICE_PARAM_ID_LANG,&iLang); + } + else + { + return (DWORD)ucLang; + } + + switch(iLang) + { + case SCE_SYSTEM_PARAM_LANG_JAPANESE : return XC_LANGUAGE_JAPANESE; + case SCE_SYSTEM_PARAM_LANG_ENGLISH_US : return XC_LANGUAGE_ENGLISH; + case SCE_SYSTEM_PARAM_LANG_FRENCH : return XC_LANGUAGE_FRENCH; + + case SCE_SYSTEM_PARAM_LANG_SPANISH : return XC_LANGUAGE_SPANISH; + case SCE_SYSTEM_PARAM_LANG_SPANISH_LA : return XC_LANGUAGE_LATINAMERICANSPANISH; + + case SCE_SYSTEM_PARAM_LANG_GERMAN : return XC_LANGUAGE_GERMAN; + case SCE_SYSTEM_PARAM_LANG_ITALIAN : return XC_LANGUAGE_ITALIAN; + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_PT : return XC_LANGUAGE_PORTUGUESE; + + case SCE_SYSTEM_PARAM_LANG_RUSSIAN : return XC_LANGUAGE_RUSSIAN; + case SCE_SYSTEM_PARAM_LANG_KOREAN : return XC_LANGUAGE_KOREAN; + case SCE_SYSTEM_PARAM_LANG_CHINESE_T : return XC_LANGUAGE_TCHINESE; + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_BR : return XC_LANGUAGE_PORTUGUESE; + case SCE_SYSTEM_PARAM_LANG_ENGLISH_GB : return XC_LANGUAGE_ENGLISH; + + case SCE_SYSTEM_PARAM_LANG_DUTCH : return XC_LANGUAGE_DUTCH; + case SCE_SYSTEM_PARAM_LANG_FINNISH : return XC_LANGUAGE_FINISH; + case SCE_SYSTEM_PARAM_LANG_SWEDISH : return XC_LANGUAGE_SWEDISH; + case SCE_SYSTEM_PARAM_LANG_DANISH : return XC_LANGUAGE_DANISH; + case SCE_SYSTEM_PARAM_LANG_NORWEGIAN : return XC_LANGUAGE_BNORWEGIAN; + case SCE_SYSTEM_PARAM_LANG_POLISH : return XC_LANGUAGE_POLISH; + case SCE_SYSTEM_PARAM_LANG_TURKISH : return XC_LANGUAGE_TURKISH; + + + case SCE_SYSTEM_PARAM_LANG_CHINESE_S : return XC_LANGUAGE_SCHINESE; + + default : return XC_LANGUAGE_ENGLISH; + } + +} +DWORD XGetLocale() +{ + int iLang; + sceSystemServiceParamGetInt(SCE_SYSTEM_SERVICE_PARAM_ID_LANG,&iLang); + switch(iLang) + { + case SCE_SYSTEM_PARAM_LANG_JAPANESE : return XC_LOCALE_JAPAN; + case SCE_SYSTEM_PARAM_LANG_ENGLISH_US : return XC_LOCALE_UNITED_STATES; + case SCE_SYSTEM_PARAM_LANG_FRENCH : return XC_LOCALE_FRANCE; + + case SCE_SYSTEM_PARAM_LANG_SPANISH : return XC_LOCALE_SPAIN; + case SCE_SYSTEM_PARAM_LANG_SPANISH_LA : return XC_LOCALE_LATIN_AMERICA; + + case SCE_SYSTEM_PARAM_LANG_GERMAN : return XC_LOCALE_GERMANY; + case SCE_SYSTEM_PARAM_LANG_ITALIAN : return XC_LOCALE_ITALY; + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_PT : return XC_LOCALE_PORTUGAL; + + case SCE_SYSTEM_PARAM_LANG_RUSSIAN : return XC_LOCALE_RUSSIAN_FEDERATION; + case SCE_SYSTEM_PARAM_LANG_KOREAN : return XC_LOCALE_KOREA; + case SCE_SYSTEM_PARAM_LANG_CHINESE_T : return XC_LOCALE_CHINA; + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_BR : return XC_LOCALE_BRAZIL; + case SCE_SYSTEM_PARAM_LANG_ENGLISH_GB : return XC_LOCALE_GREAT_BRITAIN; + + case SCE_SYSTEM_PARAM_LANG_DUTCH : return XC_LOCALE_NETHERLANDS; + case SCE_SYSTEM_PARAM_LANG_FINNISH : return XC_LOCALE_FINLAND; + case SCE_SYSTEM_PARAM_LANG_SWEDISH : return XC_LOCALE_SWEDEN; + case SCE_SYSTEM_PARAM_LANG_DANISH : return XC_LOCALE_DENMARK; + case SCE_SYSTEM_PARAM_LANG_NORWEGIAN : return XC_LOCALE_NORWAY; + case SCE_SYSTEM_PARAM_LANG_POLISH : return XC_LOCALE_POLAND; + case SCE_SYSTEM_PARAM_LANG_TURKISH : return XC_LOCALE_TURKEY; + + + case SCE_SYSTEM_PARAM_LANG_CHINESE_S : return XC_LOCALE_CHINA; + default : return XC_LOCALE_UNITED_STATES; + } +} + +DWORD XEnableGuestSignin(BOOL fEnable) +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.h b/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.h new file mode 100644 index 0000000..da9f872 --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/OrbisStubs.h @@ -0,0 +1,422 @@ +#pragma once +#include + +#include "TLSStorage.h" + +class C4JThread; + +//const char* getConsoleHomePath(); +char* getUsrDirPath(); + +void OrbisInit(); + +DWORD TlsAlloc(VOID); +LPVOID TlsGetValue(DWORD dwTlsIndex); +BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue); + +typedef struct _RECT +{ + LONG left; + LONG top; + LONG right; + LONG bottom; +} RECT, *PRECT; + +typedef void ID3D11Device; +typedef void ID3D11DeviceContext; +typedef void IDXGISwapChain; +typedef RECT D3D11_RECT; +typedef void ID3D11Buffer; +typedef DWORD (*PTHREAD_START_ROUTINE)( LPVOID lpThreadParameter); +typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; + +typedef int errno_t; + +// typedef struct _RTL_CRITICAL_SECTION { +// // +// // The following field is used for blocking when there is contention for +// // the resource +// // +// +// union { +// ULONG_PTR RawEvent[4]; +// } Synchronization; +// +// // +// // The following three fields control entering and exiting the critical +// // section for the resource +// // +// +// LONG LockCount; +// LONG RecursionCount; +// HANDLE OwningThread; +// } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION; + +class OrbisCriticalSection +{ +public: + ScePthreadMutex mutex; + int m_cLock; +#ifdef _DEBUG + C4JThread* m_pOwnerThread; +#endif +}; + +typedef OrbisCriticalSection RTL_CRITICAL_SECTION; +typedef OrbisCriticalSection* PRTL_CRITICAL_SECTION; + +typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; +typedef PRTL_CRITICAL_SECTION PCRITICAL_SECTION; +typedef PRTL_CRITICAL_SECTION LPCRITICAL_SECTION; + +void EnterCriticalSection(CRITICAL_SECTION* _c); +void LeaveCriticalSection(CRITICAL_SECTION* _c); +void InitializeCriticalSection(CRITICAL_SECTION* _c); +void DeleteCriticalSection(CRITICAL_SECTION* _c); +HANDLE CreateEvent(void* lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName); +VOID Sleep(DWORD dwMilliseconds); +BOOL SetThreadPriority(HANDLE hThread, int nPriority); +DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); + +LONG InterlockedCompareExchangeRelease(LONG volatile *Destination, LONG Exchange,LONG Comperand ); + +int32_t GetAudioBGMHandle(); + +VOID InitializeCriticalSection(PCRITICAL_SECTION CriticalSection); +VOID InitializeCriticalSectionAndSpinCount(PCRITICAL_SECTION CriticalSection, ULONG SpinCount); +VOID DeleteCriticalSection(PCRITICAL_SECTION CriticalSection); +VOID EnterCriticalSection(PCRITICAL_SECTION CriticalSection); +VOID LeaveCriticalSection(PCRITICAL_SECTION CriticalSection); +ULONG TryEnterCriticalSection(PCRITICAL_SECTION CriticalSection); +DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds); + +LONG64 InterlockedCompareExchangeRelease64(LONG64 volatile *Destination, LONG64 Exchange, LONG64 Comperand); + +BOOL CloseHandle(HANDLE hObject); +BOOL SetEvent(HANDLE hEvent); + +HMODULE GetModuleHandle(LPCSTR lpModuleName); + +HANDLE CreateThread( void* lpThreadAttributes, DWORD dwStackSize, void* lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); +DWORD ResumeThread( HANDLE hThread ); +DWORD GetCurrentThreadId(VOID); +DWORD WaitForMultipleObjectsEx(DWORD nCount,CONST HANDLE *lpHandles,BOOL bWaitAll,DWORD dwMilliseconds,BOOL bAlertable ); +BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); + + +LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +BOOL VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); + +DWORD GetFileSize( HANDLE hFile, LPDWORD lpFileSizeHigh ); +BOOL GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize ); +BOOL WriteFile( +HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped ); +BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ); +#define INVALID_SET_FILE_POINTER false +BOOL SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); +HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); +#define CreateFile CreateFileA +BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes); +#define CreateDirectory CreateDirectoryA +BOOL DeleteFileA(LPCSTR lpFileName); +#define DeleteFile DeleteFileA +DWORD GetFileAttributesA(LPCSTR lpFileName); +#define GetFileAttributes GetFileAttributesA +BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName); +#define MoveFile MoveFileA + +#define MAX_PATH 260 + +void __debugbreak(); +VOID DebugBreak(VOID); + + +enum D3D11_BLEND +{ + D3D11_BLEND_ZERO = 1, + D3D11_BLEND_ONE = 2, + D3D11_BLEND_SRC_COLOR = 3, + D3D11_BLEND_INV_SRC_COLOR = 4, + D3D11_BLEND_SRC_ALPHA = 5, + D3D11_BLEND_INV_SRC_ALPHA = 6, + D3D11_BLEND_DEST_ALPHA = 7, + D3D11_BLEND_INV_DEST_ALPHA = 8, + D3D11_BLEND_DEST_COLOR = 9, + D3D11_BLEND_INV_DEST_COLOR = 10, + D3D11_BLEND_SRC_ALPHA_SAT = 11, + D3D11_BLEND_BLEND_FACTOR = 14, + D3D11_BLEND_INV_BLEND_FACTOR = 15, + D3D11_BLEND_SRC1_COLOR = 16, + D3D11_BLEND_INV_SRC1_COLOR = 17, + D3D11_BLEND_SRC1_ALPHA = 18, + D3D11_BLEND_INV_SRC1_ALPHA = 19 +}; + + +enum D3D11_COMPARISON_FUNC +{ + D3D11_COMPARISON_NEVER = 1, + D3D11_COMPARISON_LESS = 2, + D3D11_COMPARISON_EQUAL = 3, + D3D11_COMPARISON_LESS_EQUAL = 4, + D3D11_COMPARISON_GREATER = 5, + D3D11_COMPARISON_NOT_EQUAL = 6, + D3D11_COMPARISON_GREATER_EQUAL = 7, + D3D11_COMPARISON_ALWAYS = 8 +}; + + +typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME; + +VOID GetSystemTime( LPSYSTEMTIME lpSystemTime); +BOOL FileTimeToSystemTime(CONST FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime); +BOOL SystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime); +VOID GetLocalTime(LPSYSTEMTIME lpSystemTime); + +typedef struct _MEMORYSTATUS { + DWORD dwLength; + DWORD dwMemoryLoad; + SIZE_T dwTotalPhys; + SIZE_T dwAvailPhys; + SIZE_T dwTotalPageFile; + SIZE_T dwAvailPageFile; + SIZE_T dwTotalVirtual; + SIZE_T dwAvailVirtual; +} MEMORYSTATUS, *LPMEMORYSTATUS; + + +#define WINAPI + +#define CREATE_SUSPENDED 0x00000004 + +#define THREAD_BASE_PRIORITY_LOWRT 15 // value that gets a thread to LowRealtime-1 +#define THREAD_BASE_PRIORITY_MAX 2 // maximum thread base priority boost +#define THREAD_BASE_PRIORITY_MIN -2 // minimum thread base priority boost +#define THREAD_BASE_PRIORITY_IDLE -15 // value that gets a thread to idle + +#define THREAD_PRIORITY_LOWEST THREAD_BASE_PRIORITY_MIN +#define THREAD_PRIORITY_BELOW_NORMAL (THREAD_PRIORITY_LOWEST+1) +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_HIGHEST THREAD_BASE_PRIORITY_MAX +#define THREAD_PRIORITY_ABOVE_NORMAL (THREAD_PRIORITY_HIGHEST-1) +#define THREAD_PRIORITY_ERROR_RETURN (MAXLONG) + +#define THREAD_PRIORITY_TIME_CRITICAL THREAD_BASE_PRIORITY_LOWRT +#define THREAD_PRIORITY_IDLE THREAD_BASE_PRIORITY_IDLE + +#define WAIT_TIMEOUT 258L +#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) +#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0 ) + 0 ) + +#define MAXUINT_PTR (~((UINT_PTR)0)) +#define MAXINT_PTR ((INT_PTR)(MAXUINT_PTR >> 1)) +#define MININT_PTR (~MAXINT_PTR) + +#define MAXULONG_PTR (~((ULONG_PTR)0)) +#define MAXLONG_PTR ((LONG_PTR)(MAXULONG_PTR >> 1)) +#define MINLONG_PTR (~MAXLONG_PTR) + +#define MAXUHALF_PTR ((UHALF_PTR)~0) +#define MAXHALF_PTR ((HALF_PTR)(MAXUHALF_PTR >> 1)) +#define MINHALF_PTR (~MAXHALF_PTR) + +#define INVALID_HANDLE_VALUE ((HANDLE)-1) +// +// Generic test for success on any status value (non-negative numbers +// indicate success). +// + +//#define HRESULT_SUCCEEDED(Status) ((HRESULT)(Status) >= 0) + +// +// and the inverse +// +#define _HRESULT_TYPEDEF_(_sc) _sc + +#define FAILED(Status) ((HRESULT)(Status)<0) +#define MAKE_HRESULT(sev,fac,code) \ + ((HRESULT) (((unsigned int)(sev)<<31) | ((unsigned int)(fac)<<16) | ((unsigned int)(code))) ) +#define MAKE_SCODE(sev,fac,code) \ + ((SCODE) (((unsigned int)(sev)<<31) | ((unsigned int)(fac)<<16) | ((unsigned int)(code))) ) +#define E_FAIL _HRESULT_TYPEDEF_(0x80004005L) +#define E_ABORT _HRESULT_TYPEDEF_(0x80004004L) +#define E_NOINTERFACE _HRESULT_TYPEDEF_(0x80004002L) + +#define GENERIC_READ (0x80000000L) +#define GENERIC_WRITE (0x40000000L) +#define GENERIC_EXECUTE (0x20000000L) +#define GENERIC_ALL (0x10000000L) + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_DEVICE 0x00000040 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 + +#define FILE_FLAG_WRITE_THROUGH 0x80000000 +#define FILE_FLAG_OVERLAPPED 0x40000000 +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_RANDOM_ACCESS 0x10000000 +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 + +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define PAGE_NOACCESS 0x01 +#define PAGE_READONLY 0x02 +#define PAGE_READWRITE 0x04 +#define PAGE_WRITECOPY 0x08 +#define PAGE_EXECUTE 0x10 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_EXECUTE_WRITECOPY 0x80 +#define PAGE_GUARD 0x100 +#define PAGE_NOCACHE 0x200 +#define PAGE_WRITECOMBINE 0x400 +#define PAGE_USER_READONLY 0x1000 +#define PAGE_USER_READWRITE 0x2000 +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 +#define MEM_FREE 0x10000 +#define MEM_PRIVATE 0x20000 +#define MEM_RESET 0x80000 +#define MEM_TOP_DOWN 0x100000 +#define MEM_NOZERO 0x800000 +#define MEM_LARGE_PAGES 0x20000000 +#define MEM_HEAP 0x40000000 +#define MEM_16MB_PAGES 0x80000000 + +#define IGNORE 0 // Ignore signal +#define INFINITE 0xFFFFFFFF // Infinite timeout +#define WAIT_FAILED ((DWORD)0xFFFFFFFF) +#define STATUS_WAIT_0 ((DWORD )0x00000000L) +#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 ) +#define STATUS_PENDING ((DWORD )0x00000103L) +#define STILL_ACTIVE STATUS_PENDING + +DWORD GetLastError(VOID); +VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer); + +DWORD GetTickCount(); +BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); +BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); + + +#define ERROR_SUCCESS 0L +#define ERROR_IO_PENDING 997L // dderror +#define ERROR_CANCELLED 1223L +#define S_OK ((HRESULT)0x00000000L) +#define S_FALSE ((HRESULT)0x00000001L) + +#define RtlEqualMemory(Destination,Source,Length) (!memcmp((Destination),(Source),(Length))) +#define RtlMoveMemory(Destination,Source,Length) memmove((Destination),(Source),(Length)) +#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length)) +#define RtlFillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length)) +#define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length)) + +#define MoveMemory RtlMoveMemory +#define CopyMemory RtlCopyMemory +#define FillMemory RtlFillMemory +#define ZeroMemory RtlZeroMemory + +#define CDECL +#define APIENTRY + +#define VK_ESCAPE 0x1B +#define VK_RETURN 0x0D + +VOID OutputDebugStringW(LPCWSTR lpOutputString); +VOID OutputDebugString(LPCSTR lpOutputString); +VOID OutputDebugStringA(LPCSTR lpOutputString); + +errno_t _itoa_s(int _Value, char * _DstBuf, size_t _Size, int _Radix); +errno_t _i64toa_s(__int64 _Val, char * _DstBuf, size_t _Size, int _Radix); + +#define __declspec(a) +extern "C" int _wcsicmp (const wchar_t * dst, const wchar_t * src); + +size_t wcsnlen(const wchar_t *wcs, size_t maxsize); + +typedef struct _WIN32_FIND_DATAA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + CHAR cFileName[ MAX_PATH ]; + CHAR cAlternateFileName[ 14 ]; +} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA; +typedef WIN32_FIND_DATAA WIN32_FIND_DATA; +typedef PWIN32_FIND_DATAA PWIN32_FIND_DATA; +typedef LPWIN32_FIND_DATAA LPWIN32_FIND_DATA; + +typedef struct _WIN32_FILE_ATTRIBUTE_DATA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA; + + +DWORD GetFileAttributesA(LPCSTR lpFileName); +#define GetFileAttributes GetFileAttributesA +typedef enum _GET_FILEEX_INFO_LEVELS { + GetFileExInfoStandard, + GetFileExMaxInfoLevel +} GET_FILEEX_INFO_LEVELS; + +BOOL GetFileAttributesExA(LPCSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation); +#define GetFileAttributesEx GetFileAttributesExA + +BOOL DeleteFileA(LPCSTR lpFileName); +#define DeleteFile DeleteFileA + + +HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData); +#define FindFirstFile FindFirstFileA + +BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData); +#define FindNextFile FindNextFileA +#define FindClose(hFindFile) CloseHandle(hFindFile) + +DWORD XGetLanguage(); +DWORD XGetLocale(); +DWORD XEnableGuestSignin(BOOL fEnable); + +#ifdef _CONTENT_PACKAGE +#define ORBIS_STUBBED { } +#else +#define ORBIS_STUBBED { static bool bSet = false; if(!bSet){printf("missing function on PS4 : %s\n Tell MarkH about this, then press f5 to continue.\n", __FUNCTION__); bSet = true; SCE_BREAK();} } +#endif + diff --git a/Minecraft.Client/Orbis/OrbisExtras/OrbisTypes.h b/Minecraft.Client/Orbis/OrbisExtras/OrbisTypes.h new file mode 100644 index 0000000..006a006 --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/OrbisTypes.h @@ -0,0 +1,176 @@ +#pragma once + +//#include "winerror.h" + +typedef unsigned int DWORD; +typedef int BOOL; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef float FLOAT; +typedef int __int32; +typedef FLOAT *PFLOAT; +typedef BOOL *PBOOL; +typedef BOOL *LPBOOL; +typedef BYTE *PBYTE; +typedef BYTE *LPBYTE; +typedef int *PINT; +typedef int *LPINT; +typedef WORD *PWORD; +typedef WORD *LPWORD; +typedef int *PLONG; +typedef int *LPLONG; +typedef DWORD *PDWORD; +typedef DWORD *LPDWORD; +typedef void *LPVOID; +typedef const void *LPCVOID; +typedef void *PVOID; +typedef unsigned int ULONG; + +typedef unsigned char boolean; + +typedef int INT; +typedef unsigned int UINT; +typedef unsigned int *PUINT; + + +typedef unsigned char byte; +typedef long __int64; +typedef unsigned long __uint64; +typedef unsigned int DWORD; +typedef int INT; +typedef unsigned long ULONG_PTR, *PULONG_PTR; +typedef ULONG_PTR SIZE_T, *PSIZE_T; + +typedef long LONG64, *PLONG64; + +#define VOID void +typedef char CHAR; +typedef short SHORT; +typedef int LONG; +typedef long LONGLONG; +typedef unsigned long ULONGLONG; + + +#define CONST const +typedef wchar_t WCHAR; // wc, 16-bit UNICODE character +typedef WCHAR *PWCHAR; +typedef WCHAR *LPWCH, *PWCH; +typedef CONST WCHAR *LPCWCH, *PCWCH; +typedef WCHAR *NWPSTR; +typedef WCHAR *LPWSTR, *PWSTR; +typedef CONST WCHAR *LPCWSTR, *PCWSTR; + +// +// ANSI (Multi-byte Character) types +// +typedef CHAR *PCHAR; +typedef CHAR *LPCH, *PCH; +typedef CONST CHAR *LPCCH, *PCCH; +typedef CHAR *NPSTR; +typedef CHAR *LPSTR, *PSTR; +typedef CONST CHAR *LPCSTR, *PCSTR; + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + + +typedef struct _FILETIME { +#ifdef _M_PPCBE + DWORD dwHighDateTime; + DWORD dwLowDateTime; +#else + DWORD dwLowDateTime; + DWORD dwHighDateTime; +#endif +} FILETIME, *PFILETIME, *LPFILETIME; + + +#if defined(MIDL_PASS) +typedef struct _LARGE_INTEGER { +#else // MIDL_PASS +typedef union _LARGE_INTEGER { + struct { +#if defined(_M_PPCBE) + LONG HighPart; + DWORD LowPart; +#else + DWORD LowPart; + LONG HighPart; +#endif + }; + struct { +#if defined(_M_PPCBE) + LONG HighPart; + DWORD LowPart; +#else + DWORD LowPart; + LONG HighPart; +#endif + } u; +#endif //MIDL_PASS + LONGLONG QuadPart; +} LARGE_INTEGER; + +typedef LARGE_INTEGER *PLARGE_INTEGER; + +#if defined(MIDL_PASS) +typedef struct _ULARGE_INTEGER { +#else // MIDL_PASS +typedef union _ULARGE_INTEGER { + struct { +#if defined(_M_PPCBE) + DWORD HighPart; + DWORD LowPart; +#else + DWORD LowPart; + DWORD HighPart; +#endif + }; + struct { +#if defined(_M_PPCBE) + DWORD HighPart; + DWORD LowPart; +#else + DWORD LowPart; + DWORD HighPart; +#endif + } u; +#endif //MIDL_PASS + ULONGLONG QuadPart; +} ULARGE_INTEGER; + +typedef ULARGE_INTEGER *PULARGE_INTEGER; + +// 360 specifics +typedef int32_t HRESULT; +typedef void* HANDLE; + +#define DECLARE_HANDLE(name) typedef HANDLE name +DECLARE_HANDLE(HINSTANCE); + +typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ + +typedef struct _OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + DWORD Offset; + DWORD OffsetHigh; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; + +typedef LPVOID PSECURITY_ATTRIBUTES; +typedef LPVOID LPSECURITY_ATTRIBUTES; + + + +#define __in_ecount(a) +#define __in_bcount(a) + +#ifndef AUTO_VAR +#define AUTO_VAR(_var, _val) auto _var = _val +#endif \ No newline at end of file diff --git a/Minecraft.Client/Orbis/OrbisExtras/ShutdownManager.h b/Minecraft.Client/Orbis/OrbisExtras/ShutdownManager.h new file mode 100644 index 0000000..38d9c36 --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/ShutdownManager.h @@ -0,0 +1,47 @@ +#pragma once + +class ShutdownManager +{ +public: + typedef enum + { + eMainThread, + + eLeaderboardThread, + eCommerceThread, + ePostProcessThread, + eRunUpdateThread, + eRenderChunkUpdateThread, + eServerThread, + eStorageManagerThreads, + eConnectionReadThreads, + eConnectionWriteThreads, + eEventQueueThreads, + + eThreadIdCount + } EThreadId; + + static void Initialise() {} + static void StartShutdown() {} + static void MainThreadHandleShutdown() {} +#ifdef __PS3__ + static void SysUtilCallback(uint64_t status, uint64_t param, void *userdata); +#endif + + static void HasStarted(EThreadId threadId) {}; + static void HasStarted(EThreadId threadId, C4JThread::EventArray *eventArray) {}; + static bool ShouldRun(EThreadId threadId) {return true;}; + static void HasFinished(EThreadId threadId) {}; + +private: +#ifdef __PS3__ + static bool s_threadShouldRun[eThreadIdCount]; + static int s_threadRunning[eThreadIdCount]; + static CRITICAL_SECTION s_threadRunningCS; + static C4JThread::EventArray *s_eventArray[eThreadIdCount]; + + static void RequestThreadToStop(int i); + static void WaitForSignalledToComplete(); + static void StorageManagerCompleteFn(); +#endif +}; diff --git a/Minecraft.Client/Orbis/OrbisExtras/TLSStorage.cpp b/Minecraft.Client/Orbis/OrbisExtras/TLSStorage.cpp new file mode 100644 index 0000000..9f17e99 --- /dev/null +++ b/Minecraft.Client/Orbis/OrbisExtras/TLSStorage.cpp @@ -0,0 +1,71 @@ + + +#include "stdafx.h" + + + +TLSStorageOrbis* TLSStorageOrbis::m_pInstance = NULL; + +BOOL TLSStorageOrbis::m_activeList[sc_maxSlots]; +__thread LPVOID TLSStorageOrbis::m_values[sc_maxSlots]; + + + +TLSStorageOrbis::TLSStorageOrbis() +{ + for(int i=0;i